irie 0.0.16__py3-none-any.whl → 0.0.17__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.

@@ -11,7 +11,7 @@ import zipfile
11
11
  from pathlib import Path
12
12
  import contextlib
13
13
 
14
- from irie.apps.prediction.runners import (Runner, MetricType, RunID, classproperty)
14
+ from irie.apps.prediction.runners import (Runner, RunID, classproperty)
15
15
 
16
16
  from .utilities import read_model
17
17
  from .metrics import (
@@ -29,7 +29,6 @@ OPENSEES = [
29
29
  @contextlib.contextmanager
30
30
  def new_cd(x):
31
31
  d = os.getcwd()
32
-
33
32
  # This could raise an exception, but it's probably
34
33
  # best to let it propagate and let the caller
35
34
  # deal with it, since they requested x
@@ -52,19 +51,28 @@ class OpenSeesRunner(Runner):
52
51
 
53
52
 
54
53
  @classmethod
55
- def create(cls, asset, request, form):
56
- predictor = form.save(commit=False)
57
-
58
- predictor.entry_point = [
59
- sys.executable, "-m", "opensees"
60
- ]
61
- predictor.config = {}
54
+ def create(cls, asset, request):
55
+ from irie.apps.prediction.models import PredictorModel
56
+ predictor = PredictorModel()
57
+ data = json.loads(request.body)
58
+ # TODO
59
+ data.pop("file")
60
+ uploaded_file = request.FILES.get('config_file', None)
61
+ print(uploaded_file)
62
+ if uploaded_file:
63
+
64
+ with open(uploaded_file.name, 'wb+') as destination:
65
+ for chunk in uploaded_file.chunks():
66
+ destination.write(chunk)
67
+
68
+ # predictor.config_file = uploaded_file # data.pop("file")
69
+ predictor.name = data.pop("name")
70
+ predictor.config = data
71
+ predictor.asset = asset
62
72
  predictor.protocol = "IRIE_PREDICTOR_T4"
63
73
  predictor.active = True
64
- # predictor.metrics = self.getMetricList()
65
74
  return predictor
66
75
 
67
-
68
76
  @classproperty
69
77
  def schema(cls):
70
78
  from . import schemas
@@ -491,3 +499,72 @@ class OpenSeesRunner(Runner):
491
499
  return {}
492
500
  return {}
493
501
 
502
+
503
+ import subprocess
504
+ class Event: pass
505
+
506
+ class PredictorType1(Runner):
507
+ @property
508
+ def platform(self):
509
+ return self.conf.get("platform", "")
510
+
511
+ @classmethod
512
+ def create(cls, asset, request):
513
+ from irie.apps.prediction.models import PredictorModel
514
+ predictor = PredictorModel()
515
+ data = json.loads(request.data.get("json"))
516
+ predictor.entry_point = [
517
+ sys.executable, "-m", "opensees"
518
+ ]
519
+ data["metrics"] = []
520
+
521
+ predictor.name = data.pop("name")
522
+ predictor.config = data
523
+ predictor.asset = asset
524
+ predictor.protocol = "IRIE_PREDICTOR_T1"
525
+ predictor.active = False
526
+ return predictor
527
+
528
+
529
+ @classproperty
530
+ def schema(cls):
531
+ from irie.apps.prediction.runners.opensees import schemas
532
+ return {
533
+ "title": "Structural Model",
534
+ "options": {"disable_collaps": True},
535
+ "schema": "http://json-schema.org/draft-04/schema#",
536
+ "type": "object",
537
+ "properties": {
538
+ "platform": {
539
+ "type": "string",
540
+ "title": "Platform",
541
+ "enum": ["OpenSees","CSiBridge"]
542
+ },
543
+ "model": schemas.load("hwd_conf.schema.json"),
544
+ "analysis": schemas.load("hwd_analysis.schema.json"),
545
+ }
546
+ }
547
+
548
+ def newPrediction(self, event: Event) -> RunID:
549
+ self.event = event
550
+ event_file = Path(event.event_file.path).resolve()
551
+ command = [*self.entry_point, "new", event_file]
552
+ run_id = subprocess.check_output(command).decode().strip()
553
+ return RunID(int(run_id))
554
+
555
+ def runPrediction(self, run_id: RunID):
556
+ command = [*self.entry_point, "run", str(run_id)]
557
+
558
+ if "scale" in self.event.upload_data:
559
+ command.extend(["--scale", str(float(self.event.upload_data["scale"]))])
560
+ print(":: Running ", command, file=sys.stderr)
561
+ subprocess.check_output(command)
562
+
563
+ print(f":: Model {self.name} returned", file=sys.stderr)
564
+ return
565
+
566
+ def getMetricData(self, run, metric):
567
+ try:
568
+ return json.loads(subprocess.check_output([*self.entry_point, "get", str(run), metric]).decode())
569
+ except json.decoder.JSONDecodeError:
570
+ return {}
@@ -1,3 +1,7 @@
1
+ from irie.apps.events.models import EventRecord
2
+ from irie.apps.prediction.runners import Runner, RunID
3
+ from irie.apps.prediction.models import PredictorModel
4
+
1
5
  from pathlib import Path
2
6
  import json
3
7
  import io
@@ -12,6 +16,7 @@ from mdof import transform
12
16
  from scipy.signal import find_peaks
13
17
  from mdof.utilities import Config, extract_channels
14
18
  from matplotlib import colormaps
19
+ import subprocess
15
20
  # try:
16
21
  # import scienceplots
17
22
  # plt.style.use(["science"])
@@ -22,7 +27,155 @@ N_PEAKS = 5 # number of "significant" peaks per record
22
27
  MISSING_CHANNEL_LIMIT = 3 # number of missing output channels allowed before skipping event
23
28
  MAX_ACCEL = 3.0
24
29
 
25
- from irie.apps.events.models import EventRecord
30
+
31
+ class SystemIdRunner(Runner):
32
+ platform = "mdof"
33
+
34
+ schema = {
35
+ "title": "System ID",
36
+ "name": "P2",
37
+ "type": "object",
38
+ "required": [
39
+ "name",
40
+ "decimation",
41
+ "method",
42
+ "channels"
43
+ ],
44
+ "properties": {
45
+ "name": {
46
+ "type": "string",
47
+ "title": "Name",
48
+ "description": "Predictor name",
49
+ "minLength": 2,
50
+ # "default": "S1"
51
+ },
52
+ "method": {
53
+ "type": "string",
54
+ "title": "Method",
55
+ "enum": ["Fourier Spectrum","Response Spectrum","SRIM","OKID"]
56
+ },
57
+ "decimation": {
58
+ "type": "integer",
59
+ "title": "Decimation",
60
+ "default": 1,
61
+ "minimum": 1,
62
+ "maximum": 8
63
+ },
64
+ "order": {
65
+ "type": "integer",
66
+ "title": "Model Order",
67
+ "default": 8,
68
+ "minimum": 2,
69
+ "maximum": 64,
70
+ "options": {"dependencies": {"method": ["SRIM","OKID"]}}
71
+ },
72
+ "horizon": {
73
+ "type": "integer",
74
+ "title": "Prediction Horizon",
75
+ "default": 100,
76
+ "minimum": 50,
77
+ "maximum": 500,
78
+ "options": {"dependencies": {"method": ["SRIM"]}}
79
+ },
80
+ "period_band": {
81
+ "type": "string",
82
+ "title": "Period Band",
83
+ "default": "[0.1,2.3]",
84
+ "options": {"dependencies": {"method": ["Fourier Spectrum"]}},
85
+ "description": "[0.1,2.3] if interested in periods between 0.1 seconds and 2.3 seconds"
86
+ },
87
+ "damping": {
88
+ "type": "float",
89
+ "title": "Damping",
90
+ "default": 0.02,
91
+ "options": {"dependencies": {"method": ["Response Spectrum"]}},
92
+ "description": "assumed damping ratio"
93
+ },
94
+ "channels": {
95
+ "type": "array",
96
+ "format": "table",
97
+ "title": "Channels",
98
+ "uniqueItems": True,
99
+ "items": {
100
+ "title": "Acceleration",
101
+ "type": "object",
102
+ "properties": {
103
+ "type": {
104
+ "type": "string",
105
+ "enum": ["output","input"],
106
+ "default": "output"
107
+ },
108
+ "id": {"type": "integer", "description": "Number identifying signal channel"}
109
+ }
110
+ },
111
+ "default": [{"type": "output", "id": 1}]
112
+ }
113
+ }
114
+ }
115
+
116
+ def render(self):
117
+ try:
118
+ return make_mountains(self.asset, self.conf)
119
+ except:
120
+ return None
121
+
122
+ @classmethod
123
+ def create(cls, asset, request):
124
+ predictor = PredictorModel()
125
+ data = json.loads(request.body)
126
+ method = {
127
+ "Fourier Spectrum": "fourier",
128
+ "Response Spectrum": "response",
129
+ "FDD": "fdd",
130
+ "OKID": "okid-era",
131
+ "SRIM": "srim"
132
+ }[data.pop("method")]
133
+
134
+ predictor.entry_point = [
135
+ sys.executable, "-m", "mdof", method
136
+ ]
137
+ data["outputs"] = [i["id"] for i in data["channels"] if i["type"] == "output"]
138
+ data["inputs"] = [i["id"] for i in data["channels"] if i["type"] == "input"]
139
+ data["threads"] = 4
140
+ data["metrics"] = ["SPECTRAL_SHIFT_IDENTIFICATION"]
141
+ del data["channels"]
142
+
143
+ predictor.name = data.pop("name")
144
+ predictor.config = data
145
+ predictor.asset = asset
146
+ predictor.protocol = "IRIE_PREDICTOR_T2"
147
+ predictor.active = True
148
+ return predictor
149
+
150
+
151
+ def newPrediction(self, event):
152
+ self.event = event
153
+ return RunID(1)
154
+
155
+ def runPrediction(self, run_id: RunID) -> bool:
156
+ event_file = Path(self.event.event_file.path).resolve()
157
+ command = [*self.entry_point,
158
+ "--config",
159
+ json.dumps(self.conf),
160
+ event_file]
161
+
162
+ if False:
163
+ command = [*self.entry_point,
164
+ event_file,
165
+ *map(str, self.conf.get("argv", []))]
166
+ try:
167
+ self.metric_data = json.loads(
168
+ subprocess.check_output(command).decode()
169
+ )
170
+ return True
171
+ except Exception as e:
172
+ self.metric_data = {"error": str(e)}
173
+ return False
174
+
175
+ def getMetricData(self, run, metric):
176
+ if not hasattr(self, "metric_data"):
177
+ raise Exception(f"Error {self.name}({id(self)}), {run}")
178
+ return self.metric_data
26
179
 
27
180
 
28
181
 
@@ -186,12 +339,14 @@ def _load_events(asset, output_channels):
186
339
  return {k:v for k,v in events}
187
340
 
188
341
 
189
- def get_spectra(event, period_band, cmap):
342
+ def _get_spectra(event, conf, cmap):
190
343
  """
191
344
  Get coordinates (periods, amplitudes) of spectra for an event, and return them along
192
345
  with the maximum period of the N_PEAKS tallest peaks, as well as plotting options
193
346
  such as color and alpha.
194
347
  """
348
+ period_band = conf.period_band
349
+
195
350
  n_outputs = event['outputs'].shape[0]
196
351
  frequencies,_,S = transform.fdd(outputs=event['outputs'], step=event['dt']) # Full frequency spectrum
197
352
  periods = 1/frequencies
@@ -309,14 +464,18 @@ def _plot_mountains(spectra, accellim=None):
309
464
  return fig
310
465
 
311
466
 
312
- def make_mountains(asset, output_channels=None):
467
+ def make_mountains(asset, conf=None, output_channels=None):
313
468
 
314
469
  cmap = colormaps['plasma']
315
- conf = Config()
316
- conf.period_band = (0.1,3)
317
- conf.damping = 0.02
318
- conf.ss_decimation = 8
319
- conf.order = 40
470
+ if conf is None:
471
+ conf = Config()
472
+ conf.period_band = (0.1,3)
473
+ conf.damping = 0.02
474
+ conf.ss_decimation = 8
475
+ conf.order = 40
476
+ conf.method = "fdd"
477
+ else:
478
+ conf = Config(**conf)
320
479
 
321
480
  if output_channels is None:
322
481
  if not asset.bridge_sensors:
@@ -333,7 +492,7 @@ def make_mountains(asset, output_channels=None):
333
492
  print(f"Missing {n_expected_outputs-n_parsed_channels} output channels; skipping event") # Missing too many channels
334
493
  continue
335
494
 
336
- spectra[filename] = get_spectra(event, conf.period_band, cmap) # {'spec_coords':spec_coords, 'max_peak_period':max_peak_period, 'plot_conf':plot_conf}
495
+ spectra[filename] = _get_spectra(event, conf, cmap) # {'spec_coords':spec_coords, 'max_peak_period':max_peak_period, 'plot_conf':plot_conf}
337
496
 
338
497
 
339
498
  # if station == 'CE89494':
@@ -12,8 +12,7 @@ from .views import new_prediction, asset_predictors, predictor_profile, predicto
12
12
 
13
13
  urlpatterns = [
14
14
  re_path("^inventory/[0-9 A-Z-]*/predictors/new", new_prediction),
15
- re_path("^inventory/[0-9 A-Z-]*/predictors/[0-9]", predictor_profile),
16
- re_path("^inventory/(?P<calid>[0-9 A-Z-]*)/predictors/create/", predictor_upload),
17
- re_path("^inventory/[0-9 A-Z-]*/predictors/", asset_predictors, name="asset_predictors"),
18
- re_path("^inventory/[0-9 A-Z-]*/predictors/", asset_predictors),
15
+ re_path("^inventory/(?P<calid>[0-9 A-Z-]*)/predictors/(?P<preid>[0-9 A-Z-]{1,})", predictor_profile),
16
+ re_path("^inventory/(?P<calid>[0-9 A-Z-]*)/predictors/create", predictor_upload),
17
+ re_path("^inventory/[0-9 A-Z-]*/predictors/", asset_predictors, name="asset_predictors")
19
18
  ]
@@ -19,9 +19,6 @@ from django.core.exceptions import ObjectDoesNotExist
19
19
 
20
20
  from django.shortcuts import render
21
21
 
22
- from rest_framework.decorators import api_view, permission_classes
23
- from rest_framework.permissions import IsAuthenticated
24
-
25
22
  from irie.apps.site.view_utils import raise404
26
23
  from irie.apps.inventory.models import Asset
27
24
  from irie.apps.prediction.predictor import PREDICTOR_TYPES, OpenSeesRunner
@@ -38,41 +35,7 @@ def new_prediction(request):
38
35
  return HttpResponse(html_template.render(context, request))
39
36
 
40
37
 
41
- #@login_required(login_url="/login/")
42
- @api_view(["GET", "POST", "PUT"])
43
- @permission_classes([IsAuthenticated])
44
- def predictor_api(request):
45
-
46
- context = {"segment": "assets"}
47
-
48
- context["predictor_types"] = list(reversed([
49
- {"schema": json.dumps(cls.schema),
50
- "name": cls.__name__,
51
- "title": cls.schema["title"]}
52
- for cls in set(PREDICTOR_TYPES.values())
53
- ]))
54
-
55
- calid = request.path.split("/")[-3]
56
-
57
- try:
58
- context["asset"] = Asset.objects.get(calid=calid)
59
-
60
- except:
61
- return HttpResponse(
62
- loader.get_template("site/page-404-sidebar.html").render(context, request)
63
- )
64
-
65
- if request.method == "POST":
66
- print(request.POST)
67
- PREDICTOR_TYPES["IRIE_PREDICTOR_T2"].create(context["asset"],request).save()
68
-
69
- html_template = loader.get_template("prediction/asset-predictors.html")
70
- return HttpResponse(html_template.render(context, request))
71
-
72
-
73
- @api_view(["GET", "POST", "PUT"])
74
- # @login_required(login_url="/login/")
75
- @permission_classes([IsAuthenticated])
38
+ @login_required(login_url="/login/")
76
39
  def asset_predictors(request):
77
40
 
78
41
  calid = request.path.split("/")[-3]
@@ -97,45 +60,17 @@ def asset_predictors(request):
97
60
  loader.get_template("site/page-404-sidebar.html").render(context, request)
98
61
  )
99
62
 
100
- if request.method == "POST":
101
- form = PredictorForm(request.POST, request.FILES)
102
- if form.is_valid():
103
- # Process the form data and uploaded file
104
- # asset = form.cleaned_data['asset']
105
- asset = Asset.objects.get(calid=calid)
106
- uploaded_file = request.FILES['config_file']
107
-
108
- if uploaded_file:
109
- with open(uploaded_file.name, 'wb+') as destination:
110
- for chunk in uploaded_file.chunks():
111
- destination.write(chunk)
112
-
113
- # Save the predictor
114
- if request.POST.get("runner", None) == "IRIE_PREDICTOR_T2":
115
- PREDICTOR_TYPES["IRIE_PREDICTOR_T2"].create(context["asset"],request).save()
116
- else:
117
- OpenSeesRunner.create(asset,None,form).save()
118
- else:
119
- context["form"] = PredictorForm()
120
-
121
63
  html_template = loader.get_template("prediction/asset-predictors.html")
122
64
  return HttpResponse(html_template.render(context, request))
123
65
 
124
66
 
125
67
  @login_required(login_url="/login/")
126
- def predictor_profile(request):
68
+ def predictor_profile(request, calid, preid):
127
69
 
128
70
  context = {}
129
71
  html_template = loader.get_template("prediction/predictor-profile.html")
130
72
  context["segment"] = "inventory"
131
73
 
132
- url_segs = request.path.split("/")
133
- if len(url_segs) < 5:
134
- return raise404(request, context)
135
- else:
136
- calid = url_segs[2]
137
- preid = url_segs[4]
138
-
139
74
  try:
140
75
  asset = Asset.objects.get(calid=calid)
141
76
  except Asset.DoesNotExist:
@@ -149,7 +84,6 @@ def predictor_profile(request):
149
84
  context["asset"] = asset
150
85
  context["predictor"] = PREDICTOR_TYPES[predictor.protocol](predictor)
151
86
 
152
-
153
87
  try:
154
88
  return HttpResponse(html_template.render(context, request))
155
89
 
@@ -167,29 +101,19 @@ def predictor_profile(request):
167
101
  @login_required(login_url="/login/")
168
102
  def predictor_upload(request, calid):
169
103
 
170
- context = {}
171
104
  html_template = loader.get_template("prediction/predictor-upload.html")
172
- context["segment"] = "assets"
173
105
 
174
106
  if request.method == 'POST':
175
107
  form = PredictorForm(request.POST, request.FILES) # include request.FILES
176
- if form.is_valid():
177
- # Process the form data and uploaded file
178
- # asset = form.cleaned_data['asset']
179
- asset = Asset.objects.get(calid=calid)
180
- uploaded_file = request.FILES['config_file']
181
-
182
- with open(uploaded_file.name, 'wb+') as destination:
183
- for chunk in uploaded_file.chunks():
184
- destination.write(chunk)
108
+ # if form.is_valid():
109
+ asset = Asset.objects.get(calid=calid)
185
110
 
186
- # Save the predictor
187
- OpenSeesRunner.create(asset,None,form).save()
111
+ # Save the predictor
112
+ predictor = PREDICTOR_TYPES[request.POST.get("runner")].create(asset, request)
113
+ predictor.save()
188
114
 
189
- context = {}
115
+ return HttpResponse(json.dumps({"data": {"id": predictor.id}}))
190
116
 
191
- return render(request, 'prediction/predictor-upload.html',
192
- context)
193
117
  else:
194
118
  form = PredictorForm()
195
119
 
@@ -197,7 +121,6 @@ def predictor_upload(request, calid):
197
121
  try:
198
122
  return render(request, 'prediction/predictor-upload.html', {"form": form})
199
123
 
200
-
201
124
  except Exception as e:
202
125
  if "DEBUG" in os.environ and os.environ["DEBUG"]:
203
126
  raise e
irie/apps/sitemaps.py ADDED
@@ -0,0 +1,14 @@
1
+ from django.contrib.sitemaps import Sitemap
2
+ from django.urls import reverse
3
+
4
+ class IrieSitemap(Sitemap):
5
+ priority = 0.9
6
+ changefreq = 'weekly'
7
+
8
+ def items(self):
9
+ # Return the names of your static views
10
+ return ['home', 'about', 'dashboard', 'asset_table']
11
+
12
+ def location(self, item):
13
+ return reverse(item)
14
+
@@ -230,21 +230,6 @@
230
230
  </div>
231
231
  </div>
232
232
  </div>
233
-
234
- {% if mountains %}
235
- <div class="col-8 card bg-white-100 border-0 shadow mb-4">
236
- <div class="card-body">
237
- <h4>Spectrum</h4>
238
- <div class="crop">
239
- <img src="data:image/jpeg;base64,{{ mountains }}" alt="Spectral content">
240
- </div>
241
- </div>
242
- <div class="card-footer">
243
- Powered by <a href="https://chrystalchern.github.io/mdof"><em>mdof</em></a>
244
- </div>
245
- </div>
246
- {% endif %}
247
-
248
233
  </details>
249
234
  <hr>
250
235
  {% endif %}
@@ -93,6 +93,44 @@ table a[href^="https://"]::after
93
93
  placeholder="Search assets"
94
94
  >
95
95
  </div>
96
+
97
+ <!-- Latest Year -->
98
+ <div class="input-group mb-3">
99
+ <span class="input-group-text">
100
+ <svg class="icon icon-xs" x-description="Heroicon name: solid/search" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
101
+ <path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" clip-rule="evenodd"></path>
102
+ </svg>
103
+ </span>
104
+ <button type="button" class="btn input-group-text btn-outline dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
105
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-caret-down-fill" viewBox="0 0 16 16">
106
+ <path d="M7.247 11.14l-4.796-5.481C2.014 5.334 2.482 4.5 3.25 4.5h9.5c.768 0 1.236.835.799 1.159l-4.796 5.481a1 1 0 0 1-1.506 0z"/>
107
+ </svg>
108
+ <span class="visually-hidden">Toggle Dropdown</span>
109
+ </button>
110
+ <ul class="dropdown-menu">
111
+ <li><a class="dropdown-item" href="#">Include fields:</a></li>
112
+ <li><hr class="dropdown-divider"></li>
113
+ <li><div class="form-check form-switch">
114
+ <input class="form-check-input" type="checkbox" role="switch" id="flexSwitchCheckDefault">
115
+ <label class="form-check-label" for="flexSwitchCheckDefault">Retrofit</label>
116
+ </div></li>
117
+ <li><div class="form-check form-switch">
118
+ <input class="form-check-input" type="checkbox" role="switch" id="flexSwitchCheckChecked" checked>
119
+ <label class="form-check-label" for="flexSwitchCheckChecked">Built</label>
120
+ </div></li>
121
+ </ul>
122
+ <input
123
+ id="max_year"
124
+ name="max_year"
125
+ type="number"
126
+ max="2024"
127
+ step="1"
128
+ class="form-control"
129
+ value="{{ filter.form.max_year.value|default_if_none:'2024' }}"
130
+ placeholder="Latest Year Built"
131
+ >
132
+ </div>
133
+
96
134
 
97
135
  <!-- CESMD Not Null Checkbox -->
98
136
  <div class="col-12">
@@ -108,21 +146,6 @@ table a[href^="https://"]::after
108
146
  </label>
109
147
  </div>
110
148
  </div>
111
-
112
- {# Is Complete Checkbox #}
113
- <div class="col-12">
114
- <div class="form-check">
115
- <input
116
- type="checkbox"
117
- name="is_complete"
118
- id="is_complete"
119
- class="form-check-input"
120
- {% if filter.form.is_complete.value %}checked{% endif %}>
121
- <label for="is_complete" class="form-check-label">
122
- {{ filter.form.is_complete.label }}
123
- </label>
124
- </div>
125
- </div>
126
149
 
127
150
  {# Buttons #}
128
151
  <div class="col-12 d-flex justify-content-between">