irie 0.0.58__py3-none-any.whl → 0.0.60__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 (35) hide show
  1. irie/apps/evaluation/models.py +7 -0
  2. irie/apps/events/views_events.py +2 -2
  3. irie/apps/inventory/migrations/0009_datum_angle_x_datum_angle_y.py +23 -0
  4. irie/apps/inventory/models.py +1 -1
  5. irie/apps/inventory/views.py +10 -4
  6. irie/apps/prediction/forms/csi_upload.py +68 -0
  7. irie/apps/prediction/migrations/0007_remove_sensorassignment_orient_x_and_more.py +25 -0
  8. irie/apps/prediction/runners/__init__.py +6 -4
  9. irie/apps/prediction/runners/opensees/__init__.py +49 -14
  10. irie/apps/prediction/urls.py +6 -4
  11. irie/apps/prediction/views.py +248 -17
  12. irie/apps/static/assets/css/brace.css +1 -1
  13. irie/apps/static/assets/css/brace.css.map +1 -1
  14. irie/apps/static/assets/css/brace.min.css +1 -1
  15. irie/apps/static/assets/js/brace.js +101 -42
  16. irie/apps/templates/includes/footer.html +1 -1
  17. irie/apps/templates/inventory/asset-event-summary.html +7 -0
  18. irie/apps/templates/inventory/asset-on-map.html +22 -22
  19. irie/apps/templates/inventory/asset-on-map.js +115 -113
  20. irie/apps/templates/inventory/create-datum.html +44 -0
  21. irie/apps/templates/inventory/sensor-upload.html +22 -22
  22. irie/apps/templates/layouts/base.html +3 -3
  23. irie/apps/templates/prediction/create-model.html +32 -37
  24. irie/apps/templates/prediction/upload/confirm.html +93 -0
  25. irie/apps/templates/prediction/upload/step.html +119 -0
  26. irie/apps/templates/prediction/veux/navigator.html +54 -38
  27. irie/apps/templates/prediction/veux/navigator.js +222 -154
  28. irie/apps/templates/prediction/xara-profile.html +7 -3
  29. irie/core/settings.py +1 -0
  30. {irie-0.0.58.dist-info → irie-0.0.60.dist-info}/METADATA +7 -6
  31. {irie-0.0.58.dist-info → irie-0.0.60.dist-info}/RECORD +35 -29
  32. /irie/apps/prediction/{forms.py → forms/__init__.py} +0 -0
  33. {irie-0.0.58.dist-info → irie-0.0.60.dist-info}/WHEEL +0 -0
  34. {irie-0.0.58.dist-info → irie-0.0.60.dist-info}/entry_points.txt +0 -0
  35. {irie-0.0.58.dist-info → irie-0.0.60.dist-info}/top_level.txt +0 -0
@@ -51,6 +51,13 @@ class Evaluation(models.Model):
51
51
  # Thread(target=evaluate, args=(event, evaluation)).start()
52
52
 
53
53
  return evaluation
54
+
55
+ def evaluate(self):
56
+ """
57
+ Evaluate the event and update the evaluation status.
58
+ This method can be run in a separate thread.
59
+ """
60
+ return evaluate(self.event, self)
54
61
 
55
62
 
56
63
  def evaluate(event, evaluation)->"Evaluation":
@@ -84,8 +84,8 @@ def save_event(request, event, success_status):
84
84
  # RECORD ID
85
85
  rec_id = motion_data.get("record_identifier", "")
86
86
 
87
- if EventRecord.objects.filter(record_identifier=rec_id).first():
88
- print("\n\nSKIPPING\n\n")
87
+ if (evnt := EventRecord.objects.filter(record_identifier=rec_id).first()):
88
+ print(f"\n\nSKIPPING, {evnt.id}")
89
89
  return HttpResponse(json.dumps({"data": ""}), status=success_status)
90
90
 
91
91
  # CREATE EVENT
@@ -0,0 +1,23 @@
1
+ # Generated by Django 5.1.2 on 2025-07-31 05:48
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('irie_apps_inventory', '0008_alter_sensor_dx_alter_sensor_dy_alter_sensor_dz_and_more'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name='datum',
15
+ name='angle_x',
16
+ field=models.DecimalField(blank=True, decimal_places=2, default=0.0, max_digits=10, null=True),
17
+ ),
18
+ migrations.AddField(
19
+ model_name='datum',
20
+ name='angle_y',
21
+ field=models.DecimalField(blank=True, decimal_places=2, default=0.0, max_digits=10, null=True),
22
+ ),
23
+ ]
@@ -118,7 +118,7 @@ class Datum(models.Model):
118
118
 
119
119
  def __str__(self):
120
120
  return f"{self.name}"
121
-
121
+
122
122
  def to_cardinal(self):
123
123
  """
124
124
  Create rotation matrix from datum basis to cardinal basis.
@@ -170,6 +170,9 @@ def asset_event_summary(request, cesmd, event):
170
170
  evaluation_data = {}
171
171
  evaluation = None
172
172
 
173
+ if request.method == 'POST':
174
+ evaluation.evaluate()
175
+
173
176
  try:
174
177
  for metric in evaluation_data.values():
175
178
  metric["completion"] = (
@@ -400,13 +403,13 @@ def asset_sensors(request, calid):
400
403
 
401
404
  # General Context
402
405
  context = {
403
- "asset": asset,
404
- "segment": "assets",
405
- "groups": SensorGroup.objects.filter(asset=asset)
406
+ "segment": "assets"
406
407
  }
407
408
 
408
409
  # Database
409
410
  asset = get_object_or_404(Asset, calid=calid)
411
+ context["asset"] = asset
412
+ context["groups"] = SensorGroup.objects.filter(asset=asset)
410
413
 
411
414
  return HttpResponse(html_template.render(context, request))
412
415
 
@@ -451,7 +454,10 @@ def sensor_upload(request, calid):
451
454
  "group_form": group_form,
452
455
  "formset": formset,
453
456
  "renderings": [
454
- {"name": predictor.name, "glb": predictor.render_file.url}
457
+ {
458
+ "name": predictor.name,
459
+ "glb": predictor.render_file.url
460
+ }
455
461
  for predictor in PredictorModel.objects.filter(asset=asset, protocol="IRIE_PREDICTOR_V1")
456
462
  if predictor.render_file and predictor.render_file.url
457
463
  ],
@@ -0,0 +1,68 @@
1
+ # forms.py
2
+ from django import forms
3
+ from irie.apps.inventory.models import Datum
4
+ from irie.apps.prediction.models import PredictorModel, SensorAssignment
5
+
6
+ class DatumSelectForm(forms.Form):
7
+ """
8
+ Step 0 – choose an existing datum or say 'create new'.
9
+ The empty choice means 'I want to add a new datum'.
10
+ """
11
+ datum = forms.ModelChoiceField(
12
+ queryset=Datum.objects.none(),
13
+ required=False,
14
+ empty_label="-- create new datum --",
15
+ label="Select Datum",
16
+ help_text="Select an existing datum or create a new one."
17
+ )
18
+
19
+ def __init__(self, *args, asset=None, datum_queryset=None, **kwargs):
20
+ super().__init__(*args, **kwargs)
21
+ self.fields["datum"].queryset = datum_queryset or Datum.objects.none()
22
+
23
+ class DatumCreateForm(forms.ModelForm):
24
+ class Meta:
25
+ model = Datum
26
+ fields = ("angle_x", "angle_y")
27
+
28
+ def __init__(self, *args, asset=None, **kwargs):
29
+ super().__init__(*args, **kwargs)
30
+ self.asset = asset
31
+
32
+ class PredictorForm(forms.ModelForm):
33
+ class Meta:
34
+ model = PredictorModel
35
+ help_text = {
36
+ "config_file": "File exported from CSi Bridge or SAP2000 (.b2k or .s2k).",
37
+ }
38
+ fields = "__all__"
39
+ exclude = [
40
+ "render_file",
41
+ "asset",
42
+ "metrics",
43
+ "active",
44
+ "description",
45
+ "entry_point",
46
+ "config",
47
+ "protocol"
48
+ ]
49
+ def __init__(self, *args, asset=None, **kwargs):
50
+ super().__init__(*args, **kwargs)
51
+ self.asset = asset
52
+ self.fields['name'].widget.attrs["class"] = "rounded-0"
53
+ self.fields['config_file'].widget.attrs["class"] = "rounded-0"
54
+
55
+ class SensorForm(forms.ModelForm):
56
+ class Meta:
57
+ model = SensorAssignment
58
+ fields = (
59
+ "role",
60
+ "sensor"
61
+ )
62
+
63
+ def __init__(self, *args, **kwargs):
64
+ super().__init__(*args, **kwargs)
65
+
66
+ class ConfirmForm(forms.Form):
67
+ # no fields – just a read-only summary screen
68
+ pass
@@ -0,0 +1,25 @@
1
+ # Generated by Django 5.1.2 on 2025-07-31 05:48
2
+
3
+ from django.db import migrations
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('apps_prediction', '0006_remove_sensorassignment_show_x_and_more'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.RemoveField(
14
+ model_name='sensorassignment',
15
+ name='orient_x',
16
+ ),
17
+ migrations.RemoveField(
18
+ model_name='sensorassignment',
19
+ name='orient_y',
20
+ ),
21
+ migrations.RemoveField(
22
+ model_name='sensorassignment',
23
+ name='orient_z',
24
+ ),
25
+ ]
@@ -32,13 +32,15 @@ class Runner:
32
32
  # Create from existing PredictorModel when loaded from database.
33
33
  # This is done when running analysis
34
34
  self.id = pred.id
35
- self.asset = pred.asset
35
+ # self.asset = pred.asset
36
36
  self.name: str = pred.name
37
37
  self.description = "" # conf.description
38
38
  self.conf = pred.config
39
- self.entry_point = pred.entry_point
40
- self.metrics = pred.metrics
41
- self.active = pred.active
39
+ if pred.entry_point:
40
+ self.entry_point = pred.entry_point
41
+ if pred.metrics:
42
+ self.metrics = pred.metrics
43
+ self.active = pred.active
42
44
 
43
45
  # NEW:
44
46
  self.predictor = pred
@@ -6,6 +6,7 @@
6
6
  #
7
7
  import os.path
8
8
  import shutil
9
+ import tqdm
9
10
  import sys, json
10
11
  import zipfile
11
12
  from pathlib import Path
@@ -54,7 +55,7 @@ def _create_excitation(model, predictor, inputs, dt):
54
55
  def _analyze_and_render(model, artist, nt, dt):
55
56
  import veux, veux.motion
56
57
  motion = veux.motion.Motion(artist)
57
- for i in range(nt):
58
+ for i in tqdm.tqdm(range(nt)):
58
59
  if model.analyze(1, dt) != 0:
59
60
  return -1
60
61
  motion.advance(i*dt)
@@ -102,7 +103,7 @@ class OpenSeesRunner(Runner):
102
103
  with open(uploaded_file.name, 'wb+') as destination:
103
104
  for chunk in uploaded_file.chunks():
104
105
  destination.write(chunk)
105
-
106
+
106
107
  # predictor.config_file = uploaded_file # data.pop("file")
107
108
  predictor.name = data.pop("name")
108
109
  predictor.config = data
@@ -227,6 +228,16 @@ class OpenSeesRunner(Runner):
227
228
  "analysis": schemas.load("hwd_analysis.schema.json"),
228
229
  }
229
230
  }
231
+
232
+ def render(self):
233
+ import veux
234
+ model = Job(self._csi).assemble().model
235
+
236
+ outlines = collect_outlines(self._csi, model.frame_tags)
237
+ artist = veux.render(model, canvas="gltf", vertical=3,
238
+ reference={"frame.surface", "frame.axes"},
239
+ model_config={"frame_outlines": outlines})
240
+ return artist
230
241
 
231
242
  def getMetricList(self):
232
243
  return [
@@ -236,6 +247,7 @@ class OpenSeesRunner(Runner):
236
247
  # "ACC_RESPONSE_HISTORY",
237
248
  ]
238
249
 
250
+
239
251
  def newPrediction(self, event, output_directory = None):
240
252
  """
241
253
  Create a new prediction run and return the run_id. If output_directory is None,
@@ -272,7 +284,7 @@ class OpenSeesRunner(Runner):
272
284
  if False:
273
285
  event = event.event_file.path
274
286
  shutil.copyfile(event, run_dir/"event.zip")
275
-
287
+
276
288
  model_file = None
277
289
  if hasattr(self, "model_file") and self.model_file is not None:
278
290
  shutil.copyfile(self.model_file.resolve(),
@@ -327,7 +339,6 @@ class OpenSeesRunner(Runner):
327
339
  # Create model
328
340
  import opensees.openseespy as ops
329
341
 
330
- import sys
331
342
  csi = self._csi
332
343
 
333
344
  with new_cd(self.runs[run_id]["run_output_directory"]):
@@ -445,6 +456,7 @@ class OpenSeesRunner(Runner):
445
456
  for metric in metrics:
446
457
  metric.record(asm)
447
458
 
459
+
448
460
  nt = 500
449
461
  dt = 0.02
450
462
  _create_excitation(model, self.predictor, self.runs[run_id]["inputs"], dt)
@@ -496,16 +508,16 @@ class OpenSeesRunner(Runner):
496
508
  # if type == "COLUMN_STRAIN_STATES":
497
509
  # return _clean_json(column_strain_state_metric(model, output_dir, config))
498
510
 
499
- if type == "PEAK_ACCEL":
500
- return _clean_json(peak_acceleration_metric(output_dir, config))
511
+ # if type == "PEAK_ACCEL":
512
+ # return _clean_json(peak_acceleration_metric(output_dir, config))
501
513
 
502
- elif type == "PEAK_DRIFT":
503
- return _clean_json(peak_drift_metric(model, output_dir, config))
514
+ # elif type == "PEAK_DRIFT":
515
+ # return _clean_json(peak_drift_metric(model, output_dir, config))
504
516
 
505
- elif type == "ACC_RESPONSE_HISTORY":
506
- # config = CONFIG
507
- # return accel_response_history_plot(output_dir, config)
508
- return {}
517
+ # elif type == "ACC_RESPONSE_HISTORY":
518
+ # # config = CONFIG
519
+ # # return accel_response_history_plot(output_dir, config)
520
+ # return {}
509
521
  return {}
510
522
 
511
523
  #
@@ -519,17 +531,40 @@ class OpenSeesRunner(Runner):
519
531
  try:
520
532
  csi_file = self.predictor.config_file
521
533
  self._csi_data = load_csi((str(line.decode()).replace("\r\n","\n") for line in csi_file.readlines()))
534
+
522
535
  except Exception as e:
523
536
  import sys
524
537
  print(f"Error loading CSiBridge file: {e}", file=sys.stderr)
525
- self._csi_data = None
538
+ self._csi_data = {}
526
539
 
527
540
  return self._csi_data
528
541
 
529
542
 
530
543
  def structural_section(self, name):
531
544
  if (s:= create_section(self._csi, name)) is not None:
532
- return {}, s._create_model(mesh_size=0.1)
545
+ model = s._create_model(mesh_size=0.1)
546
+ if model is None:
547
+ return [], None
548
+ cmm = model.cmm()
549
+ cnn = model.cnn()
550
+ cnm = model.cnm()
551
+ properties = [
552
+ {
553
+ "name": "Elastic", "data": [
554
+ {"name": "A", "value": float(cnn[0,0])},
555
+ {"name": "Iyy", "value": float(cmm[1,1])},
556
+ {"name": "Izz", "value": float(cmm[2,2])},
557
+ ],
558
+ },
559
+ {
560
+ "name": "Ultimate", "data": [
561
+ # {"name": "A", "value": float(cnn[0,0])},
562
+ # {"name": "Iyy", "value": float(cmm[1,1])},
563
+ # {"name": "Izz", "value": float(cmm[2,2])},
564
+ ]
565
+ }
566
+ ]
567
+ return properties, model
533
568
 
534
569
 
535
570
  def structural_sections(self):
@@ -7,10 +7,10 @@
7
7
  # Author: Claudio Perez
8
8
  #
9
9
  #----------------------------------------------------------------------------#
10
- from django.urls import re_path
10
+ from django.urls import path, re_path
11
11
  from .views import (
12
- asset_predictors, predictor_profile, predictor_render,
13
- create_mdof, create_model, asset_map
12
+ asset_predictors, predictor_profile, predictor_render, predictor_table,
13
+ create_mdof, create_model, asset_map, CsiUpload, FORMS
14
14
  )
15
15
 
16
16
  _ROOT = "^inventory/(?P<calid>[0-9 A-Z-]*)/predictors"
@@ -18,8 +18,10 @@ _ROOT = "^inventory/(?P<calid>[0-9 A-Z-]*)/predictors"
18
18
  urlpatterns = [
19
19
  re_path(f"{_ROOT}/(?P<preid>[0-9]{{1,}})/$", predictor_profile),
20
20
  re_path(f"{_ROOT}/(?P<preid>[0-9]{{1,}})/render/", predictor_render),
21
+ re_path(f"{_ROOT}/(?P<preid>[0-9]{{1,}})/properties/", predictor_table),
21
22
  re_path(f"{_ROOT}/create/map/$", asset_map),
22
- re_path(f"{_ROOT}/create/model/$", create_model),
23
+ # re_path(f"{_ROOT}/create/model/$", create_model),
24
+ re_path(f"{_ROOT}/create/model/$", CsiUpload.as_view(FORMS), name="csi_upload"),
23
25
  re_path(f"{_ROOT}/create/v1/$", create_mdof),
24
26
  re_path(f"{_ROOT}/$", asset_predictors, name="asset_predictors")
25
27
  ]