irie 0.0.4__py3-none-any.whl → 0.0.6__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 (62) hide show
  1. irie/apps/config.py +0 -1
  2. irie/apps/evaluation/identification.py +1 -1
  3. irie/apps/evaluation/models.py +3 -3
  4. irie/apps/evaluation/views.py +3 -3
  5. irie/apps/events/admin.py +2 -2
  6. irie/apps/events/migrations/0002_rename_event_eventrecord.py +19 -0
  7. irie/apps/events/migrations/0003_hazardevent.py +21 -0
  8. irie/apps/events/models.py +55 -5
  9. irie/apps/events/views.py +48 -3
  10. irie/apps/events/views_events.py +6 -10
  11. irie/apps/inventory/filters.py +37 -0
  12. irie/apps/inventory/models.py +7 -0
  13. irie/apps/inventory/urls.py +1 -0
  14. irie/apps/inventory/views.py +134 -227
  15. irie/apps/prediction/forms.py +4 -8
  16. irie/apps/prediction/metrics.py +0 -2
  17. irie/apps/prediction/migrations/0002_alter_predictormodel_protocol.py +18 -0
  18. irie/apps/prediction/models.py +4 -4
  19. irie/apps/prediction/predictor.py +18 -12
  20. irie/apps/prediction/runners/__init__.py +3 -398
  21. irie/apps/prediction/runners/hazus.py +579 -0
  22. irie/apps/prediction/runners/opensees/__init__.py +395 -0
  23. irie/apps/prediction/runners/{utilities.py → opensees/utilities.py} +7 -7
  24. irie/apps/prediction/runners/ssid.py +414 -0
  25. irie/apps/prediction/urls.py +1 -1
  26. irie/apps/prediction/views.py +45 -22
  27. irie/apps/site/view_sdof.py +2 -2
  28. irie/apps/templates/admin/base_site.html +3 -1
  29. irie/apps/templates/css/admin-extra.css +7 -0
  30. irie/apps/templates/includes/sidebar.html +17 -14
  31. irie/apps/templates/inventory/asset-event-summary.html +3 -2
  32. irie/apps/templates/inventory/asset-profile.html +126 -38
  33. irie/apps/templates/inventory/asset-table.html +191 -135
  34. irie/apps/templates/inventory/dashboard.html +105 -27
  35. irie/apps/templates/inventory/preamble.tex +131 -0
  36. irie/apps/templates/inventory/report.tex +59 -0
  37. irie/apps/templates/networks/corridor_table.html +2 -2
  38. irie/apps/templates/networks/networks.html +164 -0
  39. irie/apps/templates/networks/networks.js +2 -2
  40. irie/apps/templates/prediction/asset-predictors.html +6 -6
  41. irie/apps/templates/prediction/form-submission.html +3 -3
  42. irie/apps/templates/prediction/hazus/event.html +33 -0
  43. irie/apps/templates/prediction/hazus/history.html +1 -0
  44. irie/apps/templates/prediction/hazus/history.js +44 -0
  45. irie/apps/templates/prediction/{new-predictor.html → new-runner.html} +12 -8
  46. irie/apps/templates/site/index.html +29 -47
  47. irie/core/urls.py +7 -2
  48. irie/init/__main__.py +2 -0
  49. irie/init/bridges.py +5 -3
  50. irie/init/management/commands/init_assets.py +24 -45
  51. irie/init/management/commands/init_corridors.py +3 -6
  52. irie/init/management/commands/init_predictors.py +23 -8
  53. irie/post/__main__.py +88 -0
  54. {irie-0.0.4.dist-info → irie-0.0.6.dist-info}/METADATA +5 -3
  55. {irie-0.0.4.dist-info → irie-0.0.6.dist-info}/RECORD +62 -48
  56. /irie/apps/prediction/runners/{metrics.py → opensees/metrics.py} +0 -0
  57. /irie/apps/prediction/runners/{xmlutils.py → opensees/xmlutils.py} +0 -0
  58. /irie/apps/prediction/runners/{zipped.py → opensees/zipped.py} +0 -0
  59. /irie/init/data/{04.tar → nbi/04.tar} +0 -0
  60. {irie-0.0.4.dist-info → irie-0.0.6.dist-info}/WHEEL +0 -0
  61. {irie-0.0.4.dist-info → irie-0.0.6.dist-info}/entry_points.txt +0 -0
  62. {irie-0.0.4.dist-info → irie-0.0.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,414 @@
1
+ from pathlib import Path
2
+ import json
3
+ import io
4
+ import base64
5
+ import numpy as np
6
+ # Ensure Agg backend is set for non-interactive plotting
7
+ import matplotlib
8
+ matplotlib.use('Agg')
9
+ from matplotlib import pyplot as plt, ticker, cm
10
+ import quakeio
11
+ from mdof import transform
12
+ from scipy.signal import find_peaks
13
+ from mdof.utilities import Config, extract_channels
14
+ from matplotlib import colormaps
15
+ # try:
16
+ # import scienceplots
17
+ # plt.style.use(["science"])
18
+ # except ImportError:
19
+ # pass
20
+
21
+ N_PEAKS = 5 # number of "significant" peaks per record
22
+ MISSING_CHANNEL_LIMIT = 3 # number of missing output channels allowed before skipping event
23
+ MAX_ACCEL = 3.0
24
+
25
+ from irie.apps.events.models import EventRecord
26
+
27
+
28
+
29
+ def ssid_stats(events, key):
30
+ """
31
+ mode_results is a list (station level) of lists (event level) of dictionaries (mode level).
32
+ mode_results = [
33
+ [
34
+ {
35
+ "period": ...,
36
+ "frequency": ...,
37
+ "damping": ...,
38
+ "emac": ...,
39
+ "mpc": ...,
40
+ },
41
+ ...
42
+ ],
43
+ ...
44
+ ]
45
+
46
+ [
47
+ # "Event"
48
+ {"S1": {"period": [0.1]},
49
+ "R1": {"period": [1.2]}}
50
+ ]
51
+ """
52
+ mode_results = [_find_ssid(event.id) for event in events]
53
+ import numpy as np
54
+
55
+ filtered_results = [
56
+ {
57
+ method: [
58
+ result for result in event_results[method]
59
+ if key in result and result.get("emac", 1.0) > 0.5 and result.get("mpc", 1.0) > 0.5
60
+ ] for method in event_results
61
+ } for event_results in mode_results
62
+ ]
63
+
64
+ from collections import defaultdict
65
+ values = defaultdict(list)
66
+ for event in filtered_results:
67
+ for method in event:
68
+ for result in event[method]:
69
+ values[method].append(result[key])
70
+
71
+ mean = {method: np.mean(values[method]) for method in values}
72
+ std = {method: np.std(values[method]) for method in values}
73
+
74
+ def _first(method_results):
75
+ if method_results and len(method_results) > 0:
76
+ results = np.array([result[key] for result in method_results])
77
+ try:
78
+ idx = np.argmax([result["amplitude"] for result in method_results])
79
+ return results[idx]
80
+ except KeyError:
81
+ return np.max(results)
82
+ else:
83
+ return {}
84
+
85
+ return [
86
+ {method: {
87
+ # "distance": (closest_item[key]-mean)/std),
88
+ "nearest_mean": event_results[method][np.argmin(np.abs(mean[method] \
89
+ - [result[key] for result in event_results[method]]))] \
90
+ if event_results[method] and len(event_results[method]) > 0 else {} ,
91
+ "maximum": _first(event_results[method])
92
+ }
93
+ for method in event_results
94
+ }
95
+ for event_results in filtered_results
96
+ ]
97
+
98
+
99
+ def _find_ssid(event_id=None, evaluation=None):
100
+ """
101
+ Given an event ID, finds the results of the first configured
102
+ system ID run. This generally looks like a list of dicts,
103
+ each with fields "frequency", "damping", etc.
104
+ """
105
+ from irie.apps.evaluation.models import Evaluation
106
+
107
+ if evaluation is None:
108
+ evaluation = Evaluation.objects.filter(event_id=int(event_id))
109
+
110
+ elif not isinstance(evaluation, list):
111
+ evaluation = [evaluation]
112
+
113
+
114
+ if len(evaluation) != 1:
115
+ return []
116
+
117
+ else:
118
+ evaluation_data = evaluation[0].evaluation_data
119
+
120
+ if "SPECTRAL_SHIFT_IDENTIFICATION" in evaluation_data:
121
+ return {
122
+ key: val.get("data", val.get("error", []))
123
+ for key,val in evaluation_data["SPECTRAL_SHIFT_IDENTIFICATION"]["summary"].items()
124
+ }
125
+
126
+ else:
127
+ return []
128
+
129
+
130
+ def ssid_event_plot(evaluation):
131
+ import numpy as np
132
+ from mdof.macro import FrequencyContent
133
+
134
+ plot = FrequencyContent(scale=True, period=True, xlabel="Period (s)", ylabel="Normalized Amplitude")
135
+
136
+ for name, mdata in evaluation["summary"].items():
137
+ periods = []
138
+ amplitudes = []
139
+ for i in mdata.get("data", []):
140
+ if "period" in i:
141
+ periods.append(i["period"])
142
+ if "amplitude" in i:
143
+ amplitudes.append(i["amplitude"])
144
+
145
+ if len(amplitudes) and (len(amplitudes) == len(periods)):
146
+ plot.add(np.array(periods), np.array(amplitudes), label=name)
147
+ else:
148
+ plot.add(np.array(periods), label=name)
149
+
150
+ fig = plot.get_figure()
151
+ return fig.to_json()
152
+
153
+
154
+ def _load_events(asset, output_channels):
155
+ """
156
+ Parse event file and restructure the relevant data
157
+ (filename, date, peak acceleration, acceleration response history, and timestep).
158
+
159
+ Return a dictionary:
160
+ {
161
+ filename:
162
+ {
163
+ 'date': date,
164
+ 'peak_accel': peak_accel,
165
+ 'outputs': outputs,
166
+ 'dt': dt
167
+ }
168
+ }
169
+ """
170
+ events = {}
171
+ for evt in EventRecord.objects.filter(asset=asset):
172
+
173
+ try:
174
+ event = quakeio.read(evt.event_file.path, format="csmip.zip", exclusions=["*filter*"])
175
+ outputs, dt = extract_channels(event, output_channels)
176
+ except Exception as e:
177
+ print(f"Error loading event: {e}")
178
+ continue
179
+
180
+ filename = event['file_name']
181
+ date = event['event_date']
182
+ peak_accel = np.abs(event['peak_accel'])/980.665
183
+ events[filename] = {'date': date, 'peak_accel': peak_accel, 'outputs': outputs, 'dt': dt}
184
+
185
+ events = sorted(events.items(), key=lambda k: np.abs(k[1]["peak_accel"]))
186
+ return {k:v for k,v in events}
187
+
188
+
189
+ def get_spectra(event, period_band, cmap):
190
+ """
191
+ Get coordinates (periods, amplitudes) of spectra for an event, and return them along
192
+ with the maximum period of the N_PEAKS tallest peaks, as well as plotting options
193
+ such as color and alpha.
194
+ """
195
+ n_outputs = event['outputs'].shape[0]
196
+ frequencies,_,S = transform.fdd(outputs=event['outputs'], step=event['dt']) # Full frequency spectrum
197
+ periods = 1/frequencies
198
+ period_mask = (periods>=period_band[0]) & (periods<=period_band[1])
199
+ periods = periods[period_mask]
200
+ n_periods = len(periods) # Number of periods, x axis of spectrum. Varies per record.
201
+ spec_coords = np.empty((n_outputs, 3, n_periods)) # Initialize empty array for coordinates.
202
+ xvalue = event['peak_accel']
203
+ for j in range(n_outputs):
204
+ amplitudes = S[j,:]
205
+ amplitudes = amplitudes/max(amplitudes)
206
+ amplitudes = amplitudes[period_mask]
207
+ spec_coords[j] = [[xvalue]*n_periods, periods, amplitudes] # All spectra from FDD. One spectrum for each n_outputs outputs
208
+ peak_indices, _ = find_peaks(amplitudes, prominence=max(amplitudes)*0.01)
209
+ peaks = sorted(peak_indices, key=lambda peak: amplitudes[peak], reverse=True)
210
+ max_peak_period = np.max(periods[peaks[:N_PEAKS]])
211
+ plot_conf = Config()
212
+ plot_conf.color = cmap(event['peak_accel']/MAX_ACCEL)
213
+ plot_conf.alpha = 1
214
+
215
+ return {
216
+ 'spec_coords': spec_coords,
217
+ 'max_peak_period': max_peak_period,
218
+ 'plot_conf': plot_conf
219
+ }
220
+
221
+
222
+ def _plot_3d_spectrum(ax, date, trace, num_spectra=1, label='date', line=True, plotter='matplotlib', **options):
223
+ color = options.get('color', None)
224
+ alpha = options.get('alpha', 0.5)
225
+ if label=='date':
226
+ label = date
227
+ if plotter == 'matplotlib':
228
+ for i in range(num_spectra):
229
+ if line:
230
+ ax.plot(trace[i,0,:],trace[i,1,:],trace[i,2,:], label=label,
231
+ linestyle='-', color=color, alpha=alpha)
232
+ else:
233
+ ax.scatter(trace[i,0,:],trace[i,1,:],trace[i,2,:], label=label,
234
+ marker=options.get('marker','o'), color=color, s=30, alpha=alpha)
235
+
236
+
237
+ def _linear_interpolate(x, y, target_x):
238
+ sorted_indices = sorted(range(len(x)), key = lambda i: x[i])
239
+ x = x[sorted_indices]
240
+ y = y[sorted_indices]
241
+ i1 = max(np.where(x<=target_x)[0])
242
+ i2 = min(np.where(x>=target_x)[0])
243
+ x1 = x[i1]
244
+ x2 = x[i2]
245
+ y1 = y[i1]
246
+ y2 = y[i2]
247
+ target_y = y1 + (target_x - x1)*(y2 - y1)/(x2 - x1)
248
+ return target_y
249
+
250
+
251
+ def plot_spectral_surface(ax, traces, **options):
252
+ alpha = options.get('alpha',0.5)
253
+ colors = options.get('colors',None)
254
+ cmap = options.get('cmap',None)
255
+ n_points_per_trace = 2000
256
+ period_lower_bound = np.max([trace[0,1,-1] for trace in traces])
257
+ period_upper_bound = np.min([trace[0,1,0] for trace in traces])
258
+ standard_periods = np.linspace(period_lower_bound+0.01, period_upper_bound-0.01, n_points_per_trace)
259
+ X = []
260
+ Y = []
261
+ Z = []
262
+ for trace in traces:
263
+ X.append(np.full(n_points_per_trace,trace[0,0,0]))
264
+ Y.append(standard_periods)
265
+ current_periods = trace[0,1,:]
266
+ current_amplitudes = trace[0,2,:]
267
+ z = []
268
+ for period in standard_periods:
269
+ z.append(_linear_interpolate(current_periods,current_amplitudes,period))
270
+ Z.append(z)
271
+ X = np.array(X)
272
+ Y = np.array(Y)
273
+ Z = np.array(Z)
274
+ if colors is None:
275
+ if cmap is None:
276
+ cmap = cm.plasma
277
+ ax.plot_surface(X,Y,Z, cmap=cmap, alpha=alpha)
278
+ else:
279
+ ax.plot_surface(X,Y,X, facecolors=colors)
280
+
281
+
282
+ def _plot_mountains(spectra, accellim=None):
283
+ fig = plt.figure(figsize=(13,13))
284
+ ax = fig.add_subplot(projection='3d')
285
+
286
+ max_period = 1.1*np.max([event['max_peak_period'] for event in spectra.values()])
287
+
288
+ for event in spectra:
289
+ periods = spectra[event]['spec_coords'][0,1,:]
290
+ spec_coords = spectra[event]['spec_coords'][:,:,(periods<=max_period)]
291
+ _plot_3d_spectrum(ax=ax, date="", # TODO events[event]['date'],
292
+ trace=spec_coords,
293
+ label=None, **(spectra[event]['plot_conf']))
294
+
295
+
296
+ ax.tick_params(axis='x', which='major', pad=15)
297
+ ax.tick_params(axis='y', which='major', pad=15)
298
+ for tick in ax.xaxis.get_majorticklabels():
299
+ tick.set_verticalalignment("bottom")
300
+ ax.xaxis.set_minor_locator(ticker.NullLocator())
301
+ if accellim is not None:
302
+ ax.set_xlim((0,accellim))
303
+ ax.view_init(elev=30, azim=-20, roll=2)
304
+ ax.set_box_aspect((5,5,1), zoom=0.75)
305
+ ax.set_xlabel("Peak Station Acceleration (g)", labelpad=30)
306
+ ax.set_ylabel("Period (s)", labelpad=40)
307
+ ax.set_zlabel("Normalized spectral amplitude", labelpad=10)
308
+ # ax.legend(frameon=True, framealpha=0.9, bbox_to_anchor=(0.5,0,0.5,0.7), loc='upper left')
309
+ return fig
310
+
311
+
312
+ def make_mountains(asset, output_channels=None):
313
+
314
+ 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
320
+
321
+ if output_channels is None:
322
+ if not asset.bridge_sensors:
323
+ raise ValueError("Failed to determine output sensors for Asset")
324
+ output_channels = json.loads(asset.bridge_sensors[2:-1])
325
+
326
+ n_expected_outputs = len(output_channels)
327
+ events = _load_events(asset, output_channels)
328
+
329
+ spectra = {} # dictionary to store the spectra at each event
330
+ for filename,event in events.items():
331
+ n_parsed_channels = event['outputs'].shape[0]
332
+ if n_parsed_channels < n_expected_outputs-MISSING_CHANNEL_LIMIT:
333
+ print(f"Missing {n_expected_outputs-n_parsed_channels} output channels; skipping event") # Missing too many channels
334
+ continue
335
+
336
+ spectra[filename] = get_spectra(event, conf.period_band, cmap) # {'spec_coords':spec_coords, 'max_peak_period':max_peak_period, 'plot_conf':plot_conf}
337
+
338
+
339
+ # if station == 'CE89494':
340
+ # max_period = 2.5
341
+ # if station == 'CE89973':
342
+ # accellim = 2.5
343
+ if len(spectra) == 0:
344
+ return None
345
+
346
+ fig = _plot_mountains(spectra)
347
+
348
+ # Save the plot to a BytesIO buffer
349
+ buffer = io.BytesIO()
350
+ fig.savefig(buffer, format="png")
351
+ buffer.seek(0)
352
+ # plt.show()
353
+
354
+ # Encode the image as Base64
355
+ return base64.b64encode(buffer.read()).decode("utf-8")
356
+
357
+ def parse_args(args):
358
+ procedure = {
359
+ 'STATION_TYPE': None,
360
+ 'station': None,
361
+ 'indir': None,
362
+ 'outdir': "./out",
363
+ 'accellim': None
364
+ }
365
+ argi = iter(args[1:])
366
+ for arg in argi:
367
+ if arg in ['--help', '-h']:
368
+ print(HELP)
369
+ sys.exit()
370
+ elif arg == '--station':
371
+ procedure['station'] = next(argi)
372
+ elif arg == '--indir':
373
+ procedure['indir'] = next(argi)
374
+ elif arg == '--outdir':
375
+ procedure['outdir'] = next(argi)
376
+ elif arg == '--accellim':
377
+ procedure['accellim'] = float(next(argi))
378
+ else:
379
+ procedure['STATION_TYPE'] = arg
380
+ if procedure['STATION_TYPE'] is None:
381
+ print("Station type ('bridges' or 'buildings') is a required argument.")
382
+ sys.exit()
383
+ return procedure
384
+
385
+
386
+ if __name__ == '__main__':
387
+
388
+ import sys
389
+ procedure = parse_args(sys.argv)
390
+ STATION_TYPE = procedure['STATION_TYPE']
391
+ outdir = Path(procedure['outdir'])
392
+ if not outdir.exists():
393
+ outdir.mkdir()
394
+
395
+ conf = Config()
396
+ conf.period_band = (0.1,3) if STATION_TYPE=="bridges" else (0.1,8)
397
+ if procedure['accellim'] is None:
398
+ accellim = 1.5 if STATION_TYPE=="bridges" else 1.2
399
+ else:
400
+ accellim = procedure['accellim']
401
+ conf.damping = 0.02
402
+ conf.ss_decimation = 8
403
+ conf.order = 40
404
+ # cmap = colormaps['seismic']
405
+ cmap = colormaps['plasma']
406
+ path_to_channels = f"../channels_{STATION_TYPE}/channels_summary.json"
407
+ with open(path_to_channels, 'r') as readfile:
408
+ CHANNELS = json.load(readfile)
409
+
410
+ if procedure['station'] is not None:
411
+ CHANNELS = {k:v for k,v in CHANNELS.items() if k==procedure['station']}
412
+
413
+ for station in CHANNELS:
414
+ make_mountains(station)
@@ -13,7 +13,7 @@ from .views import new_prediction, asset_predictors, predictor_profile, predicto
13
13
  urlpatterns = [
14
14
  re_path("^inventory/[0-9 A-Z-]*/predictors/new", new_prediction),
15
15
  re_path("^inventory/[0-9 A-Z-]*/predictors/[0-9]", predictor_profile),
16
- re_path("upload/", predictor_upload),
16
+ re_path("^inventory/(?P<calid>[0-9 A-Z-]*)/predictors/create/", predictor_upload),
17
17
  re_path("^inventory/[0-9 A-Z-]*/predictors/", asset_predictors, name="asset_predictors"),
18
18
  re_path("^inventory/[0-9 A-Z-]*/predictors/", asset_predictors),
19
19
  ]
@@ -32,7 +32,7 @@ from .forms import PredictorForm
32
32
  def new_prediction(request):
33
33
  context = {}
34
34
 
35
- page_template = "form-submission.html" #request.path.split("/")[-1]
35
+ page_template = "form-submission.html"
36
36
  context["segment"] = page_template
37
37
  html_template = loader.get_template("prediction/" + page_template)
38
38
  return HttpResponse(html_template.render(context, request))
@@ -59,40 +59,64 @@ def predictor_api(request):
59
59
 
60
60
  except:
61
61
  return HttpResponse(
62
- loader.get_template("home/page-404-sidebar.html").render(context, request)
62
+ loader.get_template("site/page-404-sidebar.html").render(context, request)
63
63
  )
64
64
 
65
65
  if request.method == "POST":
66
- PREDICTOR_TYPES["BRACE2_CLI_PREDICTOR_T2"].create(context["asset"],request).save()
66
+ print(request.POST)
67
+ PREDICTOR_TYPES["IRIE_PREDICTOR_T2"].create(context["asset"],request).save()
67
68
 
68
69
  html_template = loader.get_template("prediction/asset-predictors.html")
69
70
  return HttpResponse(html_template.render(context, request))
70
71
 
71
72
 
72
- # @api_view(["GET", "POST", "PUT"])
73
- @login_required(login_url="/login/")
74
- # @permission_classes([IsAuthenticated])
73
+ @api_view(["GET", "POST", "PUT"])
74
+ # @login_required(login_url="/login/")
75
+ @permission_classes([IsAuthenticated])
75
76
  def asset_predictors(request):
76
77
 
77
- context = {"segment": "assets"}
78
+ calid = request.path.split("/")[-3]
78
79
 
79
- context["predictor_types"] = list(reversed([
80
- {"schema": json.dumps(cls.schema),
80
+ context = {"segment": "inventory"}
81
+
82
+ context["runners"] = list(reversed([
83
+ {
84
+ "schema": json.dumps(cls.schema),
81
85
  "name": cls.__name__,
82
- "title": cls.schema.get("title", "NO TITLE")}
83
- for cls in set(PREDICTOR_TYPES.values())
86
+ "title": cls.schema.get("title", "NO TITLE"),
87
+ "protocol": key
88
+ }
89
+ for key,cls in PREDICTOR_TYPES.items() if key
84
90
  ]))
85
91
 
86
- calid = request.path.split("/")[-3]
92
+
87
93
  try:
88
94
  context["asset"] = Asset.objects.get(calid=calid)
89
- except:
95
+ except Asset.DoesNotExist:
90
96
  return HttpResponse(
91
- loader.get_template("home/page-404-sidebar.html").render(context, request)
97
+ loader.get_template("site/page-404-sidebar.html").render(context, request)
92
98
  )
93
99
 
94
100
  if request.method == "POST":
95
- PREDICTOR_TYPES["BRACE2_CLI_PREDICTOR_T2"].create(context["asset"],request).save()
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()
96
120
 
97
121
  html_template = loader.get_template("prediction/asset-predictors.html")
98
122
  return HttpResponse(html_template.render(context, request))
@@ -103,7 +127,7 @@ def predictor_profile(request):
103
127
 
104
128
  context = {}
105
129
  html_template = loader.get_template("prediction/predictor-profile.html")
106
- context["segment"] = "assets"
130
+ context["segment"] = "inventory"
107
131
 
108
132
  url_segs = request.path.split("/")
109
133
  if len(url_segs) < 5:
@@ -114,7 +138,7 @@ def predictor_profile(request):
114
138
 
115
139
  try:
116
140
  asset = Asset.objects.get(calid=calid)
117
- except ObjectDoesNotExist:
141
+ except Asset.DoesNotExist:
118
142
  return raise404(request, context)
119
143
 
120
144
  try:
@@ -141,19 +165,18 @@ def predictor_profile(request):
141
165
 
142
166
 
143
167
  @login_required(login_url="/login/")
144
- def predictor_upload(request):
168
+ def predictor_upload(request, calid):
145
169
 
146
170
  context = {}
147
171
  html_template = loader.get_template("prediction/predictor-upload.html")
148
172
  context["segment"] = "assets"
149
173
 
150
- context["title"] = "The Title"
151
-
152
174
  if request.method == 'POST':
153
175
  form = PredictorForm(request.POST, request.FILES) # include request.FILES
154
176
  if form.is_valid():
155
177
  # Process the form data and uploaded file
156
- asset = form.cleaned_data['asset']
178
+ # asset = form.cleaned_data['asset']
179
+ asset = Asset.objects.get(calid=calid)
157
180
  uploaded_file = request.FILES['config_file']
158
181
 
159
182
  with open(uploaded_file.name, 'wb+') as destination:
@@ -172,7 +195,7 @@ def predictor_upload(request):
172
195
 
173
196
 
174
197
  try:
175
- return render(request, 'prediction/predictor-upload.html', {'form': form})
198
+ return render(request, 'prediction/predictor-upload.html', {"form": form})
176
199
 
177
200
 
178
201
  except Exception as e:
@@ -9,7 +9,7 @@ from django.urls import reverse
9
9
 
10
10
  import quakeio
11
11
 
12
- from irie.apps.events.models import Event
12
+ from irie.apps.events.models import EventRecord
13
13
 
14
14
 
15
15
  @login_required(login_url="/login/")
@@ -21,7 +21,7 @@ def earthquake_fitter(request):
21
21
 
22
22
  context = {}
23
23
 
24
- context["event"] = Event.objects.filter(pk=pk)[0]
24
+ context["event"] = EventRecord.objects.filter(pk=pk)[0]
25
25
 
26
26
  try:
27
27
  context["event_data"] = quakeio.read(
@@ -1,9 +1,11 @@
1
1
  {% extends "admin/base.html" %}
2
+ {% load static from static %}
2
3
 
3
4
  {% block title %}{% if subtitle %}{{ subtitle }} | {% endif %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}
5
+ {% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "css/admin-extra.css" %}" />{% endblock %}
4
6
 
5
7
  {% block branding %}
6
- <div id="site-name"><a href="{% url 'admin:index' %}">{{ site_header|default:_('Django administration') }}</a></div>
8
+ <div id="site-name"><a style="font-family: Consolas, monospace;" href="{% url 'admin:index' %}">{{ site_header|default:_('Django administration') }}</a></div>
7
9
  {% if user.is_anonymous %}
8
10
  {% include "admin/color_theme_toggle.html" %}
9
11
  {% endif %}
@@ -0,0 +1,7 @@
1
+ body {
2
+ margin: 0;
3
+ padding: 0;
4
+ font-size: 40px;
5
+ color: #333;
6
+ background: #fff;
7
+ }
@@ -1,5 +1,5 @@
1
1
 
2
- <nav class="navbar navbar-dark navbar-theme-primary px-4 col-12 d-lg-none">
2
+ <nav class="navbar navbar-theme-primary navbar-dark px-4 col-12 d-lg-none">
3
3
  <a class="navbar-brand me-lg-5" href="/">
4
4
  <img class="navbar-brand-light" src="{{ ASSETS_ROOT }}/img/brace2-no_text.png" alt="BRACE2 logo" />
5
5
  </a>
@@ -18,21 +18,21 @@
18
18
  <nav id="sidebarMenu" class="sidebar d-lg-block collapse bg-gray-200" data-simplebar>
19
19
  <div class="sidebar-inner px-4 pt-3">
20
20
  <div class="user-card d-flex d-md-none align-items-center justify-content-between justify-content-md-center pb-4">
21
- {% comment %}
22
21
  <div class="d-flex align-items-center">
22
+ {% comment %}
23
23
  <div class="avatar-lg me-4">
24
24
  <img src="{{ ASSETS_ROOT }}/img/team/profile-picture-3.jpg" class="card-img-top rounded-circle border-white"
25
- alt="Bonnie Green">
25
+ alt="Profile picture">
26
26
  </div>
27
+ {% endcomment %}
27
28
  <div class="d-block">
28
- <h2 class="h5 mb-3">Hi, Jane</h2>
29
+ <h2 class="h5 mb-3">Hi</h2>
29
30
  <a href="/page-sign-in.html" class="btn btn-secondary btn-sm d-inline-flex align-items-center">
30
31
  <svg class="icon icon-xxs me-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"></path></svg>
31
- Sign Out
32
+ Log Out
32
33
  </a>
33
34
  </div>
34
35
  </div>
35
- {% endcomment %}
36
36
  <div class="collapse-close">
37
37
  <a href="#sidebarMenu" data-bs-toggle="collapse"
38
38
  data-bs-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="true"
@@ -61,14 +61,7 @@
61
61
  <span class="sidebar-text">Dashboard</span>
62
62
  </a>
63
63
  </li>
64
- <li class="nav-item {% if 'networks' in segment %} active {% endif %}">
65
- <a href="{% url 'networks' %}" class="nav-link">
66
- <span class="sidebar-icon">
67
- <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>
68
- </span>
69
- <span class="sidebar-text">Networks</span>
70
- </a>
71
- </li>
64
+
72
65
  <li class="nav-item {% if 'events' in segment %} active {% endif %}">
73
66
  <span
74
67
  class="nav-link collapsed d-flex justify-content-between align-items-center"
@@ -95,6 +88,7 @@
95
88
  </ul>
96
89
  </div>
97
90
  </li>
91
+
98
92
  <li class="nav-item {% if 'assets' in segment %} active {% endif %}">
99
93
  <a href="{% url 'asset_table' %}" class="nav-link">
100
94
  <span class="sidebar-icon">
@@ -105,6 +99,15 @@
105
99
  </a>
106
100
  </li>
107
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>
109
+ </li>
110
+
108
111
  <li role="separator" class="dropdown-divider mt-4 mb-3 border-gray-700"></li>
109
112
 
110
113