Skip to content

Commit 0a9c26c

Browse files
authored
Merge pull request #138 from cmu-delphi/development
Development
2 parents 92683de + fdc424f commit 0a9c26c

File tree

11 files changed

+406
-55
lines changed

11 files changed

+406
-55
lines changed

src/assets/js/signal_sets.js

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,32 @@ function showWarningAlert(warningMessage, slideUpTime = 2000) {
1515
});
1616
}
1717

18-
function checkGeoCoverage(geoType, geoValue) {
19-
var notCoveredSignals = [];
20-
$.ajax({
21-
url: "epidata/covidcast/geo_coverage/",
22-
type: 'GET',
23-
async: false,
24-
data: {
25-
'geo': `${geoType}:${geoValue}`
26-
},
27-
success: function (result) {
28-
checkedSignalMembers.forEach(signal => {
29-
var covered = result["epidata"].some(
30-
e => (e.source === signal.data_source && e.signal === signal.signal)
31-
)
32-
if (!covered) {
33-
notCoveredSignals.push(signal);
34-
}
35-
})
36-
}
37-
})
38-
return notCoveredSignals;
18+
async function checkGeoCoverage(geoType, geoValue) {
19+
const notCoveredSignals = [];
20+
21+
try {
22+
const result = await $.ajax({
23+
url: "epidata/covidcast/geo_coverage/",
24+
type: 'GET',
25+
data: {
26+
'geo': `${geoType}:${geoValue}`
27+
}
28+
});
29+
30+
checkedSignalMembers.forEach(signal => {
31+
const covered = result["epidata"].some(
32+
e => (e.source === signal.data_source && e.signal === signal.signal)
33+
);
34+
if (!covered) {
35+
notCoveredSignals.push(signal);
36+
}
37+
});
38+
39+
return notCoveredSignals;
40+
} catch (error) {
41+
console.error('Error fetching geo coverage:', error);
42+
return notCoveredSignals;
43+
}
3944
}
4045

4146

@@ -123,6 +128,18 @@ function addSelectedSignal(element) {
123128
}
124129
}
125130

131+
$("#showSelectedSignalsButton").click(function() {
132+
alertPlaceholder.innerHTML = "";
133+
$('#geographic_value').select2("data").forEach(geo => {
134+
checkGeoCoverage(geo.geoType, geo.id).then((notCoveredSignals) => {
135+
if (notCoveredSignals.length > 0) {
136+
showNotCoveredGeoWarningMessage(notCoveredSignals, geo.text);
137+
}
138+
})
139+
140+
});
141+
});
142+
126143
// Add an event listener to each 'bulk-select' element
127144
let bulkSelectDivs = document.querySelectorAll('.bulk-select');
128145
bulkSelectDivs.forEach(div => {
@@ -439,15 +456,23 @@ function showNotCoveredGeoWarningMessage(notCoveredSignals, geoValue) {
439456

440457
$('#geographic_value').on('select2:select', function (e) {
441458
var geo = e.params.data;
442-
var notCoveredSignals = checkGeoCoverage(geo.geoType, geo.id)
443-
if (notCoveredSignals.length > 0) {
444-
showNotCoveredGeoWarningMessage(notCoveredSignals, geo.text);
459+
checkGeoCoverage(geo.geoType, geo.id).then((notCoveredSignals) => {
460+
if (notCoveredSignals.length > 0) {
461+
showNotCoveredGeoWarningMessage(notCoveredSignals, geo.text);
462+
}
445463
}
464+
);
446465
});
447466

448467

449468
function submitMode(event) {
450469
event.preventDefault();
470+
var geographicValues = $('#geographic_value').select2('data');
471+
472+
if (geographicValues.length === 0) {
473+
appendAlert("Please select at least one geographic location", "warning")
474+
return;
475+
}
451476

452477
if (currentMode === 'epivis') {
453478
plotData();

src/datasources/resources.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,21 @@ def process_links(row, dua_column_name="DUA", link_column_name="Link"):
2727
links.append(link.id)
2828
else:
2929
for match in matches:
30-
link, _ = Link.objects.get_or_create(url=match[1], defaults={'link_type': match[0], })
30+
link, _ = Link.objects.get_or_create(
31+
url=match[1],
32+
defaults={
33+
"link_type": match[0],
34+
},
35+
)
3136
links.append(link.id)
3237
row["Links"] = links
3338

3439

40+
def process_datasource_name(row):
41+
if row["Name"]:
42+
row["Name"] = row["Name"].capitalize()
43+
44+
3545
def process_datasources(row):
3646
datasource, _ = DataSource.objects.get_or_create(
3747
name=row["DB Source"],
@@ -67,5 +77,6 @@ class Meta:
6777
skip_unchanged = True
6878

6979
def before_import_row(self, row, **kwargs):
80+
process_datasource_name(row)
7081
process_links(row)
7182
process_datasources(row)

src/fixtures/severity_pyramid_rungs.json

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,10 @@
8282
"fields": {
8383
"created": "2024-08-15T09:57:49.327Z",
8484
"modified": "2024-08-15T13:07:46.136Z",
85-
"name": "Population",
86-
"display_name": "Population",
87-
"used_in": "signal_sets"
85+
"name": "Entire Population",
86+
"display_name": "Entire Population",
87+
"used_in": "signal_sets",
88+
"display_order_number": 1
8889
}
8990
},
9091
{
@@ -95,7 +96,8 @@
9596
"modified": "2024-08-15T13:07:46.136Z",
9697
"name": "Vaccinated",
9798
"display_name": "Vaccinated",
98-
"used_in": "signal_sets"
99+
"used_in": "signal_sets",
100+
"display_order_number": 2
99101
}
100102
},
101103
{
@@ -106,7 +108,8 @@
106108
"modified": "2024-08-15T13:07:46.136Z",
107109
"name": "Infected",
108110
"display_name": "Infected",
109-
"used_in": "signal_sets"
111+
"used_in": "signal_sets",
112+
"display_order_number": 3
110113
}
111114
},
112115
{
@@ -117,7 +120,8 @@
117120
"modified": "2024-08-15T13:07:46.136Z",
118121
"name": "Tested",
119122
"display_name": "Tested",
120-
"used_in": "signal_sets"
123+
"used_in": "signal_sets",
124+
"display_order_number": 4
121125
}
122126
},
123127
{
@@ -128,7 +132,8 @@
128132
"modified": "2024-08-15T13:07:46.136Z",
129133
"name": "Ascertained (Case)",
130134
"display_name": "Ascertained (Case)",
131-
"used_in": "signal_sets"
135+
"used_in": "signal_sets",
136+
"display_order_number": 5
132137
}
133138
},
134139
{
@@ -139,7 +144,8 @@
139144
"modified": "2024-08-15T13:07:46.136Z",
140145
"name": "Symptomatic",
141146
"display_name": "Symptomatic",
142-
"used_in": "signal_sets"
147+
"used_in": "signal_sets",
148+
"display_order_number": 6
143149
}
144150
},
145151
{
@@ -150,7 +156,8 @@
150156
"modified": "2024-08-15T13:07:46.136Z",
151157
"name": "Outpatient / ED",
152158
"display_name": "Outpatient / ED",
153-
"used_in": "signal_sets"
159+
"used_in": "signal_sets",
160+
"display_order_number": 7
154161
}
155162
},
156163
{
@@ -161,7 +168,8 @@
161168
"modified": "2024-08-15T13:07:46.136Z",
162169
"name": "Hospitalized",
163170
"display_name": "Hospitalized",
164-
"used_in": "signal_sets"
171+
"used_in": "signal_sets",
172+
"display_order_number": 8
165173
}
166174
},
167175
{
@@ -172,7 +180,8 @@
172180
"modified": "2024-08-15T13:07:46.136Z",
173181
"name": "ICU",
174182
"display_name": "ICU",
175-
"used_in": "signal_sets"
183+
"used_in": "signal_sets",
184+
"display_order_number": 9
176185
}
177186
},
178187
{
@@ -183,7 +192,8 @@
183192
"modified": "2024-08-15T13:07:46.136Z",
184193
"name": "Deceased",
185194
"display_name": "Deceased",
186-
"used_in": "signal_sets"
195+
"used_in": "signal_sets",
196+
"display_order_number": 10
187197
}
188198
}
189199
]

src/signal_sets/forms.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class SignalSetFilterForm(forms.ModelForm):
3535
queryset=SeverityPyramidRung.objects.filter(
3636
# id__in=SignalSet.objects.values_list("severity_pyramid_rungs", flat="True")
3737
used_in="signal_sets"
38-
),
38+
).order_by("display_order_number"),
3939
widget=forms.CheckboxSelectMultiple(),
4040
)
4141

src/signal_sets/resources.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,10 @@ def skip_row(self, instance, original, row, import_validation_errors=None):
221221
def after_import_row(self, row, row_result, **kwargs):
222222
try:
223223
signal_set_obj = SignalSet.objects.get(id=row_result.object_id)
224+
signal_set_obj.pathogens.clear()
225+
signal_set_obj.severity_pyramid_rungs.clear()
226+
signal_set_obj.available_geographies.clear()
224227
for pathogen in row["Pathogen(s)/Syndrome(s)"].split(","):
225-
signal_set_obj.pathogens.clear()
226228
pathogen = Pathogen.objects.get(name=pathogen, used_in="signal_sets")
227229
signal_set_obj.pathogens.add(pathogen)
228230
for severity_pyramid_rung in row["Surveillance Categories"].split(","):
@@ -231,7 +233,6 @@ def after_import_row(self, row, row_result, **kwargs):
231233
used_in="signal_sets"
232234
).first()
233235
signal_set_obj.severity_pyramid_rungs.add(severity_pyramid_rung)
234-
235236
for available_geography in row["Geographic Granularity - Delphi"].split(","):
236237
available_geography = Geography.objects.get(name=available_geography, used_in="signal_sets")
237238
signal_set_obj.available_geographies.add(available_geography)

src/signals/admin.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from django.contrib import admin
44
from import_export.admin import ImportExportModelAdmin
55

6-
from signals.resources import SignalResource, SignalBaseResource
6+
from signals.resources import SignalResource, SignalBaseResource, OtherEndpointSignalResource
77

88

99
from signals.models import (
@@ -14,6 +14,7 @@
1414
Pathogen,
1515
SeverityPyramidRung,
1616
Signal,
17+
OtherEndointSignal,
1718
SignalType,
1819
SignalGeography,
1920
GeographyUnit,
@@ -102,6 +103,7 @@ class SeverityPyramidRungAdmin(admin.ModelAdmin):
102103
"name",
103104
"display_name",
104105
"used_in",
106+
"display_order_number",
105107
)
106108
exclude = ("id",)
107109
search_fields: tuple[Literal["name"]] = ("name",)
@@ -149,6 +151,35 @@ class SignalAdmin(ImportExportModelAdmin):
149151
resource_classes: list[type[SignalResource]] = [SignalResource, SignalBaseResource]
150152

151153

154+
@admin.register(OtherEndointSignal)
155+
class OtherEndpointsSignalAdmin(ImportExportModelAdmin):
156+
"""
157+
Admin interface for managing signal objects.
158+
"""
159+
160+
list_display: tuple[
161+
Literal["name"],
162+
Literal["signal_type"],
163+
Literal["format_type"],
164+
Literal["category"],
165+
Literal["geographic_scope"],
166+
] = ("name", "signal_type", "format_type", "category", "geographic_scope")
167+
search_fields: tuple[
168+
Literal["name"],
169+
Literal["signal_type__name"],
170+
Literal["format_type__name"],
171+
Literal["category__name"],
172+
Literal["geographic_scope__name"],
173+
] = (
174+
"name",
175+
"signal_type__name",
176+
"format_type__name",
177+
"category__name",
178+
"geographic_scope__name",
179+
)
180+
resource_classes: list[type[SignalResource]] = [OtherEndpointSignalResource]
181+
182+
152183
@admin.register(SignalGeography)
153184
class SignalGeographyAdmin(admin.ModelAdmin):
154185
"""
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 5.0.7 on 2025-03-12 17:52
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('signals', '0016_signal_source_view'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='severitypyramidrung',
15+
name='display_order_number',
16+
field=models.IntegerField(help_text='Display order number of the severity pyramid rung.', null=True, verbose_name='display order number'),
17+
),
18+
]
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Generated by Django 5.0.7 on 2025-03-27 18:21
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('signals', '0017_severitypyramidrung_display_order_number'),
10+
]
11+
12+
operations = [
13+
migrations.CreateModel(
14+
name='OtherEndointSignal',
15+
fields=[
16+
],
17+
options={
18+
'verbose_name': 'Other Endpoint Signal',
19+
'verbose_name_plural': 'Other Endpoint Signals',
20+
'proxy': True,
21+
'indexes': [],
22+
'constraints': [],
23+
},
24+
bases=('signals.signal',),
25+
),
26+
]

src/signals/models.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ class SeverityPyramidRung(TimeStampedModel):
8181
default="signals",
8282
)
8383

84+
display_order_number: models.IntegerField = models.IntegerField(
85+
verbose_name=_("display order number"),
86+
help_text=_("Display order number of the severity pyramid rung."),
87+
null=True,
88+
)
89+
8490
class Meta:
8591
verbose_name_plural: str = "Severity Pyramid Rungs"
8692
unique_together: list[str] = ["name", "used_in"]
@@ -569,6 +575,13 @@ def get_display_name(self):
569575
return self.name
570576

571577

578+
class OtherEndointSignal(Signal):
579+
class Meta:
580+
proxy = True
581+
verbose_name = "Other Endpoint Signal"
582+
verbose_name_plural = "Other Endpoint Signals"
583+
584+
572585
class SignalsDbView(models.Model):
573586
id = models.BigIntegerField(primary_key=True)
574587
name = models.CharField(max_length=255)

0 commit comments

Comments
 (0)