irie 0.0.35__py3-none-any.whl → 0.0.37__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of irie might be problematic. Click here for more details.

Files changed (28) hide show
  1. irie/apps/context_processors.py +8 -0
  2. irie/apps/inventory/admin.py +4 -1
  3. irie/apps/inventory/forms.py +30 -1
  4. irie/apps/inventory/migrations/0004_datum_sensorgroup_sensor.py +43 -0
  5. irie/apps/inventory/migrations/0005_alter_sensor_group.py +19 -0
  6. irie/apps/inventory/migrations/0006_datum_asset_datum_locate_x_datum_locate_y_and_more.py +56 -0
  7. irie/apps/inventory/migrations/0007_sensor_name.py +19 -0
  8. irie/apps/inventory/migrations/0008_alter_sensor_dx_alter_sensor_dy_alter_sensor_dz_and_more.py +43 -0
  9. irie/apps/inventory/models.py +47 -17
  10. irie/apps/inventory/urls.py +4 -2
  11. irie/apps/inventory/views.py +55 -2
  12. irie/apps/templates/includes/sidebar.html +29 -10
  13. irie/apps/templates/inventory/asset-event-summary.html +17 -7
  14. irie/apps/templates/inventory/asset-profile.html +10 -2
  15. irie/apps/templates/inventory/asset-sensors.html +69 -0
  16. irie/apps/templates/inventory/sensor-upload.html +452 -0
  17. irie/apps/templates/layouts/base-fullscreen.html +3 -4
  18. irie/apps/templates/site/index.html +2 -2
  19. irie/core/settings.py +1 -0
  20. irie/init/data/nbi_definitions.json +1 -0
  21. irie/init/getNBIData.py +12 -0
  22. irie/init/management/commands/init_assets.py +11 -7
  23. irie/init/management/commands/init_cesmd.py +11 -8
  24. {irie-0.0.35.dist-info → irie-0.0.37.dist-info}/METADATA +1 -1
  25. {irie-0.0.35.dist-info → irie-0.0.37.dist-info}/RECORD +28 -20
  26. {irie-0.0.35.dist-info → irie-0.0.37.dist-info}/WHEEL +1 -1
  27. {irie-0.0.35.dist-info → irie-0.0.37.dist-info}/entry_points.txt +0 -0
  28. {irie-0.0.35.dist-info → irie-0.0.37.dist-info}/top_level.txt +0 -0
@@ -3,3 +3,11 @@ from django.conf import settings
3
3
 
4
4
  def cfg_assets_root(request):
5
5
  return {"ASSETS_ROOT": settings.ASSETS_ROOT}
6
+
7
+ def irie_apps(request):
8
+ return {
9
+ 'irie_apps': settings.IRIE_APPS,
10
+ 'irie_app_name': request.resolver_match.app_name,
11
+ 'namespace': request.resolver_match.namespace,
12
+ 'irie_url_name': request.resolver_match.url_name
13
+ }
@@ -4,7 +4,10 @@
4
4
  #
5
5
  #===----------------------------------------------------------------------===#
6
6
  from django.contrib import admin
7
- from .models import Asset, Corridor
7
+ from .models import Asset, Corridor, SensorGroup, Sensor, Datum
8
8
 
9
9
  admin.site.register(Corridor)
10
10
  admin.site.register(Asset)
11
+ admin.site.register(SensorGroup)
12
+ admin.site.register(Sensor)
13
+ admin.site.register(Datum)
@@ -4,9 +4,38 @@
4
4
  #
5
5
  #===----------------------------------------------------------------------===#
6
6
  from django import forms
7
- from irie.apps.inventory.models import Asset
7
+ from django.forms import formset_factory
8
+ from irie.apps.inventory.models import Asset, SensorGroup, Sensor, Datum
8
9
 
9
10
  class AssetForm(forms.ModelForm):
10
11
  class Meta:
11
12
  model = Asset
12
13
  fields = '__all__'
14
+
15
+ class SensorGroupForm(forms.ModelForm):
16
+ class Meta:
17
+ model = SensorGroup
18
+ fields = ['name', 'datum']
19
+
20
+ def __init__(self, *args, asset=None, **kwargs):
21
+ super().__init__(*args, **kwargs)
22
+ if asset:
23
+ self.fields['datum'].queryset = Datum.objects.filter(asset=asset)
24
+
25
+ class SensorForm(forms.ModelForm):
26
+ class Meta:
27
+ model = Sensor
28
+ fields = ['name', 'x', 'y', 'z', 'dx', 'dy', 'dz']
29
+ labels = {
30
+ 'name': 'Name',
31
+ 'x': 'x',
32
+ 'y': 'y',
33
+ 'z': 'z',
34
+ 'dx': 'dx',
35
+ 'dy': 'dy',
36
+ 'dz': 'dz',
37
+ }
38
+
39
+
40
+ # Create a formset for multiple sensors
41
+ SensorFormSet = formset_factory(SensorForm, extra=3) # Default to 3 empty sensor forms
@@ -0,0 +1,43 @@
1
+ # Generated by Django 5.1.2 on 2025-02-24 00:30
2
+
3
+ import django.db.models.deletion
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('irie_apps_inventory', '0003_asset_notes'),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.CreateModel(
15
+ name='Datum',
16
+ fields=[
17
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18
+ ('name', models.CharField(max_length=100)),
19
+ ],
20
+ ),
21
+ migrations.CreateModel(
22
+ name='SensorGroup',
23
+ fields=[
24
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
25
+ ('name', models.CharField(max_length=100)),
26
+ ('asset', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, to='irie_apps_inventory.asset')),
27
+ ('datum', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, to='irie_apps_inventory.datum')),
28
+ ],
29
+ ),
30
+ migrations.CreateModel(
31
+ name='Sensor',
32
+ fields=[
33
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
34
+ ('x', models.FloatField()),
35
+ ('y', models.FloatField()),
36
+ ('z', models.FloatField()),
37
+ ('dx', models.FloatField()),
38
+ ('dy', models.FloatField()),
39
+ ('dz', models.FloatField()),
40
+ ('group', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, to='irie_apps_inventory.sensorgroup')),
41
+ ],
42
+ ),
43
+ ]
@@ -0,0 +1,19 @@
1
+ # Generated by Django 5.1.2 on 2025-02-25 04:49
2
+
3
+ import django.db.models.deletion
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('irie_apps_inventory', '0004_datum_sensorgroup_sensor'),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AlterField(
15
+ model_name='sensor',
16
+ name='group',
17
+ field=models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, related_name='sensors', to='irie_apps_inventory.sensorgroup'),
18
+ ),
19
+ ]
@@ -0,0 +1,56 @@
1
+ # Generated by Django 5.1.2 on 2025-02-25 05:07
2
+
3
+ import django.db.models.deletion
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('irie_apps_inventory', '0005_alter_sensor_group'),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AddField(
15
+ model_name='datum',
16
+ name='asset',
17
+ field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.RESTRICT, to='irie_apps_inventory.asset'),
18
+ preserve_default=False,
19
+ ),
20
+ migrations.AddField(
21
+ model_name='datum',
22
+ name='locate_x',
23
+ field=models.CharField(default=0, max_length=240),
24
+ preserve_default=False,
25
+ ),
26
+ migrations.AddField(
27
+ model_name='datum',
28
+ name='locate_y',
29
+ field=models.CharField(default=0, max_length=240),
30
+ preserve_default=False,
31
+ ),
32
+ migrations.AddField(
33
+ model_name='datum',
34
+ name='locate_z',
35
+ field=models.CharField(default=0, max_length=240),
36
+ preserve_default=False,
37
+ ),
38
+ migrations.AddField(
39
+ model_name='datum',
40
+ name='orient_x',
41
+ field=models.CharField(default=0, max_length=240),
42
+ preserve_default=False,
43
+ ),
44
+ migrations.AddField(
45
+ model_name='datum',
46
+ name='orient_y',
47
+ field=models.CharField(default=0, max_length=240),
48
+ preserve_default=False,
49
+ ),
50
+ migrations.AddField(
51
+ model_name='datum',
52
+ name='orient_z',
53
+ field=models.CharField(default=0, max_length=240),
54
+ preserve_default=False,
55
+ ),
56
+ ]
@@ -0,0 +1,19 @@
1
+ # Generated by Django 5.1.2 on 2025-02-26 03:21
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('irie_apps_inventory', '0006_datum_asset_datum_locate_x_datum_locate_y_and_more'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name='sensor',
15
+ name='name',
16
+ field=models.CharField(default=1, max_length=100),
17
+ preserve_default=False,
18
+ ),
19
+ ]
@@ -0,0 +1,43 @@
1
+ # Generated by Django 5.1.2 on 2025-02-26 07:05
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('irie_apps_inventory', '0007_sensor_name'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name='sensor',
15
+ name='dx',
16
+ field=models.DecimalField(decimal_places=2, max_digits=10),
17
+ ),
18
+ migrations.AlterField(
19
+ model_name='sensor',
20
+ name='dy',
21
+ field=models.DecimalField(decimal_places=2, max_digits=10),
22
+ ),
23
+ migrations.AlterField(
24
+ model_name='sensor',
25
+ name='dz',
26
+ field=models.DecimalField(decimal_places=2, max_digits=10),
27
+ ),
28
+ migrations.AlterField(
29
+ model_name='sensor',
30
+ name='x',
31
+ field=models.DecimalField(decimal_places=2, max_digits=10),
32
+ ),
33
+ migrations.AlterField(
34
+ model_name='sensor',
35
+ name='y',
36
+ field=models.DecimalField(decimal_places=2, max_digits=10),
37
+ ),
38
+ migrations.AlterField(
39
+ model_name='sensor',
40
+ name='z',
41
+ field=models.DecimalField(decimal_places=2, max_digits=10),
42
+ ),
43
+ ]
@@ -22,23 +22,6 @@ class Corridor(models.Model):
22
22
  return f"{self.name} ({self.assets.count()})"
23
23
 
24
24
 
25
- class _Sensor: # (models.Model):
26
- class Status:
27
- active: bool
28
-
29
- status = None
30
-
31
- class Station: # (models.Model):
32
- sensors = None
33
- assets = None
34
- network = None
35
- events = None
36
-
37
- class Vulnerability: # (models.Model):
38
- type = None
39
- asset = None
40
- notes = models.CharField(max_length=1024, blank=True, null=True)
41
-
42
25
  class Asset(models.Model):
43
26
  id = models.BigAutoField(primary_key=True)
44
27
  cesmd = models.CharField(max_length=7, blank=True, null=True)
@@ -112,3 +95,50 @@ class Asset(models.Model):
112
95
  class Meta:
113
96
  ordering = ["-id"]
114
97
 
98
+
99
+ class Vulnerability: # (models.Model):
100
+ type = None
101
+ asset = None
102
+ notes = models.CharField(max_length=1024, blank=True, null=True)
103
+
104
+
105
+ class Datum(models.Model):
106
+ name = models.CharField(max_length=100)
107
+ orient_x = models.CharField(max_length=240) # eg, longitudinal axis of the deck from west to east
108
+ locate_x = models.CharField(max_length=240) # eg, east abutment-to-deck interface
109
+ orient_y = models.CharField(max_length=240) # eg, transverse axis of the deck from north to south
110
+ locate_y = models.CharField(max_length=240) # eg, center of deck
111
+ orient_z = models.CharField(max_length=240) # eg, vertical upwards
112
+ locate_z = models.CharField(max_length=240) # eg, deck surface
113
+ asset = models.ForeignKey(Asset, on_delete=models.RESTRICT)
114
+
115
+ def __str__(self):
116
+ return f"{self.name}"
117
+
118
+ class SensorGroup(models.Model):
119
+ name = models.CharField(max_length=100)
120
+ # sensors; access with .sensor_set.all()
121
+ asset = models.ForeignKey(Asset, on_delete=models.RESTRICT)
122
+ datum = models.ForeignKey(Datum, on_delete=models.RESTRICT)
123
+ # network = models.CharField(max_length=100)
124
+ # events = None
125
+ def __str__(self):
126
+ return f"{self.name} - {self.datum}"
127
+
128
+ class Sensor(models.Model):
129
+ # class Status:
130
+ # active: bool
131
+
132
+ name = models.CharField(max_length=100)
133
+ x = models.DecimalField(decimal_places=2, max_digits=10)
134
+ y = models.DecimalField(decimal_places=2, max_digits=10)
135
+ z = models.DecimalField(decimal_places=2, max_digits=10)
136
+
137
+ dx = models.DecimalField(decimal_places=2, max_digits=10)
138
+ dy = models.DecimalField(decimal_places=2, max_digits=10)
139
+ dz = models.DecimalField(decimal_places=2, max_digits=10)
140
+
141
+ group = models.ForeignKey(SensorGroup, related_name="sensors", on_delete=models.RESTRICT)
142
+
143
+ def __str__(self):
144
+ return f"{self.name} ({self.group.name})"
@@ -23,6 +23,8 @@ urlpatterns = [
23
23
  "^evaluations/(?P<event>[0-9 A-Z-]*)/(?P<cesmd>[0-9 A-Z-]*)/.*", views.asset_event_summary,
24
24
  name="asset_event_summary"
25
25
  ),
26
- re_path("^inventory/(?P<calid>[0-9 A-Z-]*)/evaluations/$", views.asset_evals, name="asset_evals"),
27
- re_path("^inventory/(?P<calid>[0-9 A-Z-]*)/$", views.asset_profile, name="asset_profile"),
26
+ re_path("^inventory/(?P<calid>[0-9 A-Z-]*)/evaluations/$", views.asset_evals, name="asset_evals"),
27
+ re_path("^inventory/(?P<calid>[0-9 A-Z-]*)/$", views.asset_profile, name="asset_profile"),
28
+ re_path("^inventory/(?P<calid>[0-9 A-Z-]*)/sensors/$", views.asset_sensors, name="asset_sensors"),
29
+ re_path("^inventory/(?P<calid>[0-9 A-Z-]*)/sensor_upload", views.sensor_upload, name="sensor_upload"),
28
30
  ]
@@ -17,10 +17,13 @@ from django.core.paginator import Paginator
17
17
  from django.template import loader
18
18
  from django.http import HttpResponse
19
19
  from django.contrib.auth.decorators import login_required
20
+ from django.shortcuts import render, redirect, get_object_or_404
21
+ from django.forms import formset_factory
20
22
 
21
23
  from irie.apps.events.models import EventRecord
22
24
  from irie.apps.site.view_utils import raise404
23
- from irie.apps.inventory.models import Asset
25
+ from irie.apps.inventory.models import Asset, SensorGroup, Sensor, Datum
26
+ from irie.apps.inventory.forms import SensorGroupForm, SensorForm, SensorFormSet
24
27
  from .filters import AssetFilter
25
28
  # Predictors
26
29
  from irie.apps.prediction.runners.hazus import hazus_fragility
@@ -315,7 +318,10 @@ def _make_tables(asset):
315
318
  } for group in asset.nbi_data.values()
316
319
  ]
317
320
  tables.extend(nbi_data)
318
- tables = [tables[2], *tables[:2], *tables[3:]]
321
+ try:
322
+ tables = [tables[2], *tables[:2], *tables[3:]]
323
+ except:
324
+ pass
319
325
  condition = {}
320
326
  for table in tables:
321
327
  keys = set()
@@ -339,6 +345,53 @@ def _make_tables(asset):
339
345
  return tables
340
346
 
341
347
 
348
+ @login_required(login_url="/login/")
349
+ def asset_sensors(request, calid):
350
+ asset = Asset.objects.get(calid=calid)
351
+ context = {
352
+ "asset": asset,
353
+ "groups": SensorGroup.objects.filter(asset=asset)
354
+ }
355
+ html_template = loader.get_template("inventory/asset-sensors.html")
356
+ return HttpResponse(html_template.render(context, request))
357
+
358
+ @login_required(login_url="/login/")
359
+ def sensor_upload(request, calid):
360
+ asset = get_object_or_404(Asset, calid=calid) # Fetch the asset using calid
361
+ datums = Datum.objects.filter(asset=asset).values(
362
+ 'id', 'orient_x', 'locate_x', 'orient_y', 'locate_y', 'orient_z', 'locate_z'
363
+ )
364
+ SensorFormSet = formset_factory(SensorForm, extra=1, can_delete=False)
365
+
366
+ if request.method == "POST":
367
+ group_form = SensorGroupForm(request.POST, asset=asset)
368
+ formset = SensorFormSet(request.POST)
369
+
370
+ if group_form.is_valid() and formset.is_valid():
371
+ sensor_group = group_form.save(commit=False)
372
+ sensor_group.asset = asset # Assign the asset
373
+ sensor_group.save()
374
+
375
+ for form in formset:
376
+ if form.cleaned_data and not form.cleaned_data.get('DELETE', False):
377
+ sensor = form.save(commit=False)
378
+ sensor.group = sensor_group
379
+ sensor.save()
380
+
381
+ return redirect('asset_sensors', calid=calid) # Redirect after successful submission
382
+
383
+ else:
384
+ group_form = SensorGroupForm(asset=asset)
385
+ formset = SensorFormSet()
386
+
387
+ return render(request, "inventory/sensor-upload.html", {
388
+ "group_form": group_form,
389
+ "formset": formset,
390
+ "asset": asset,
391
+ "datums": list(datums)
392
+ })
393
+
394
+
342
395
  def _filter_asset_table(request):
343
396
  # Copy the GET parameters and remove the 'page' parameter
344
397
  page_query = request.GET.copy()
@@ -65,7 +65,7 @@
65
65
  <li class="nav-item {% if 'events' in segment %} active {% endif %}">
66
66
  <span
67
67
  class="nav-link collapsed d-flex justify-content-between align-items-center"
68
- data-bs-toggle="collapse" data-bs-target="#submenu-app">
68
+ data-bs-toggle="collapse" data-bs-target="#submenu-events">
69
69
  <span>
70
70
  <span class="sidebar-icon">
71
71
  <svg class="icon icon-xs me-2" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5 4a3 3 0 00-3 3v6a3 3 0 003 3h10a3 3 0 003-3V7a3 3 0 00-3-3H5zm-1 9v-1h5v2H5a1 1 0 01-1-1zm7 1h4a1 1 0 001-1v-1h-5v2zm0-4h5V8h-5v2zM9 8H4v2h5V8z" clip-rule="evenodd"></path></svg>
@@ -78,7 +78,7 @@
78
78
  </span>
79
79
  </span>
80
80
  <div class="multi-level collapse {% if 'events' in segment %} show {% endif %}"
81
- role="list" id="submenu-app" aria-expanded="false">
81
+ role="list" id="submenu-events" aria-expanded="false">
82
82
  <ul class="flex-column nav">
83
83
  <li class="nav-item {% if 'cgs' in segment %} active {% endif %}">
84
84
  <a class="nav-link" href="{% url 'event_table' %}">
@@ -99,15 +99,34 @@
99
99
  </a>
100
100
  </li>
101
101
 
102
- <li class="nav-item {% if 'networks' in segment %} active {% endif %}">
103
- <a href="{% url 'networks' %}" class="nav-link">
104
- <span class="sidebar-icon">
105
- <svg class="icon icon-xs me-2" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M2 10a8 8 0 018-8v8h8a8 8 0 11-16 0z"></path><path d="M12 2.252A8.014 8.014 0 0117.748 8H12V2.252z"></path></svg>
106
- </span>
107
- <span class="sidebar-text">Networks</span>
108
- </a>
102
+ <li class="nav-item {% if 'applications' in segment %} active {% endif %}">
103
+ <span
104
+ class="nav-link collapsed d-flex justify-content-between align-items-center"
105
+ data-bs-toggle="collapse" data-bs-target="#submenu-apps">
106
+ <span>
107
+ <span class="sidebar-icon">
108
+ <svg class="icon icon-xs me-2" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5 4a3 3 0 00-3 3v6a3 3 0 003 3h10a3 3 0 003-3V7a3 3 0 00-3-3H5zm-1 9v-1h5v2H5a1 1 0 01-1-1zm7 1h4a1 1 0 001-1v-1h-5v2zm0-4h5V8h-5v2zM9 8H4v2h5V8z" clip-rule="evenodd"></path></svg>
109
+ </span>
110
+ <span title="Listing of processed events"
111
+ class="sidebar-text">Applications</span>
112
+ </span>
113
+ <span class="link-arrow">
114
+ <svg class="icon icon-sm" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path></svg>
115
+ </span>
116
+ </span>
117
+ <div class="multi-level collapse {% if app.url in irie_url_name %} show {% endif %}"
118
+ role="list" id="submenu-apps" aria-expanded="false">
119
+ <ul class="flex-column nav">
120
+ {% for app in irie_apps %}
121
+ <li class="nav-item {% if app.url in irie_url_name %} active {% endif %}">
122
+ <a class="nav-link" href="{% url app.url %}">
123
+ <span class="sidebar-text">{{ app.name }}</span>
124
+ </a>
125
+ </li>
126
+ {% endfor %}
127
+ </ul>
128
+ </div>
109
129
  </li>
110
-
111
130
  <li role="separator" class="dropdown-divider mt-4 mb-3 border-gray-700"></li>
112
131
 
113
132
 
@@ -6,6 +6,10 @@
6
6
  {% endblock %}
7
7
 
8
8
  {% block stylesheets %}
9
+ {% if asset.rendering %}
10
+ <!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
11
+ <script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/4.0.0/model-viewer.min.js"></script>
12
+ {% endif %}
9
13
  <style>
10
14
  .ct-series-a .ct-point, .ct-series-a .ct-line, .ct-series-a .ct-bar, .ct-series-a .ct-slice-donut{
11
15
  stroke: #506690;
@@ -60,15 +64,21 @@
60
64
  <div class="tab-content" id="pills-tabContent">
61
65
  <div class="tab-pane fade show active"
62
66
  id="pills-rendering" role="tabpanel">
63
- {% if asset.is_complete %}
64
- {% with cesmd=asset.cesmd %}
65
- {% with filename="bridges/InteractiveTwin-"|add:asset.cesmd|add:".html" %}
66
- {% include filename %}
67
- {% endwith %}
68
- {% endwith %}
67
+ {% if asset.rendering %}
68
+ <model-viewer
69
+ id="viewer"
70
+ src="{{ asset.rendering }}"
71
+ alt="3D Model"
72
+ auto-rotate
73
+ camera-controls
74
+ interaction-prompt="none"
75
+ camera-orbit="0deg 75deg 2m"
76
+ field-of-view="30deg"
77
+ style="width: 100%; height: 400px;">
78
+ </model-viewer>
69
79
  {% endif %}
70
80
  </div>
71
- <div class="tab-pane fade show {% if not asset.is_complete %}active{% endif %}"
81
+ <div class="tab-pane fade show {% if not asset.rendering %}active{% endif %}"
72
82
  id="pills-sensors" role="tabpanel">
73
83
  <img src="{{ ASSETS_ROOT }}/inventory/ll{{ asset.cesmd|slice:"2:" }}.svg">
74
84
  </div>
@@ -129,15 +129,23 @@
129
129
  <div class="btn-toolbar mb-2 mb-md-0">
130
130
  <a role="button"
131
131
  href="/inventory/{{ asset.calid }}/predictors/"
132
- class="btn btn-sm btn-outline-primary d-inline-flex align-items-center">
132
+ class="btn btn-sm btn-outline-primary d-inline-flex align-items-center mx-1">
133
133
  <svg class="icon icon-xs me-2" fill="none"
134
134
  stroke="currentColor" viewBox="0 0 24 24"
135
135
  xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path></svg>
136
136
  Predictors
137
137
  </a>
138
+ <a role="button"
139
+ href="{% url 'asset_sensors' calid=asset.calid %}"
140
+ class="btn btn-sm btn-outline-primary d-inline-flex align-items-center mx-1">
141
+ <svg class="icon icon-xs me-2" fill="none"
142
+ stroke="currentColor" viewBox="0 0 24 24"
143
+ xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path></svg>
144
+ Sensors
145
+ </a>
138
146
  </div>
139
147
  </div>
140
- {% if True %}
148
+ {% if asset.nbi_data.NBI_BRIDGE %}
141
149
  <div class="col-10">
142
150
  <p>
143
151
  {% if asset.id|divisibleby:3 %}
@@ -0,0 +1,69 @@
1
+ <!--
2
+ ===----------------------------------------------------------------------===#
3
+
4
+ STAIRLab -- STructural Artificial Intelligence Laboratory
5
+
6
+ ===----------------------------------------------------------------------===#
7
+
8
+ Claudio Perez, Summer 2023
9
+ Chrystal Chern, Spring 2025
10
+
11
+ -->
12
+
13
+ {% extends "layouts/base.html" %}
14
+ {% block title %} {{ asset.calid }} Sensors {% endblock %}
15
+
16
+ {% block content %}
17
+ <h1><code>{{ asset.calid }}</code> Sensors</h1>
18
+ <div class="py-4 align-right">
19
+ <a role="button" class="button btn btn-outline-white btn-gray-600 text-white"
20
+ href="{% url 'asset_profile' calid=asset.calid %}" class="me-1">Back to Structure</a>
21
+ <a role="button" class="button btn btn-outline-white btn-gray-600 text-white"
22
+ href="{% url 'sensor_upload' calid=asset.calid %}" class="me-1">Add SensorGroup</a>
23
+ </div>
24
+
25
+ <div class="col-lg-10">
26
+ {% for group in groups %}
27
+ <div class="card shadow-lg mb-4">
28
+ <div class="card-header bg-dark text-white fw-bold d-flex justify-content-between align-items-center">
29
+ <span>{{ group.name }}</span>
30
+ <span class="badge bg-gray text-white px-2 py-1 fs-6">Datum: {{ group.datum.name }}</span>
31
+ </div>
32
+ <div class="card-body">
33
+ <table class="table table-striped table-hover table-bordered">
34
+ <thead class="table-dark">
35
+ <tr>
36
+ <th>Sensor</th>
37
+ <th>x</th>
38
+ <th>y</th>
39
+ <th>z</th>
40
+ <th>dx</th>
41
+ <th>dy</th>
42
+ <th>dz</th>
43
+ </tr>
44
+ </thead>
45
+ <tbody>
46
+ {% for sensor in group.sensors.all %}
47
+ <tr>
48
+ <td class="fw-bold">{{ sensor.name }}</td>
49
+ <td>{{ sensor.x }}</td>
50
+ <td>{{ sensor.y }}</td>
51
+ <td>{{ sensor.z }}</td>
52
+ <td>{{ sensor.dx }}</td>
53
+ <td>{{ sensor.dy }}</td>
54
+ <td>{{ sensor.dz }}</td>
55
+ </tr>
56
+ {% endfor %}
57
+ </tbody>
58
+ </table>
59
+ </div>
60
+ </div>
61
+ {% endfor %}
62
+ </div>
63
+
64
+ {% endblock %}
65
+
66
+ {% block javascripts %}
67
+
68
+ {% endblock %}
69
+