irie 0.0.59__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.
- irie/apps/evaluation/models.py +7 -0
- irie/apps/events/views_events.py +2 -2
- irie/apps/inventory/models.py +1 -1
- irie/apps/inventory/views.py +7 -1
- irie/apps/prediction/forms/csi_upload.py +68 -0
- irie/apps/prediction/runners/__init__.py +6 -4
- irie/apps/prediction/runners/opensees/__init__.py +49 -14
- irie/apps/prediction/urls.py +6 -4
- irie/apps/prediction/views.py +248 -17
- irie/apps/static/assets/css/brace.css +1 -1
- irie/apps/static/assets/css/brace.css.map +1 -1
- irie/apps/static/assets/css/brace.min.css +1 -1
- irie/apps/static/assets/js/brace.js +101 -42
- irie/apps/templates/includes/footer.html +1 -1
- irie/apps/templates/inventory/asset-event-summary.html +7 -0
- irie/apps/templates/inventory/asset-on-map.html +22 -22
- irie/apps/templates/inventory/asset-on-map.js +115 -113
- irie/apps/templates/inventory/create-datum.html +27 -27
- irie/apps/templates/layouts/base.html +3 -3
- irie/apps/templates/prediction/create-model.html +32 -29
- irie/apps/templates/prediction/upload/confirm.html +93 -0
- irie/apps/templates/prediction/upload/step.html +119 -0
- irie/apps/templates/prediction/veux/navigator.html +54 -38
- irie/apps/templates/prediction/veux/navigator.js +222 -154
- irie/apps/templates/prediction/xara-profile.html +7 -3
- irie/core/settings.py +1 -0
- {irie-0.0.59.dist-info → irie-0.0.60.dist-info}/METADATA +7 -6
- {irie-0.0.59.dist-info → irie-0.0.60.dist-info}/RECORD +32 -29
- /irie/apps/prediction/{forms.py → forms/__init__.py} +0 -0
- {irie-0.0.59.dist-info → irie-0.0.60.dist-info}/WHEEL +0 -0
- {irie-0.0.59.dist-info → irie-0.0.60.dist-info}/entry_points.txt +0 -0
- {irie-0.0.59.dist-info → irie-0.0.60.dist-info}/top_level.txt +0 -0
irie/apps/evaluation/models.py
CHANGED
|
@@ -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":
|
irie/apps/events/views_events.py
CHANGED
|
@@ -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
|
|
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
|
irie/apps/inventory/models.py
CHANGED
irie/apps/inventory/views.py
CHANGED
|
@@ -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"] = (
|
|
@@ -451,7 +454,10 @@ def sensor_upload(request, calid):
|
|
|
451
454
|
"group_form": group_form,
|
|
452
455
|
"formset": formset,
|
|
453
456
|
"renderings": [
|
|
454
|
-
{
|
|
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
|
|
@@ -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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
500
|
-
|
|
511
|
+
# if type == "PEAK_ACCEL":
|
|
512
|
+
# return _clean_json(peak_acceleration_metric(output_dir, config))
|
|
501
513
|
|
|
502
|
-
|
|
503
|
-
|
|
514
|
+
# elif type == "PEAK_DRIFT":
|
|
515
|
+
# return _clean_json(peak_drift_metric(model, output_dir, config))
|
|
504
516
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
# return accel_response_history_plot(output_dir, config)
|
|
508
|
-
|
|
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 =
|
|
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
|
-
|
|
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):
|
irie/apps/prediction/urls.py
CHANGED
|
@@ -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
|
]
|
irie/apps/prediction/views.py
CHANGED
|
@@ -16,13 +16,15 @@ import uuid
|
|
|
16
16
|
import base64
|
|
17
17
|
import hashlib
|
|
18
18
|
|
|
19
|
+
from django.template.loader import render_to_string
|
|
20
|
+
from django.utils.html import escape
|
|
19
21
|
from django.template import loader
|
|
20
22
|
from django.contrib.auth.decorators import login_required
|
|
21
23
|
from django.core.exceptions import ObjectDoesNotExist
|
|
22
24
|
from django.core.files.base import ContentFile
|
|
23
25
|
from django.shortcuts import HttpResponse, get_object_or_404
|
|
24
26
|
|
|
25
|
-
from irie.apps.inventory.models import Asset
|
|
27
|
+
from irie.apps.inventory.models import Asset, Datum
|
|
26
28
|
from irie.apps.prediction.predictor import PREDICTOR_TYPES
|
|
27
29
|
from irie.apps.prediction.models import PredictorModel
|
|
28
30
|
from .forms import PredictorForm
|
|
@@ -76,25 +78,93 @@ def asset_predictors(request, calid):
|
|
|
76
78
|
def predictor_render(request, calid, preid):
|
|
77
79
|
|
|
78
80
|
predictor = get_object_or_404(PredictorModel, pk=int(preid))
|
|
81
|
+
|
|
82
|
+
canvas = None
|
|
83
|
+
|
|
84
|
+
sname = request.GET.get("section", None)
|
|
85
|
+
|
|
86
|
+
runner = predictor.runner
|
|
87
|
+
|
|
88
|
+
if sname is None:
|
|
89
|
+
try:
|
|
90
|
+
artist = runner.render()
|
|
91
|
+
if artist is None:
|
|
92
|
+
return HttpResponse(
|
|
93
|
+
json.dumps({"error": "No rendering available"}),
|
|
94
|
+
content_type="application/json",
|
|
95
|
+
status=404
|
|
96
|
+
)
|
|
97
|
+
canvas = artist.canvas
|
|
98
|
+
except Exception as e:
|
|
99
|
+
return HttpResponse(
|
|
100
|
+
json.dumps({"error": str(e)}),
|
|
101
|
+
content_type="application/json",
|
|
102
|
+
status=500
|
|
103
|
+
)
|
|
104
|
+
else:
|
|
105
|
+
try:
|
|
106
|
+
_, mesh = runner.structural_section(sname)
|
|
107
|
+
|
|
108
|
+
artist = veux.create_artist(mesh.model, canvas="gltf")
|
|
109
|
+
artist.draw_surfaces()
|
|
110
|
+
# artist.draw_outlines()
|
|
111
|
+
canvas = artist.canvas
|
|
112
|
+
# canvas = veux._create_canvas(name="gltf")
|
|
113
|
+
import numpy as np
|
|
114
|
+
R = np.array([[1, 0],
|
|
115
|
+
[0, 1],
|
|
116
|
+
[0, 0]])
|
|
117
|
+
exterior = mesh.exterior()
|
|
118
|
+
exterior = np.append(exterior, np.array([[exterior[0][0], exterior[0][1]]]), axis=0)
|
|
119
|
+
|
|
120
|
+
canvas.plot_lines(exterior@R.T)
|
|
121
|
+
if (interior := mesh.interior()) is not None:
|
|
122
|
+
for i in interior:
|
|
123
|
+
i = np.append(i, np.array([[i[0][0], i[0][1]]]), axis=0)
|
|
124
|
+
canvas.plot_lines(i@R.T)
|
|
125
|
+
try:
|
|
126
|
+
canvas.plot_vectors(np.zeros((3,3)),
|
|
127
|
+
np.eye(3)*min(mesh.depth, mesh.width)/3, extrude=True)
|
|
128
|
+
except:
|
|
129
|
+
pass
|
|
130
|
+
|
|
131
|
+
except Exception as e:
|
|
132
|
+
raise e
|
|
133
|
+
return HttpResponse(
|
|
134
|
+
json.dumps({"error": "Section not found"}),
|
|
135
|
+
content_type="application/json",
|
|
136
|
+
status=404
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
if canvas is None:
|
|
140
|
+
return HttpResponse(
|
|
141
|
+
json.dumps({"error": "No rendering available"}),
|
|
142
|
+
content_type="application/json",
|
|
143
|
+
status=404
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
glb = canvas.to_glb()
|
|
147
|
+
return HttpResponse(glb, content_type="application/binary")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@login_required(login_url="/login/")
|
|
151
|
+
def predictor_table(request, calid, preid):
|
|
152
|
+
|
|
153
|
+
predictor = get_object_or_404(PredictorModel, pk=int(preid))
|
|
79
154
|
|
|
80
155
|
sname = request.GET.get("section", None)
|
|
81
156
|
|
|
82
|
-
runner =
|
|
157
|
+
runner = predictor.runner
|
|
83
158
|
|
|
84
159
|
try:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
can.plot_lines(mesh.exterior()@R.T)
|
|
91
|
-
if (interior := mesh.interior()) is not None:
|
|
92
|
-
for i in interior:
|
|
93
|
-
can.plot_lines(i@R.T)
|
|
94
|
-
glb = can.to_glb()
|
|
95
|
-
return HttpResponse(glb, content_type="application/binary")
|
|
160
|
+
properties, _ = runner.structural_section(sname)
|
|
161
|
+
|
|
162
|
+
data = json.dumps(properties)
|
|
163
|
+
return HttpResponse(data,
|
|
164
|
+
content_type="application/json")
|
|
96
165
|
|
|
97
166
|
except Exception as e:
|
|
167
|
+
print(e)
|
|
98
168
|
return HttpResponse(
|
|
99
169
|
json.dumps({"error": "Section not found"}),
|
|
100
170
|
content_type="application/json",
|
|
@@ -102,6 +172,44 @@ def predictor_render(request, calid, preid):
|
|
|
102
172
|
)
|
|
103
173
|
|
|
104
174
|
|
|
175
|
+
@login_required(login_url="/login/")
|
|
176
|
+
def predictor_analysis(request, calid, preid):
|
|
177
|
+
|
|
178
|
+
predictor = get_object_or_404(PredictorModel, pk=int(preid))
|
|
179
|
+
|
|
180
|
+
runner = predictor.runner
|
|
181
|
+
sname = request.GET.get("section", None)
|
|
182
|
+
|
|
183
|
+
if sname is not None:
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
_, mesh = runner.structural_section(sname)
|
|
187
|
+
except Exception as e:
|
|
188
|
+
return HttpResponse(
|
|
189
|
+
json.dumps({"error": "Section not found"}),
|
|
190
|
+
content_type="application/json",
|
|
191
|
+
status=404
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
try:
|
|
195
|
+
content = "<div></div>"
|
|
196
|
+
return HttpResponse(content)
|
|
197
|
+
|
|
198
|
+
except Exception as e:
|
|
199
|
+
return HttpResponse(
|
|
200
|
+
json.dumps({"error": str(e)}),
|
|
201
|
+
content_type="application/json",
|
|
202
|
+
status=500
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
return HttpResponse(
|
|
207
|
+
json.dumps({"error": str(e)}),
|
|
208
|
+
content_type="application/json",
|
|
209
|
+
status=500
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
|
|
105
213
|
@login_required(login_url="/login/")
|
|
106
214
|
def predictor_profile(request, calid, preid):
|
|
107
215
|
|
|
@@ -124,8 +232,8 @@ def predictor_profile(request, calid, preid):
|
|
|
124
232
|
|
|
125
233
|
predictor = get_object_or_404(PredictorModel, pk=int(preid))
|
|
126
234
|
|
|
127
|
-
context["asset"]
|
|
128
|
-
context["runner"]
|
|
235
|
+
context["asset"] = asset
|
|
236
|
+
context["runner"] = predictor.runner
|
|
129
237
|
context["predictor"] = predictor
|
|
130
238
|
context["sensors"] = predictor.sensorassignment_set.all()
|
|
131
239
|
|
|
@@ -151,7 +259,8 @@ def predictor_profile(request, calid, preid):
|
|
|
151
259
|
if "DEBUG" in os.environ and os.environ["DEBUG"]:
|
|
152
260
|
raise e
|
|
153
261
|
html_template = loader.get_template("site/page-500.html")
|
|
154
|
-
return HttpResponse(html_template.render(context, request))
|
|
262
|
+
return HttpResponse(html_template.render(context, request), status=500)
|
|
263
|
+
|
|
155
264
|
|
|
156
265
|
|
|
157
266
|
@login_required(login_url="/login/")
|
|
@@ -204,7 +313,6 @@ def asset_map(request, calid):
|
|
|
204
313
|
return HttpResponse(r200.render(context, request))
|
|
205
314
|
|
|
206
315
|
except Exception as e:
|
|
207
|
-
raise e
|
|
208
316
|
r500 = loader.get_template("site/page-500.html")
|
|
209
317
|
return HttpResponse(r500.render({"message": str(e)}, request), status=500)
|
|
210
318
|
|
|
@@ -220,7 +328,129 @@ def create_mdof(request):
|
|
|
220
328
|
html_template = loader.get_template("prediction/" + page_template)
|
|
221
329
|
return HttpResponse(html_template.render(context, request))
|
|
222
330
|
|
|
331
|
+
from formtools.wizard.views import SessionWizardView
|
|
332
|
+
from .forms.csi_upload import DatumCreateForm, DatumSelectForm, PredictorForm, SensorForm, ConfirmForm
|
|
333
|
+
|
|
334
|
+
FORMS = [
|
|
335
|
+
("select datum", DatumSelectForm),
|
|
336
|
+
("create datum", DatumCreateForm),
|
|
337
|
+
("structure", PredictorForm),
|
|
338
|
+
("sensor", SensorForm),
|
|
339
|
+
("confirm", ConfirmForm),
|
|
340
|
+
]
|
|
341
|
+
|
|
342
|
+
TEMPLATES = {
|
|
343
|
+
"select datum": "prediction/upload/step.html",
|
|
344
|
+
"create datum": "prediction/upload/step.html",
|
|
345
|
+
"structure": "prediction/upload/step.html",
|
|
346
|
+
"sensor": "prediction/upload/step.html",
|
|
347
|
+
"confirm": "prediction/upload/step.html",
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
from django.shortcuts import redirect
|
|
351
|
+
from django.core.files.storage import FileSystemStorage
|
|
352
|
+
from django.conf import settings
|
|
353
|
+
|
|
354
|
+
class CsiUpload(SessionWizardView):
|
|
355
|
+
form_list = FORMS
|
|
356
|
+
file_storage = FileSystemStorage(location=settings.MEDIA_ROOT)
|
|
357
|
+
|
|
358
|
+
condition_dict = {
|
|
359
|
+
"create datum": lambda self: (
|
|
360
|
+
self.get_cleaned_data_for_step("select datum") is None \
|
|
361
|
+
or self.get_cleaned_data_for_step("select datum").get("datum") is None
|
|
362
|
+
),
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
def dispatch(self, request, *args, **kwargs):
|
|
366
|
+
self.asset = get_object_or_404(Asset, calid=kwargs["calid"])
|
|
367
|
+
self.request = request
|
|
368
|
+
return super().dispatch(request, *args, **kwargs)
|
|
369
|
+
|
|
370
|
+
def get_template_names(self):
|
|
371
|
+
return [TEMPLATES[self.steps.current]]
|
|
372
|
+
|
|
373
|
+
def get_form_kwargs(self, step=None):
|
|
374
|
+
kwargs = super().get_form_kwargs(step)
|
|
375
|
+
|
|
376
|
+
if step == "select datum":
|
|
377
|
+
kwargs["asset"] = self.asset
|
|
378
|
+
kwargs["initial"] = {"asset": self.asset.id}
|
|
379
|
+
kwargs["datum_queryset"] = Datum.objects.filter(asset=self.asset)
|
|
380
|
+
elif step == "create datum":
|
|
381
|
+
kwargs["asset"] = self.asset
|
|
382
|
+
kwargs["initial"] = {"asset": self.asset.id}
|
|
383
|
+
elif step == "structure":
|
|
384
|
+
kwargs["asset"] = self.asset
|
|
385
|
+
kwargs["initial"] = {
|
|
386
|
+
"asset": self.asset,
|
|
387
|
+
"active": False,
|
|
388
|
+
"protocol": PredictorModel.Protocol.TYPE1,
|
|
389
|
+
}
|
|
390
|
+
elif step == "sensor":
|
|
391
|
+
datum_data = self.get_cleaned_data_for_step("datum") or {}
|
|
392
|
+
kwargs["initial"] = {
|
|
393
|
+
# "datum": datum_data.get("id"),
|
|
394
|
+
"asset": self.asset.id,
|
|
395
|
+
"node": -1,
|
|
396
|
+
"predictor": self.get_cleaned_data_for_step("structure").get("id")
|
|
397
|
+
}
|
|
398
|
+
return kwargs
|
|
399
|
+
|
|
400
|
+
def get_context_data(self, form, **kwargs):
|
|
401
|
+
context = super().get_context_data(form=form, **kwargs)
|
|
402
|
+
context["asset"] = self.asset
|
|
403
|
+
context["segment"] = "assets"
|
|
404
|
+
context["step"] = self.steps.current
|
|
405
|
+
|
|
406
|
+
if self.steps.current == "confirm":
|
|
407
|
+
predictor = self.get_form("structure").save(commit=False)
|
|
408
|
+
model_file = self.get_cleaned_data_for_step("structure").get("config_file")
|
|
409
|
+
|
|
410
|
+
predictor.protocol = PredictorModel.Protocol.TYPE1
|
|
411
|
+
predictor.config_file = model_file
|
|
412
|
+
artist = predictor.runner.render()
|
|
413
|
+
glb = artist.canvas.to_glb()
|
|
414
|
+
glb64 = base64.b64encode(glb).decode("utf-8")
|
|
415
|
+
|
|
416
|
+
context["rndrdoc"] = escape(render_to_string(
|
|
417
|
+
"inventory/asset-on-map.html",
|
|
418
|
+
context={
|
|
419
|
+
"asset": self.asset,
|
|
420
|
+
"viewer": "three",
|
|
421
|
+
"scale": 1/3.2808, # TODO
|
|
422
|
+
# "offset": json.dumps(list(reversed(list(self.asset.coordinates)))),
|
|
423
|
+
"rotate": "[0, 0, 0]",
|
|
424
|
+
"render_glb": f"data:application/octet-stream;base64,{glb64}",
|
|
425
|
+
"location": json.dumps(list(reversed(list(self.asset.coordinates)))),
|
|
426
|
+
},
|
|
427
|
+
request=self.request
|
|
428
|
+
))
|
|
429
|
+
return context
|
|
430
|
+
|
|
431
|
+
def done(self, form_list, form_dict, **kwargs):
|
|
432
|
+
# datum comes from select step unless create step ran
|
|
433
|
+
if "select datum" in form_dict:
|
|
434
|
+
datum = form_dict["select datum"].cleaned_data["datum"]
|
|
435
|
+
elif "datum create" in form_dict:
|
|
436
|
+
datum = form_dict["datum create"].save(commit=False)
|
|
437
|
+
datum.save()
|
|
438
|
+
else:
|
|
439
|
+
datum = form_dict["select datum"].cleaned_data["datum"]
|
|
440
|
+
|
|
441
|
+
predictor = form_dict["structure"].save(commit=False)
|
|
442
|
+
sensor = form_dict["sensor"].save(commit=False)
|
|
223
443
|
|
|
444
|
+
predictor.asset = self.asset
|
|
445
|
+
predictor.active = True
|
|
446
|
+
predictor.protocol = PredictorModel.Protocol.TYPE1
|
|
447
|
+
predictor.save()
|
|
448
|
+
|
|
449
|
+
sensor.node = -1
|
|
450
|
+
sensor.predictor = predictor
|
|
451
|
+
sensor.save()
|
|
452
|
+
return redirect("asset_predictors", calid=predictor.asset.calid)
|
|
453
|
+
|
|
224
454
|
@login_required(login_url="/login/")
|
|
225
455
|
def create_model(request, calid):
|
|
226
456
|
|
|
@@ -252,6 +482,7 @@ def create_model(request, calid):
|
|
|
252
482
|
except Exception as e:
|
|
253
483
|
return HttpResponse(r400.render({"message": json.dumps({"error": str(e)})}), status=500)
|
|
254
484
|
|
|
485
|
+
|
|
255
486
|
# 3) Render the model
|
|
256
487
|
outlines = collect_outlines(csi, model.frame_tags)
|
|
257
488
|
artist = veux.create_artist(model,
|
|
@@ -5577,7 +5577,7 @@ textarea.form-control-lg {
|
|
|
5577
5577
|
--bs-accordion-body-padding-x: 1.25rem;
|
|
5578
5578
|
--bs-accordion-body-padding-y: 1rem;
|
|
5579
5579
|
--bs-accordion-active-color: rgb(27.9, 36.9, 49.5);
|
|
5580
|
-
--bs-accordion-active-bg:
|
|
5580
|
+
--bs-accordion-active-bg: transparent;
|
|
5581
5581
|
}
|
|
5582
5582
|
|
|
5583
5583
|
.accordion-button {
|