irie 0.0.41__py3-none-any.whl → 0.0.43__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 (56) hide show
  1. irie/apps/inventory/archive/CESMD.py +0 -0
  2. irie/apps/inventory/filters.py +1 -1
  3. irie/apps/inventory/models.py +27 -0
  4. irie/apps/inventory/services/render.py +0 -0
  5. irie/apps/inventory/urls.py +3 -0
  6. irie/apps/inventory/views.py +57 -0
  7. irie/apps/prediction/forms.py +2 -1
  8. irie/apps/prediction/models.py +24 -0
  9. irie/apps/prediction/runners/hazus.py +33 -19
  10. irie/apps/prediction/urls.py +5 -3
  11. irie/apps/prediction/views.py +101 -8
  12. irie/apps/static/assets/css/brace.css +0 -33
  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 +2 -1
  16. irie/apps/templates/includes/scripts.html +10 -1
  17. irie/apps/templates/includes/sidebar.html +19 -11
  18. irie/apps/templates/inventory/asset-on-map.html +457 -0
  19. irie/apps/templates/inventory/asset-profile.html +1 -2
  20. irie/apps/templates/inventory/map-inventory.html +136 -0
  21. irie/apps/templates/inventory/map-inventory2.html +143 -0
  22. irie/apps/templates/inventory/map-single-asset.html +0 -0
  23. irie/apps/templates/inventory/map-single-asset2.html +618 -0
  24. irie/apps/templates/inventory/map-terrain.html +214 -0
  25. irie/apps/templates/inventory/sensor-upload.html +1 -0
  26. irie/apps/templates/inventory/three-maps.html +229 -0
  27. irie/apps/templates/layouts/base.html +2 -1
  28. irie/apps/templates/prediction/predictor-upload.html +68 -22
  29. irie/apps/templates/site/index.html +36 -27
  30. irie/apps/templates/site/page-400-sidebar.html +31 -0
  31. irie/apps/templates/site/page-400.html +29 -0
  32. irie/apps/templates/site/page-404-sidebar.html +1 -1
  33. irie/apps/templates/site/page-404.html +1 -1
  34. irie/fhwa/__init__.py +132 -0
  35. irie/fhwa/__main__.py +79 -0
  36. irie/fhwa/fields/nbi001.py +61 -0
  37. irie/fhwa/fields/nbi001b.py +1 -0
  38. irie/fhwa/fields/nbi002.py +0 -0
  39. irie/fhwa/fields.py +32 -0
  40. irie/init/__main__.py +0 -4
  41. irie/init/calid.py +86 -3
  42. irie/init/getNBIData.py +1 -1
  43. irie/init/getNBIData2.py +304 -0
  44. irie/init/management/commands/init_assets.py +11 -11
  45. irie/init/management/commands/init_predictors.py +1 -1
  46. irie/init/management/commands/make_asset.py +0 -0
  47. irie/pull/nbi.py +304 -0
  48. {irie-0.0.41.dist-info → irie-0.0.43.dist-info}/METADATA +1 -1
  49. {irie-0.0.41.dist-info → irie-0.0.43.dist-info}/RECORD +53 -36
  50. {irie-0.0.41.dist-info → irie-0.0.43.dist-info}/WHEEL +1 -1
  51. irie/apps/inventory/CESMD.py +0 -81
  52. irie/apps/inventory/archive/arcGIS.py +0 -1175
  53. irie/apps/inventory/traffic.py +0 -175052
  54. /irie/apps/inventory/{calid.py → archive/calid.py} +0 -0
  55. {irie-0.0.41.dist-info → irie-0.0.43.dist-info}/entry_points.txt +0 -0
  56. {irie-0.0.41.dist-info → irie-0.0.43.dist-info}/top_level.txt +0 -0
File without changes
@@ -37,7 +37,7 @@ class AssetFilter(django_filters.FilterSet):
37
37
  def filter_year(self, queryset, name, value):
38
38
  ids = {
39
39
  asset.id for asset in queryset if (
40
- asset.nbi_data and int(asset.nbi_data["NBI_BRIDGE"]["Year Built"] or 0) <= int(value) #.year
40
+ asset.nbi_data and int(asset.nbi_data["NBI_BRIDGE"].get("Year Built",0) or 0) <= int(value) #.year
41
41
  )
42
42
  }
43
43
  return queryset.filter(id__in=ids)
@@ -142,3 +142,30 @@ class Sensor(models.Model):
142
142
 
143
143
  def __str__(self):
144
144
  return f"{self.name} ({self.group.name})"
145
+
146
+
147
+
148
+ # class Rendering:
149
+ # def __init__(self, url=None, units, datum):
150
+ # self._url = url
151
+
152
+
153
+ # def url(self):
154
+ # return self._url
155
+
156
+ # def url_or_data(self):
157
+ # pass
158
+
159
+ # def scale_meter(self):
160
+ # pass
161
+
162
+ # class PersistentRendering(models.Model):
163
+ # asset = models.ForeignKey(Asset, on_delete=models.CASCADE)
164
+ # datum = models.ForeignKey(Datum, on_delete=models.CASCADE)
165
+ # units = models.CharField(max_length=3)
166
+
167
+ # class TemporaryRendering(Rendering):
168
+ # def __init__(self, data, units, datum, asset):
169
+ # self._data = data
170
+ # self._units = units
171
+ # self._datum = datum
File without changes
@@ -27,4 +27,7 @@ urlpatterns = [
27
27
  re_path("^inventory/(?P<calid>[0-9 A-Z-]*)/$", views.asset_profile, name="asset_profile"),
28
28
  re_path("^inventory/(?P<calid>[0-9 A-Z-]*)/sensors/$", views.asset_sensors, name="asset_sensors"),
29
29
  re_path("^inventory/(?P<calid>[0-9 A-Z-]*)/sensor_upload", views.sensor_upload, name="sensor_upload"),
30
+
31
+ path("inventory/map2/", views.map_inventory),
32
+ path("california.json", views.california_json),
30
33
  ]
@@ -221,6 +221,62 @@ def asset_evals(request, calid):
221
221
  return HttpResponse(html_template.render(context, request))
222
222
 
223
223
 
224
+ def california_json(request):
225
+ with open("ca.json") as f:
226
+ return HttpResponse(f.read(), content_type="application/json")
227
+ with open("us_states.json") as f:
228
+ cal = [s for s in json.load(f)["features"] if s["id"] == "CA"][0]
229
+ return HttpResponse(json.dumps({"type": "FeatureCollection", "features": [cal]}), content_type="application/json")
230
+
231
+ def map_inventory(request):
232
+
233
+ import json
234
+ from irie.apps.inventory.models import Asset
235
+
236
+ consumed = {
237
+ "Partial": set(),
238
+ "Complete": set(),
239
+ "Instrumented": set()
240
+ }
241
+
242
+ data = {
243
+ "Partial": [],
244
+ "Complete": [],
245
+ "Instrumented": []
246
+ }
247
+
248
+ for asset in Asset.objects.all():
249
+ if asset.is_complete:
250
+ consumed["Complete"].add(asset.calid)
251
+ lat, lon = asset.coordinates
252
+ data["Complete"].append({
253
+ "lat": lat, "lon": lon
254
+ })
255
+
256
+ from irie.init.calid import CESMD, CESMD_LONG_LAT
257
+ for calid in CESMD:
258
+ if calid not in consumed["Complete"]:
259
+ consumed["Instrumented"].add(asset.calid)
260
+ try:
261
+ lat, lon = CESMD_LONG_LAT[CESMD[calid][0][2:]]
262
+ except:
263
+ continue
264
+ data["Instrumented"].append({
265
+ # "lat": lat, "lon": -lon
266
+ "lat": lat, "lon": lon
267
+ })
268
+
269
+ for asset in Asset.objects.all():
270
+ if asset.calid not in consumed["Instrumented"] and asset.calid not in consumed["Complete"]:
271
+ lat, lon = asset.coordinates
272
+ data["Partial"].append({
273
+ "lat": lat, "lon": lon
274
+ })
275
+
276
+
277
+ html_template = loader.get_template("inventory/map-inventory.html")
278
+ return HttpResponse(html_template.render({"data": json.dumps(data)}, request))
279
+
224
280
  # @login_required(login_url="/login/")
225
281
  def asset_profile(request, calid):
226
282
 
@@ -548,6 +604,7 @@ class AssetMap:
548
604
  weight=1,
549
605
  z_index_offset=10 if b.is_complete else 100000
550
606
  )
607
+
551
608
  if b.is_complete:
552
609
  marker = folium.Marker(
553
610
  location=[lat, lon],
@@ -14,7 +14,8 @@ class PredictorForm(forms.ModelForm):
14
14
  "render_file",
15
15
  "asset",
16
16
  "metrics",
17
- "active",
17
+ "active",
18
+ "description",
18
19
  "entry_point",
19
20
  "config",
20
21
  "protocol"
@@ -35,3 +35,27 @@ class PredictorModel(models.Model):
35
35
  def __str__(self):
36
36
  return f"{self.asset.calid} - {self.name} : {self.description}"
37
37
 
38
+
39
+ # class PhysicsPredictor(models.Model):
40
+ # class Units(models.TextChoices):
41
+ # iks = "IKS"
42
+ # ips = "IPS"
43
+ # fps = "FPS"
44
+ # mks = "MKS"
45
+ # cgs = "CGS"
46
+
47
+ # id = models.BigAutoField(primary_key=True)
48
+ # name = models.CharField(max_length=35)
49
+ # active = models.BooleanField()
50
+ # asset = models.ForeignKey(Asset, on_delete=models.CASCADE)
51
+ # description = models.TextField(default="")
52
+
53
+ # config_file = models.FileField(upload_to="predictor_configs/", null=True, blank=True)
54
+ # render_file = models.FileField(upload_to="renderings/", null=True, blank=True)
55
+ # metrics = models.JSONField(default=list)
56
+
57
+ # units = models.CharField(max_length=3, choices=Units.choices)
58
+
59
+
60
+ # def __str__(self):
61
+ # return f"{self.name} : {self.asset.calid}"
@@ -31,7 +31,6 @@ REFERENCES
31
31
  import math
32
32
  import json
33
33
  from scipy.stats import norm
34
- from irie.apps.events.models import EventRecord
35
34
  from irie.apps.prediction.runners import Runner, RunID
36
35
  from irie.apps.prediction.models import PredictorModel
37
36
 
@@ -181,30 +180,30 @@ def _bridge_info(nbi: dict) -> dict:
181
180
  """
182
181
  try:
183
182
  nbi_bridge = nbi.get("NBI_BRIDGE", {})
184
- nbi_superstructure = nbi.get("NBI_SUPERSTRUCTURE_DECK", {})
183
+ nbi_supers = nbi.get("NBI_SUPERSTRUCTURE_DECK", {})
185
184
 
186
- # Parse "Skew Angle"
187
- skew_angle_str = nbi_bridge.get("Skew Angle", "0")
188
- if "99" in skew_angle_str: # Handle undefined skew
185
+ # 34: "Skew Angle"
186
+ skew_angle_str = nbi_bridge.get("Skew Angle", "99")
187
+ if "99" in skew_angle_str:
189
188
  skew_angle = 0
190
189
  else:
191
190
  skew_angle = float(skew_angle_str.split(" - ")[0])
192
191
 
193
192
  return {
194
193
  "state_code": StateCodes.California,
195
- "year_built": int(nbi_bridge.get("Year Built", 0)),
196
- "skew_angle": skew_angle,
194
+ "year_built": int(nbi_bridge.get("Year Built", 0)), # 27
195
+ "skew_angle": skew_angle, # 34
197
196
  "service_type": int(
198
- nbi_bridge.get("Type of Service on Bridge Code", "0 - Unknown").split(" - ")[0]
199
- + nbi_bridge.get("Type Of Service Under Bridge Code", "0 - Unknown").split(" - ")[0]
197
+ nbi_bridge.get("Type of Service on Bridge Code", "0 - Unknown").split(" - ")[0] # 42A
198
+ + nbi_bridge.get("Type Of Service Under Bridge Code", "0 - Unknown").split(" - ")[0] # 42B
200
199
  ),
201
- "material_flag": int(nbi_superstructure.get("Main Span Material", "0 - Unknown").split(" - ")[0]),
202
- "geometry_flag": int(nbi_superstructure.get("Main Span Design", "0 - Unknown").split(" - ")[0]),
203
- "span_count": int(nbi_superstructure.get("Number of Spans in Main Unit", 0)),
204
- "approach_spans": int(nbi_superstructure.get("Number of Approach Spans", 0)),
205
- "max_span_length": float(nbi_bridge.get("Length of Maximum Span", 0.0)),
206
- "total_length": float(nbi_bridge.get("Structure Length", 0.0)),
207
- "deck_width": float(nbi_bridge.get("Deck Width - Out to Out", 0.0)),
200
+ "material_flag": int(nbi_supers.get("Main Span Material", "0 - Unknown").split(" - ")[0]), # 43A
201
+ "geometry_flag": int(nbi_supers.get("Main Span Design", "0 - Unknown").split(" - ")[0]), # 43B
202
+ "span_count": int(nbi_supers.get("Number of Spans in Main Unit", 0)), # 45
203
+ "approach_spans": int(nbi_supers.get("Number of Approach Spans", 0)), # 46
204
+ "max_span_length": float(nbi_bridge.get("Length of Maximum Span", 0.0)), # 48
205
+ "total_length": float(nbi_bridge.get("Structure Length", 0.0)), # 49
206
+ "deck_width": float(nbi_bridge.get("Deck Width - Out to Out", 0.0)), # 52
208
207
  }
209
208
  except ValueError as e:
210
209
  raise ValueError(f"Error processing NBI data: {e}")
@@ -214,12 +213,20 @@ def _bridge_info(nbi: dict) -> dict:
214
213
 
215
214
  def _hazus_curve(type: int, properties: dict, sa_03s: float, sa_10s: float, soil_type: str) -> dict:
216
215
  """
217
- Compute fragility probabilities for the four damage states and optionally generate fragility curves.
216
+ Compute fragility probabilities for the four damage states
218
217
 
219
218
  See page 7-14 of [1]
220
- Args:
219
+ See reference [3] for details
220
+ See Section 7.1.6.2 of [1] for example
221
+
222
+ Parameters
221
223
  - type (int): Bridge classification (integer from 1 to 28).
222
- - properties (dict): Dictionary containing bridge-specific properties.
224
+ - properties (dict): Dictionary containing NBI data:
225
+ - span_count (N),
226
+ - skew_angle (α),
227
+ - deck_width (W),
228
+ - max_span_length (Lmax), # 48
229
+ - total_length (L), # 49
223
230
  - pga (float): Peak Ground Acceleration (g).
224
231
  - sa_03s (float): Spectral Acceleration at 0.3 seconds (g).
225
232
  - sa_10s (float): Spectral Acceleration at 1.0 seconds (g).
@@ -227,6 +234,10 @@ def _hazus_curve(type: int, properties: dict, sa_03s: float, sa_10s: float, soil
227
234
 
228
235
  Returns:
229
236
  - dict: Fragility probabilities for Slight, Moderate, Extensive, and Complete damage states.
237
+
238
+ Note
239
+ The skew angle is defined as the angle between the centerline of a
240
+ pier and a line normal to the roadway centerline.
230
241
  """
231
242
  # Validate inputs
232
243
  required_keys = {"span_count", "skew_angle"}
@@ -242,6 +253,9 @@ def _hazus_curve(type: int, properties: dict, sa_03s: float, sa_10s: float, soil
242
253
  skew_angle = properties["skew_angle"]
243
254
 
244
255
  # Step 2: Get soil-amplified shaking parameters
256
+ # Evaluate the soil-amplified shaking at the bridge site. That is, get the peak ground acceleration
257
+ # (PGA), spectral accelerations (Sa at 0.3 seconds and Sa at 1.0 second) and the permanent ground
258
+ # deformation (in inches).
245
259
  modified_values = modify_ground_motion(soil_type, None, sa_03s, sa_10s)
246
260
  modified_sa_03s = modified_values['sa_03s']
247
261
  modified_sa_10s = modified_values['sa_10s']
@@ -8,11 +8,13 @@
8
8
  #
9
9
  #----------------------------------------------------------------------------#
10
10
  from django.urls import re_path
11
- from .views import new_prediction, asset_predictors, predictor_profile, predictor_upload
11
+ from .views import new_prediction, asset_predictors, predictor_profile, predictor_upload, asset_map
12
12
 
13
13
  urlpatterns = [
14
14
  re_path("^inventory/[0-9 A-Z-]*/predictors/new", new_prediction),
15
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/(?P<calid>[0-9 A-Z-]*)/predictors/", asset_predictors, name="asset_predictors")
16
+ re_path("^inventory/(?P<calid>[0-9 A-Z-]*)/predictors/create/map/$", asset_map),
17
+ re_path("^inventory/(?P<calid>[0-9 A-Z-]*)/map/$", asset_map),
18
+ re_path("^inventory/(?P<calid>[0-9 A-Z-]*)/predictors/create/$", predictor_upload),
19
+ re_path("^inventory/(?P<calid>[0-9 A-Z-]*)/predictors/", asset_predictors, name="asset_predictors")
18
20
  ]
@@ -11,11 +11,15 @@
11
11
  #----------------------------------------------------------------------------#
12
12
  import os
13
13
  import json
14
+ import veux
15
+ import uuid
16
+ import base64
14
17
 
15
18
  from django.shortcuts import HttpResponse
16
19
  from django.template import loader, TemplateDoesNotExist
17
20
  from django.contrib.auth.decorators import login_required
18
21
  from django.core.exceptions import ObjectDoesNotExist
22
+ from django.core.files.base import ContentFile
19
23
 
20
24
  from django.shortcuts import render
21
25
 
@@ -97,27 +101,116 @@ def predictor_profile(request, calid, preid):
97
101
  return HttpResponse(html_template.render(context, request))
98
102
 
99
103
 
104
+ @login_required(login_url="/login/")
105
+ def asset_map(request, calid):
106
+
107
+
108
+ r200 = loader.get_template("inventory/asset-on-map.html")
109
+ r400 = loader.get_template("site/page-400.html")
110
+ asset = Asset.objects.get(calid=calid)
111
+ context = {
112
+ "asset": asset,
113
+ "viewer": "three",
114
+ "location": json.dumps(list(reversed(list(asset.coordinates)))),
115
+ }
116
+
117
+ if request.method == "GET":
118
+ context["render_src"] = asset.rendering
119
+
120
+ elif request.method == "POST":
121
+ from openbim.csi import load, create_model, collect_outlines
122
+ # context["offset"] = json.dumps(list(reversed(list(asset.coordinates))))
123
+ context["rotate"] = "[0, 0, 0]"
124
+ context["scale"] = 1/3.2808 # TODO
125
+
126
+ uploaded_file = request.FILES.get('config_file')
127
+
128
+ try:
129
+ csi = load((str(line.decode()).replace("\r\n","\n") for line in uploaded_file.readlines()))
130
+ except Exception as e:
131
+ return HttpResponse(r400.render({"message": json.dumps({"error": str(e)})}), status=400)
132
+
133
+ try:
134
+ model = create_model(csi, verbose=True)
135
+ except Exception as e:
136
+ return HttpResponse(r400.render({"message": json.dumps({"error": str(e)})}), status=400)
137
+
138
+
139
+ outlines = collect_outlines(csi, model.frame_tags)
140
+ artist = veux.render(model, canvas="gltf", vertical=3,
141
+ reference={"frame.surface", "frame.axes"},
142
+ model_config={"frame_outlines": outlines})
143
+
144
+ glb = artist.canvas.to_glb()
145
+ glb64 = base64.b64encode(glb).decode("utf-8")
146
+ context["render_glb"] = f"data:application/octet-stream;base64,{glb64}"
147
+
148
+
149
+ try:
150
+ return HttpResponse(r200.render(context, request))
151
+
152
+ except Exception as e:
153
+ r500 = loader.get_template("site/page-500.html")
154
+ return HttpResponse(r500.render({"message": str(e)}, request), status=500)
155
+
156
+
157
+
158
+
100
159
  @login_required(login_url="/login/")
101
160
  def predictor_upload(request, calid):
102
161
 
162
+ asset = Asset.objects.get(calid=calid)
103
163
  html_template = loader.get_template("prediction/predictor-upload.html")
164
+ r400 = loader.get_template("site/page-400.html")
165
+ context = {
166
+ "asset": asset,
167
+ "segment": "inventory",
168
+ "viewer": "babylon",
169
+ "offset": json.dumps(list(reversed(list(asset.coordinates)))),
170
+ }
104
171
 
105
172
  if request.method == "POST":
173
+ from openbim.csi import load, create_model, collect_outlines
106
174
  form = PredictorForm(request.POST, request.FILES)
107
- asset = Asset.objects.get(calid=calid)
108
175
 
109
- # Save the predictor
110
- predictor = PREDICTOR_TYPES[request.POST.get("runner")].create(asset, request)
111
- predictor.save()
176
+ uploaded_file = request.FILES.get('config_file')
112
177
 
113
- return HttpResponse(json.dumps({"data": {"id": predictor.id}}))
178
+ try:
179
+ csi = load((str(line.decode()).replace("\r\n","\n") for line in uploaded_file.readlines()))
180
+ except Exception as e:
181
+ return HttpResponse(r400.render({"message": json.dumps({"error": str(e)})}), status=400)
114
182
 
115
- else:
116
- form = PredictorForm()
183
+ model = create_model(csi, verbose=True)
184
+
185
+ # Generate the .glb file using veux
186
+ outlines = collect_outlines(csi, model.frame_tags)
187
+ artist = veux.create_artist(model, canvas="gltf", vertical=3,
188
+ model_config={"frame_outlines": outlines}
189
+ )
190
+ artist.draw_surfaces()
191
+ glb = artist.canvas.to_glb()
192
+
193
+ if request.POST.get("action") == "commit":
194
+ if not form.is_valid():
195
+ return HttpResponse(json.dumps({"error": "Invalid form data"}), status=400)
196
+ predictor = PredictorModel()
197
+ predictor.active = False
198
+ predictor.asset = asset
199
+ predictor.name = form.cleaned_data['name']
200
+ predictor.description = "empty"
201
+
202
+ predictor.render_file.save(f"{uuid.uuid4()}.glb", ContentFile(glb), save=True)
203
+ predictor.save()
204
+
205
+ context["form"] = form
206
+
207
+ else: # probably a GET
208
+ context["form"] = PredictorForm()
117
209
 
118
210
 
119
211
  try:
120
- return render(request, "prediction/predictor-upload.html", {"form": form})
212
+ return HttpResponse(html_template.render(context, request))
213
+ # return render(request, "prediction/predictor-upload.html", {"form": form})
121
214
 
122
215
  except Exception as e:
123
216
  if "DEBUG" in os.environ and os.environ["DEBUG"]:
@@ -22277,38 +22277,6 @@ textarea.form-control-lg {
22277
22277
  color: #3A416F;
22278
22278
  }
22279
22279
 
22280
- .breadcrumb-item {
22281
- font-size: 0.875rem;
22282
- }
22283
- .breadcrumb-item.text-white::before {
22284
- color: #ffffff;
22285
- }
22286
-
22287
- .breadcrumb-dark {
22288
- background-color: #1F2937;
22289
- }
22290
- .breadcrumb-dark .breadcrumb-item {
22291
- font-weight: 600;
22292
- }
22293
- .breadcrumb-dark .breadcrumb-item a {
22294
- color: #F2F4F6;
22295
- }
22296
- .breadcrumb-dark .breadcrumb-item a:hover {
22297
- color: #ffffff;
22298
- }
22299
- .breadcrumb-dark .breadcrumb-item + .breadcrumb-item::before {
22300
- color: #6B7280;
22301
- }
22302
- .breadcrumb-dark .breadcrumb-item.active {
22303
- color: #D1D5DB;
22304
- }
22305
-
22306
- .breadcrumb-links {
22307
- padding: 0;
22308
- margin: 0;
22309
- background: transparent;
22310
- }
22311
-
22312
22280
  .card {
22313
22281
  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
22314
22282
  border: 1px solid #E5E7EB;
@@ -23442,7 +23410,6 @@ svg.text-dark .color-background {
23442
23410
  padding-left: 0.75rem;
23443
23411
  padding-right: 0.75rem;
23444
23412
  font-weight: 500;
23445
- color: #ffffff;
23446
23413
  font-size: 0.75rem;
23447
23414
  }
23448
23415
  .navbar-vertical .navbar-nav .nav-link > i {