Skip to content

Commit f147d09

Browse files
committed
OKRS24-144 Created new restAPI ListView for the GeographyUnit model to use it in EpiVis block in Signal Detailed View.
1 parent 4b4de34 commit f147d09

File tree

6 files changed

+175
-21
lines changed

6 files changed

+175
-21
lines changed

src/assets/css/custom.css

+35
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,39 @@
8686

8787
.padding-top-1rem {
8888
padding-top: 1rem;
89+
}
90+
91+
92+
.select2 {
93+
display: block;
94+
width: 100%!important;
95+
padding: 0.375rem 2.25rem 0.375rem 0.75rem;
96+
-moz-padding-start: calc(0.75rem - 3px);
97+
font-size: 1rem;
98+
font-weight: 400;
99+
line-height: 1.5;
100+
color: #212529;
101+
background-color: #fff;
102+
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");
103+
background-repeat: no-repeat;
104+
background-position: right 0.75rem center;
105+
background-size: 16px 12px;
106+
border: 1px solid #ced4da;
107+
border-radius: 0.25rem;
108+
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
109+
-webkit-appearance: none;
110+
-moz-appearance: none;
111+
appearance: none;
112+
}
113+
114+
.select2-container--default .select2-selection--single {
115+
border: none!important;
116+
}
117+
118+
.select2-container--default .select2-selection--single .select2-selection__arrow {
119+
display: none!important;
120+
}
121+
122+
.select2-container .select2-selection--single .select2-selection__rendered {
123+
padding-left: 0px!important;
89124
}

src/signals/serializers.py

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from rest_framework.serializers import ModelSerializer, SlugRelatedField
22

33
from base.serializers import LinkSerializer
4-
from signals.models import Signal
4+
from signals.models import Signal, GeographyUnit
55

66

77
class SignalBaseSerializer(ModelSerializer):
@@ -30,3 +30,15 @@ class SignalSerializer(ModelSerializer):
3030
class Meta:
3131
model = Signal
3232
fields = '__all__'
33+
34+
35+
class GeographyUnitSerialializer(ModelSerializer):
36+
"""
37+
Serializer for the GeographyUnit model.
38+
"""
39+
40+
category = SlugRelatedField(read_only=True, slug_field='name')
41+
42+
class Meta:
43+
model = GeographyUnit
44+
fields = '__all__'

src/signals/urls.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
SignalsDetailView,
66
SignalsListApiView,
77
SignalsListView,
8+
GeographyUnitListApiView
89
)
910

1011
urlpatterns: list[URLPattern] = [
@@ -13,5 +14,6 @@
1314
path('signals/<pk>/', SignalsDetailView.as_view(), name='signal'),
1415

1516
# REST API
16-
path('api/v1/signals/', SignalsListApiView.as_view(), name='signals_api')
17+
path('api/v1/signals/', SignalsListApiView.as_view(), name='signals_api'),
18+
path('api/v1/geography_units/', GeographyUnitListApiView.as_view(), name='geography_units_api')
1719
]

src/signals/views.py

+43-2
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22

33
from django.conf import settings
44
from django.views.generic import DetailView, ListView
5+
from django.db.models import Q
56
from django_filters.rest_framework import DjangoFilterBackend
67
from rest_framework.filters import SearchFilter
78
from rest_framework.generics import ListAPIView
89

910
from signals.filters import SignalFilter
1011
from signals.forms import SignalFilterForm
11-
from signals.models import Signal
12-
from signals.serializers import SignalSerializer
12+
from signals.models import Signal, GeographyUnit
13+
from signals.serializers import SignalSerializer, GeographyUnitSerialializer
1314

1415

1516
class SignalsListView(ListView):
@@ -91,6 +92,17 @@ class SignalsDetailView(DetailView):
9192

9293
model = Signal
9394

95+
def get_context_data(self, **kwargs) -> Dict[str, Any]:
96+
"""
97+
Get the context data for the view.
98+
99+
Returns:
100+
Dict[str, Any]: The context data for the view.
101+
"""
102+
103+
context: Dict[str, Any] = super().get_context_data(**kwargs)
104+
return context
105+
94106

95107
class SignalsListApiView(ListAPIView):
96108
"""
@@ -113,3 +125,32 @@ class SignalsListApiView(ListAPIView):
113125
"source__name",
114126
"time_label",
115127
)
128+
129+
130+
class GeographyUnitListApiView(ListAPIView):
131+
"""
132+
ListAPIView for retrieving a list of Signal objects via API.
133+
"""
134+
135+
queryset = GeographyUnit.objects.all()
136+
serializer_class = GeographyUnitSerialializer
137+
search_fields = ("name")
138+
filter_backends = [DjangoFilterBackend]
139+
filterset_fields = (
140+
"name",
141+
"display_name",
142+
"geography__name"
143+
)
144+
145+
def get_queryset(self):
146+
search_term = self.request.GET.get('term')
147+
if search_term:
148+
queries: list[Q] = []
149+
for field in ['name', 'display_name']:
150+
queries.append(Q(**{f'{field}__icontains': search_term}))
151+
query = queries.pop()
152+
153+
for item in queries:
154+
query |= item
155+
return GeographyUnit.objects.filter(query)
156+
return super().get_queryset()

src/templates/index.html

+6
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
<link href="{% static 'css/style.css' %}" rel="stylesheet" />
3232
<link href="{% static 'css/custom.css' %}" rel="stylesheet" />
3333

34+
<!-- Select2 css -->
35+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/select2.min.css" rel="stylesheet" />
36+
3437
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
3538
<script src="{% static 'js/htmx.min.js' %}"></script>
3639
</head>
@@ -130,5 +133,8 @@
130133
<script src="{% static 'vendor/tinymce/tinymce.min.js' %}"></script>
131134

132135
<script src="{% static 'js/main.js' %}"></script>
136+
137+
138+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/select2.min.js"></script>
133139
</body>
134140
</html>
+75-17
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,41 @@
1-
<h5>Build chart</h5>
1+
<h5>Plot data</h5>
22
<section class="section">
33
<div class="row">
44
<div class="card">
55
<div class="card-body">
6-
<form class="margin-top-1rem">
6+
<form class="margin-top-1rem" onsubmit="generateEpiVisLink(event)" id="epivis-form">
77
<div class="row">
88
<div class="col-2">
9-
<label for="inputState" class="form-label"
9+
<label for="source" class="form-label"
1010
>Data Source:</label
1111
>
1212
</div>
1313
<div class="col-10">
14-
<select id="inputState" class="form-select" disabled>
14+
<select id="source" name="source" class="form-select" disabled>
1515
<option selected>{{ signal.source.name }}</option>
1616
</select>
1717
</div>
1818
</div>
1919
<div class="row margin-top-1rem">
2020
<div class="col-2">
21-
<label for="inputState" class="form-label"
21+
<label for="signal" class="form-label"
2222
>Data Signal:</label
2323
>
2424
</div>
2525
<div class="col-10">
26-
<select id="inputState" class="form-select" disabled>
26+
<select id="signal" name="signal" class="form-select" disabled>
2727
<option selected>{{ signal.name }}</option>
2828
</select>
2929
</div>
3030
</div>
3131
<div class="row margin-top-1rem">
3232
<div class="col-2">
33-
<label for="inputState" class="form-label"
33+
<label for="geographic_type" class="form-label"
3434
>Geographic Type:</label
3535
>
3636
</div>
3737
<div class="col-10">
38-
<select id="inputState" class="form-select">
38+
<select id="geographic_type" name="geographic_type" class="form-select">
3939
<option selected>Choose...</option>
4040
{% for geography in signal.available_geography.all %}
4141
<option>{{ geography }}</option>
@@ -45,22 +45,80 @@ <h5>Build chart</h5>
4545
</div>
4646
<div class="row margin-top-1rem">
4747
<div class="col-2">
48-
<label for="inputEmail3" class="col-form-label">Geographic Value:</label>
48+
<label for="geographic_value" class="col-form-label">Geographic Value:</label>
4949
</div>
5050
<div class="col-10">
51-
<input type="text" class="form-control" id="inputText">
51+
<select id="geographic_value" name="geographic_value" class="form-select select-responsive"></select>
5252
</div>
5353
</div>
54-
{% comment %} <div class="text-center">
55-
<button type="submit" class="btn btn-primary">
56-
Submit
57-
</button>
58-
<button type="reset" class="btn btn-secondary">
59-
Reset
54+
<div class="row">
55+
<button type="submit" value="Submit" class="btn btn-primary margin-top-1rem" id="show-chart">
56+
Show plot
6057
</button>
61-
</div> {% endcomment %}
58+
</div>
6259
</form>
6360
</div>
6461
</div>
6562
</div>
6663
</section>
64+
<script>
65+
$(document).ready(function () {
66+
$('#geographic_value').select2({
67+
ajax: {
68+
url: '{% url 'geography_units_api' %}',
69+
dataType: 'json',
70+
data: function (params) {
71+
var geographic_type = document.getElementById('geographic_type');
72+
return {
73+
geography__name: geographic_type.value,
74+
term: params.term,
75+
limit: 50
76+
};
77+
},
78+
processResults: function (data) {
79+
return {
80+
results: $.map(data.results, function (item) {
81+
if (typeof item.geo_id === 'string') {
82+
return {
83+
text: item.display_name,
84+
id: item.geo_id.toLowerCase()
85+
}
86+
} else {
87+
return {
88+
text: item.display_name,
89+
id: item.geo_id
90+
}
91+
}
92+
})
93+
};
94+
}
95+
},
96+
minimumInputLength: 1
97+
});
98+
});
99+
100+
document.getElementById('geographic_type').addEventListener("change", (event) => {
101+
$('#geographic_value').val(null).trigger('change');
102+
})
103+
104+
105+
function generateEpiVisLink(event) {
106+
event.preventDefault();
107+
var dataSource = document.getElementById('source').value;
108+
var dataSignal = document.getElementById('signal').value;
109+
110+
var geographicType = document.getElementById('geographic_type').value;
111+
var geographicValue = document.getElementById('geographic_value').value;
112+
113+
var urlParamsEncoded = btoa(`{"datasets":[{"color":"#415742","title":"value","params":{"_endpoint":"covidcast","data_source":"${dataSource}","signal":"${dataSignal}","time_type":"day","geo_type":"${geographicType}","geo_value":"${geographicValue}"}}]}`);
114+
115+
var epiVisUrl = `https://deploy-preview-36--cmu-delphi-epivis.netlify.app/`;
116+
117+
if (geographicType === 'Choose...' || geographicValue === '') {
118+
window.open(epiVisUrl, '_blank').focus();
119+
} else {
120+
epiVisUrl += `#${urlParamsEncoded}`;
121+
window.open(epiVisUrl, '_blank').focus();
122+
}
123+
}
124+
</script>

0 commit comments

Comments
 (0)