Skip to content

Commit 4bc78e0

Browse files
authored
Merge pull request #3 from cmu-delphi/main
development
2 parents 244e730 + 4bc7cda commit 4bc78e0

11 files changed

+420
-62
lines changed

src/base/resources.py

+14-4
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,25 @@
5454
"display_order_number": 10,
5555
"short_name": "DMA",
5656
},
57-
"facility": {
58-
"display_name": 'Hospital ("Facility")',
57+
"other_substate_region": {
58+
"display_name": "Other Substate Region",
5959
"display_order_number": 11,
60-
"short_name": "Facility",
60+
"short_name": "Other Substate Region",
6161
},
6262
"FluSurv-Net site": {
6363
"display_name": "FluSurv-Net site (see documentation)",
6464
"display_order_number": 12,
6565
"short_name": "FluSurv-Net site",
6666
},
67-
"N/A": {"display_name": "N/A", "display_order_number": 13, "short_name": "N/A"},
67+
"facility": {
68+
"display_name": 'Hospital ("Facility")',
69+
"display_order_number": 13,
70+
"short_name": "Facility",
71+
},
72+
"lat_long": {
73+
"display_name": "Lat/Long",
74+
"display_order_number": 14,
75+
"short_name": "Lat/Long",
76+
},
77+
"N/A": {"display_name": "N/A", "display_order_number": 15, "short_name": "N/A"},
6878
}

src/indicators/admin.py

+20
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
IndicatorGeography,
99
IndicatorType,
1010
OtherEndpointIndicator,
11+
NonDelphiIndicator
1112
)
1213
from indicators.resources import (
1314
IndicatorResource,
1415
IndicatorBaseResource,
1516
OtherEndpointIndicatorResource,
17+
NonDelphiIndicatorResource,
1618
)
1719

1820

@@ -90,3 +92,21 @@ class OtherEndpointIndicatorAdmin(ImportExportModelAdmin):
9092
list_display_links = ("name",)
9193

9294
resource_classes = [OtherEndpointIndicatorResource]
95+
96+
97+
@admin.register(NonDelphiIndicator)
98+
class NonDelphiIndicatorAdmin(ImportExportModelAdmin):
99+
list_display = (
100+
"name",
101+
"member_name",
102+
"description",
103+
"indicator_set",
104+
)
105+
search_fields = ("name", "description")
106+
ordering = ("name",)
107+
list_per_page = 50
108+
list_select_related = True
109+
list_editable = ("member_name", "description", "indicator_set")
110+
list_display_links = ("name",)
111+
112+
resource_classes = [NonDelphiIndicatorResource]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Generated by Django 5.0.7 on 2025-05-01 14:57
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('indicators', '0001_initial'),
10+
]
11+
12+
operations = [
13+
migrations.CreateModel(
14+
name='NonDelphiIndicator',
15+
fields=[
16+
],
17+
options={
18+
'verbose_name': 'Non-Delphi Indicator',
19+
'verbose_name_plural': 'Non-Delphi Indicators',
20+
'proxy': True,
21+
'indexes': [],
22+
'constraints': [],
23+
},
24+
bases=('indicators.indicator',),
25+
),
26+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Generated by Django 5.0.7 on 2025-05-01 15:04
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('base', '0001_initial'),
11+
('datasources', '0001_initial'),
12+
('indicators', '0002_nondelphiindicator'),
13+
]
14+
15+
operations = [
16+
migrations.AlterField(
17+
model_name='indicator',
18+
name='geographic_scope',
19+
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='indicators', to='base.geographicscope', verbose_name='Geographic Scope'),
20+
),
21+
migrations.AlterField(
22+
model_name='indicator',
23+
name='source',
24+
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='indicators', to='datasources.sourcesubdivision', verbose_name='Source Subdivision'),
25+
),
26+
]

src/indicators/models.py

+12
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,8 @@ class Indicator(models.Model):
235235
verbose_name="Geographic Scope",
236236
on_delete=models.PROTECT,
237237
related_name="indicators",
238+
null=True,
239+
blank=True,
238240
)
239241
available_geographies: models.ManyToManyField = models.ManyToManyField(
240242
"base.Geography",
@@ -300,6 +302,8 @@ class Indicator(models.Model):
300302
verbose_name="Source Subdivision",
301303
on_delete=models.PROTECT,
302304
related_name="indicators",
305+
null=True,
306+
blank=True,
303307
)
304308
data_censoring: models.TextField = models.TextField(
305309
verbose_name="Data Censoring",
@@ -437,3 +441,11 @@ class Meta:
437441
proxy = True
438442
verbose_name = "Other Endpoint Indicator"
439443
verbose_name_plural = "Other Endpoint Indicators"
444+
445+
446+
class NonDelphiIndicator(Indicator):
447+
448+
class Meta:
449+
proxy = True
450+
verbose_name = "Non-Delphi Indicator"
451+
verbose_name_plural = "Non-Delphi Indicators"

src/indicators/resources.py

+69-21
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
import logging
2+
13
from django.db.models import Max
24
from import_export import resources
35
from import_export.fields import Field
4-
from import_export.widgets import ForeignKeyWidget, ManyToManyWidget
56
from import_export.results import RowResult
7+
from import_export.widgets import ForeignKeyWidget, ManyToManyWidget
68

79
from base.models import GeographicScope, Geography, Pathogen, SeverityPyramidRung
810
from base.resources import GEOGRAPHIC_GRANULARITY_MAPPING
@@ -13,8 +15,12 @@
1315
Indicator,
1416
IndicatorGeography,
1517
IndicatorType,
18+
NonDelphiIndicator,
19+
OtherEndpointIndicator,
1620
)
17-
from indicatorsets.models import IndicatorSet
21+
from indicatorsets.models import IndicatorSet, NonDelphiIndicatorSet
22+
23+
logger = logging.getLogger(__name__)
1824

1925

2026
def fix_boolean_fields(row) -> None:
@@ -29,11 +35,11 @@ def fix_boolean_fields(row) -> None:
2935
]
3036

3137
for field in fields:
32-
if row[field] == "TRUE":
38+
if row.get(field, "") == "TRUE":
3339
row[field] = True
34-
elif row[field] == "FALSE":
40+
elif row.get(field, "") == "FALSE":
3541
row[field] = False
36-
elif row[field] == "":
42+
elif row.get(field, None) == "":
3743
row[field] = False
3844
return row
3945

@@ -225,18 +231,27 @@ def import_row(self, row, instance_loader, **kwargs):
225231
return import_result
226232

227233

234+
class PermissiveForeignKeyWidget(ForeignKeyWidget):
235+
236+
def clean(self, value, row=None, **kwargs):
237+
try:
238+
return super().clean(value)
239+
except self.model.DoesNotExist:
240+
logger.warning(f"instance matching '{value}' does not exist")
241+
242+
228243
class IndicatorBaseResource(ModelResource):
229244
name = Field(attribute="name", column_name="Signal")
230245
display_name = Field(attribute="display_name", column_name="Name")
231246
base = Field(
232247
attribute="base",
233248
column_name="base",
234-
widget=ForeignKeyWidget(Indicator, field="id"),
249+
widget=PermissiveForeignKeyWidget(Indicator, field="id"),
235250
)
236251
source = Field(
237252
attribute="source",
238253
column_name="Source Subdivision",
239-
widget=ForeignKeyWidget(SourceSubdivision, field="name"),
254+
widget=PermissiveForeignKeyWidget(SourceSubdivision, field="name"),
240255
)
241256

242257
class Meta:
@@ -271,13 +286,13 @@ class IndicatorResource(ModelResource):
271286
indicator_type = Field(
272287
attribute="indicator_type",
273288
column_name="Indicator Type",
274-
widget=ForeignKeyWidget(IndicatorType),
289+
widget=PermissiveForeignKeyWidget(IndicatorType),
275290
)
276291
active = Field(attribute="active", column_name="Active")
277292
format_type = Field(
278293
attribute="format_type",
279294
column_name="Format",
280-
widget=ForeignKeyWidget(FormatType),
295+
widget=PermissiveForeignKeyWidget(FormatType),
281296
)
282297
time_type = Field(attribute="time_type", column_name="Time Type")
283298
time_label = Field(attribute="time_label", column_name="Time Label")
@@ -299,12 +314,12 @@ class IndicatorResource(ModelResource):
299314
category = Field(
300315
attribute="category",
301316
column_name="Category",
302-
widget=ForeignKeyWidget(Category),
317+
widget=PermissiveForeignKeyWidget(Category),
303318
)
304319
geographic_scope = Field(
305320
attribute="geographic_scope",
306321
column_name="Geographic Coverage",
307-
widget=ForeignKeyWidget(GeographicScope),
322+
widget=PermissiveForeignKeyWidget(GeographicScope),
308323
)
309324
available_geographies = Field(
310325
attribute="available_geographies",
@@ -332,7 +347,7 @@ class IndicatorResource(ModelResource):
332347
source = Field(
333348
attribute="source",
334349
column_name="Source Subdivision",
335-
widget=ForeignKeyWidget(SourceSubdivision),
350+
widget=PermissiveForeignKeyWidget(SourceSubdivision),
336351
)
337352
data_censoring = Field(attribute="data_censoring", column_name="Data Censoring")
338353
missingness = Field(attribute="missingness", column_name="Missingness")
@@ -349,7 +364,7 @@ class IndicatorResource(ModelResource):
349364
indicator_set = Field(
350365
attribute="indicator_set",
351366
column_name="Indicator Set",
352-
widget=ForeignKeyWidget(IndicatorSet, field="name"),
367+
widget=PermissiveForeignKeyWidget(IndicatorSet, field="name"),
353368
)
354369

355370
class Meta:
@@ -425,7 +440,7 @@ class OtherEndpointIndicatorResource(ModelResource):
425440
attribute="short_description", column_name="Short Description"
426441
)
427442
description = Field(attribute="description", column_name="Description")
428-
member_name = Field(attribute="member_name", column_name="Member API Name")
443+
member_name = Field(attribute="member_name", column_name="Member Name")
429444
member_short_name = Field(
430445
attribute="member_short_name", column_name="Member Short Name"
431446
)
@@ -440,13 +455,13 @@ class OtherEndpointIndicatorResource(ModelResource):
440455
indicator_type = Field(
441456
attribute="indicator_type",
442457
column_name="Indicator Type",
443-
widget=ForeignKeyWidget(IndicatorType),
458+
widget=PermissiveForeignKeyWidget(IndicatorType),
444459
)
445460
active = Field(attribute="active", column_name="Active")
446461
format_type = Field(
447462
attribute="format_type",
448463
column_name="Format",
449-
widget=ForeignKeyWidget(FormatType),
464+
widget=PermissiveForeignKeyWidget(FormatType),
450465
)
451466
time_type = Field(attribute="time_type", column_name="Time Type")
452467
time_label = Field(attribute="time_label", column_name="Time Label")
@@ -468,12 +483,12 @@ class OtherEndpointIndicatorResource(ModelResource):
468483
category = Field(
469484
attribute="category",
470485
column_name="Category",
471-
widget=ForeignKeyWidget(Category),
486+
widget=PermissiveForeignKeyWidget(Category),
472487
)
473488
geographic_scope = Field(
474489
attribute="geographic_scope",
475490
column_name="Geographic Coverage",
476-
widget=ForeignKeyWidget(GeographicScope),
491+
widget=PermissiveForeignKeyWidget(GeographicScope),
477492
)
478493
available_geographies = Field(
479494
attribute="available_geographies",
@@ -501,7 +516,7 @@ class OtherEndpointIndicatorResource(ModelResource):
501516
source = Field(
502517
attribute="source",
503518
column_name="Source Subdivision",
504-
widget=ForeignKeyWidget(SourceSubdivision),
519+
widget=PermissiveForeignKeyWidget(SourceSubdivision),
505520
)
506521
data_censoring = Field(attribute="data_censoring", column_name="Data Censoring")
507522
missingness = Field(attribute="missingness", column_name="Missingness")
@@ -518,11 +533,11 @@ class OtherEndpointIndicatorResource(ModelResource):
518533
indicator_set = Field(
519534
attribute="indicator_set",
520535
column_name="Indicator Set",
521-
widget=ForeignKeyWidget(IndicatorSet, field="name"),
536+
widget=PermissiveForeignKeyWidget(IndicatorSet, field="name"),
522537
)
523538

524539
class Meta:
525-
model = Indicator
540+
model = OtherEndpointIndicator
526541
fields: list[str] = [
527542
"name",
528543
"display_name",
@@ -585,3 +600,36 @@ def skip_row(self, instance, original, row, import_validation_errors=None):
585600

586601
def after_import_row(self, row, row_result, **kwargs):
587602
process_indicator_geography(row)
603+
604+
605+
class NonDelphiIndicatorResource(resources.ModelResource):
606+
607+
name = Field(attribute="name", column_name="Indicator Name")
608+
display_name = Field(attribute="display_name", column_name="Indicator Name")
609+
member_name = Field(attribute="member_name", column_name="Indicator API Name")
610+
description = Field(attribute="description", column_name="Indicator Description")
611+
indicator_set = Field(
612+
attribute="indicator_set",
613+
column_name="Indicator Set",
614+
widget=PermissiveForeignKeyWidget(NonDelphiIndicatorSet, field="name"),
615+
)
616+
617+
class Meta:
618+
model = NonDelphiIndicator
619+
fields: list[str] = [
620+
"name",
621+
"display_name",
622+
"member_name",
623+
"description",
624+
"indicator_set",
625+
]
626+
import_id_fields: list[str] = ["name"]
627+
skip_unchanged = True
628+
629+
def before_import_row(self, row, **kwargs) -> None:
630+
"""Post-processes each row after importing."""
631+
fix_boolean_fields(row)
632+
633+
def skip_row(self, instance, original, row, import_validation_errors=None):
634+
if not row["Include in indicator app"]:
635+
return True

0 commit comments

Comments
 (0)