Skip to content

Commit d782048

Browse files
committed
Additional features and functionality:
based on issue inasafe/inasafe-realtime#35 Celery: - Limit prefetch multiplier explicitly to 1 (avoid hanging worker) Earthquake: - Add MMI Output FileField - Add download button for MMI Output zip - Auto zoom to first earthquake in the table Flood: - Fix signals to recalculate total affected population and flooded RW - Add Data Source Field, since it needs to support PetaBencana - Add code branching to handle old hazard data and new PetaBencana - Add download button for Hazard File and Impact Layers - Change download button icon to follow Earthquake pattern - Auto zoom to first flood in the table Ash: - Add Impact Files FileField - Add download button for Impact Files zip - Change download button icon to follow Earthquake pattern - Auto zoom to first ash event in the table
1 parent 0a1bbd3 commit d782048

File tree

24 files changed

+563
-121
lines changed

24 files changed

+563
-121
lines changed

django_project/core/settings/celery_config.py

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
CELERY_DEFAULT_ROUTING_KEY = "default"
2323
CELERY_CREATE_MISSING_QUEUES = True
2424
CELERYD_CONCURRENCY = 1
25+
CELERYD_PREFETCH_MULTIPLIER = 1
2526

2627
CELERY_QUEUES = [
2728
Queue('default', routing_key='default'),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import unicode_literals
3+
4+
from django.db import models, migrations
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('realtime', '0025_auto_20170206_2046'),
11+
]
12+
13+
operations = [
14+
migrations.AddField(
15+
model_name='ash',
16+
name='impact_files',
17+
field=models.FileField(help_text=b'Impact files processed zipped', upload_to=b'ash/impact_files/%Y/%m/%d', null=True, verbose_name=b'Impact Files', blank=True),
18+
preserve_default=True,
19+
),
20+
migrations.AddField(
21+
model_name='earthquake',
22+
name='mmi_output',
23+
field=models.FileField(help_text=b'MMI related file, layers, and data, zipped.', upload_to=b'earthquake/mmi_output', null=True, verbose_name=b'MMI related file zipped', blank=True),
24+
preserve_default=True,
25+
),
26+
migrations.AlterField(
27+
model_name='ash',
28+
name='eruption_height',
29+
field=models.IntegerField(default=0, verbose_name=b'Eruption height in metres'),
30+
preserve_default=True,
31+
),
32+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import unicode_literals
3+
4+
from django.db import models, migrations
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('realtime', '0026_auto_20170209_1905'),
11+
]
12+
13+
operations = [
14+
migrations.AddField(
15+
model_name='flood',
16+
name='data_source',
17+
field=models.CharField(default=None, max_length=255, blank=True, help_text=b'The source of the hazard data used for analysis', null=True, verbose_name=b'The source of hazard data'),
18+
preserve_default=True,
19+
),
20+
]

django_project/realtime/models/ash.py

+9
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ class Meta:
3535
upload_to='ash/hazard_file/%Y/%m/%d',
3636
blank=False
3737
)
38+
impact_files = models.FileField(
39+
verbose_name='Impact Files',
40+
help_text='Impact files processed zipped',
41+
upload_to='ash/impact_files/%Y/%m/%d',
42+
blank=True,
43+
null=True
44+
)
3845
event_time = models.DateTimeField(
3946
verbose_name='Event Date and Time',
4047
help_text='The time the ash happened.',
@@ -67,6 +74,8 @@ def delete(self, using=None):
6774
# delete all report
6875
if self.hazard_file:
6976
self.hazard_file.delete()
77+
if self.impact_files:
78+
self.impact_files.delete()
7079
return super(Ash, self).delete(using=using)
7180

7281

django_project/realtime/models/earthquake.py

+8
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ class Meta:
2222
upload_to='earthquake/grid',
2323
blank=True,
2424
null=True)
25+
mmi_output = models.FileField(
26+
verbose_name='MMI related file zipped',
27+
help_text='MMI related file, layers, and data, zipped.',
28+
upload_to='earthquake/mmi_output',
29+
blank=True,
30+
null=True)
2531
magnitude = models.FloatField(
2632
verbose_name='The magnitude',
2733
help_text='The magnitude of the event.')
@@ -61,6 +67,8 @@ def delete(self, using=None):
6167
# delete all report
6268
if self.shake_grid:
6369
self.shake_grid.delete()
70+
if self.mmi_output:
71+
self.mmi_output.delete()
6472
for report in self.reports.all():
6573
report.delete(using=using)
6674
super(Earthquake, self).delete(using=using)

django_project/realtime/models/flood.py

+7
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ class Meta:
8181
max_length=20,
8282
unique=True,
8383
blank=False)
84+
data_source = models.CharField(
85+
verbose_name='The source of hazard data',
86+
help_text='The source of the hazard data used for analysis',
87+
max_length=255,
88+
blank=True,
89+
null=True,
90+
default=None)
8491
time = models.DateTimeField(
8592
verbose_name='Date and Time',
8693
help_text='The time the flood reported.',

django_project/realtime/serializers/ash_serializer.py

+21
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ class Meta:
117117
'url',
118118
'id',
119119
'volcano',
120+
'hazard_file',
121+
'impact_files',
120122
'reports',
121123
'event_time',
122124
'task_status',
@@ -132,13 +134,32 @@ class AshGeoJsonSerializer(GeoFeatureModelSerializer):
132134
def get_location(self, obj):
133135
return obj.volcano.location
134136

137+
def get_event_id_formatted(self, serializer_field, obj):
138+
"""
139+
:param serializer_field:
140+
:type serializer_field: CustomSerializerMethodField
141+
:param obj:
142+
:type obj: Ash
143+
:return:
144+
"""
145+
dateformat = '%Y%m%d%H%M%S%z'
146+
return '%s-%s' % (
147+
obj.event_time.strftime(dateformat),
148+
obj.volcano.volcano_name)
149+
150+
# auto bind to get_url method
151+
event_id_formatted = CustomSerializerMethodField()
152+
135153
class Meta:
136154
model = Ash
137155
geo_field = 'location'
138156
id = 'id',
139157
fields = (
140158
'id',
159+
'event_id_formatted',
141160
'volcano',
161+
'hazard_file',
162+
'impact_files',
142163
'event_time',
143164
'alert_level',
144165
'task_status',

django_project/realtime/serializers/earthquake_serializer.py

+2
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ class Meta:
106106
'url',
107107
'shake_id',
108108
'shake_grid',
109+
'mmi_output',
109110
'magnitude',
110111
'time',
111112
'depth',
@@ -125,6 +126,7 @@ class Meta:
125126
fields = (
126127
'shake_id',
127128
'shake_grid',
129+
'mmi_output',
128130
'magnitude',
129131
'time',
130132
'depth',

django_project/realtime/serializers/flood_serializer.py

+1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ class Meta:
139139
fields = (
140140
'url',
141141
'event_id',
142+
'data_source',
142143
'event_id_formatted',
143144
'time',
144145
'time_description',

django_project/realtime/signals/flood.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,6 @@ def flood_post_save(
3838
chain(
3939
process_hazard_layer.si(instance),
4040
process_impact_layer.si(instance),
41-
recalculate_impact_info(instance))()
41+
recalculate_impact_info.si(instance))()
4242
except Exception as e:
4343
LOGGER.exception(e)

django_project/realtime/static/realtime/css/realtime.css

+9-1
Original file line numberDiff line numberDiff line change
@@ -263,4 +263,12 @@ th.dynatable-head a, th.dynatable-head a:hover {
263263
/* ******************** */
264264
.timepicker-picker td.separator{
265265
line-height: 54px;
266-
}
266+
}
267+
268+
269+
/* ******************** */
270+
/* Dropdown in table */
271+
/* ******************** */
272+
.btn-group .dropdown-menu{
273+
position: absolute;
274+
}

django_project/realtime/static/realtime/js/ash/ash.js

+110-15
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,15 @@ function createShowEventHandler(map, markers, map_events) {
106106

107107
/**
108108
* Closure to create handler for showReport
109-
* use magic number 000 for url placeholder
109+
* use magic number for url placeholder
110110
*
111-
* @param {string} report_url A report url that contains shake_id placeholder
112-
* @return {function} Open the report based on shake_id in a new tab
111+
* @param {string} report_url A report url that contains event_id placeholder
112+
* @return {function} Open the report based on event_id in a new tab
113113
*/
114114
function createShowReportHandler(report_url) {
115115
var showReportHandler = function (id) {
116116
var url = report_url;
117-
// replace magic number 000 with shake_id
117+
// replace magic number
118118
function createFindWithId(id){
119119
return function (event) {
120120
return event.properties.id == id;
@@ -171,6 +171,54 @@ function createShowReportHandler(report_url) {
171171
return showReportHandler;
172172
}
173173

174+
/**
175+
* Closure to create handler for downloadReport
176+
* use magic number for url placeholder
177+
*
178+
* @param {string} report_url A report url that contains event_id placeholder
179+
* @return {function} Open the report based on event_id in a new tab
180+
*/
181+
function createDownloadReportHandler(report_url) {
182+
var downloadReportHandler = function (id) {
183+
var url = report_url;
184+
// replace magic number 000 with shake_id
185+
function createFindWithId(id){
186+
return function (event) {
187+
return event.properties.id == id;
188+
}
189+
}
190+
var findWithId = createFindWithId(id);
191+
var feature = event_json.features.find(findWithId);
192+
var volcano_name = feature.properties.volcano.volcano_name;
193+
var event_time = feature.properties.event_time;
194+
var event_time_string = moment(event_time).format('YYYYMMDDHHmmssZZ');
195+
var event_id_formatted = feature.properties.event_id_formatted;
196+
var task_status = feature.properties.task_status;
197+
if(task_status == 'PENDING'){
198+
alert("Report is currently being generated. Refresh this page later.");
199+
return;
200+
}
201+
else if(task_status == 'FAILED'){
202+
alert("Report failed to generate.");
203+
return;
204+
}
205+
url = url.replace('VOLCANOTEMPLATENAME', volcano_name)
206+
.replace('1234567890123456789', event_time_string);
207+
$.get(url, function (data) {
208+
if (data && data.report_map) {
209+
var pdf_url = data.report_map;
210+
SaveToDisk(pdf_url, event_id_formatted+'-'+data.language+'.pdf');
211+
}
212+
}).fail(function(e){
213+
console.log(e);
214+
if(e.status == 404){
215+
alert("No Report recorded for this event.");
216+
}
217+
});
218+
};
219+
return downloadReportHandler;
220+
}
221+
174222

175223
/**
176224
* Create Action Writer based on button_templates
@@ -200,17 +248,64 @@ function createActionRowWriter(button_templates, date_format) {
200248
var $span = $('<span></span>');
201249
for (var i = 0; i < button_templates.length; i++) {
202250
var button = button_templates[i];
203-
var $inner_button = $('<span></span>');
204-
$inner_button.addClass('row-action-icon')
205-
$inner_button.addClass(button.css_class);
206-
$inner_button.attr('title', button.name);
207-
$inner_button.text(button.label);
208-
var $button = $('<button></button>');
209-
$button.addClass('btn btn-primary row-action-container');
210-
$button.attr('title', button.name);
211-
$button.attr('onclick', button.handler + "('" + record.id + "')");
212-
$button.append($inner_button);
213-
$span.append($button);
251+
if(button.type == 'simple-button') {
252+
var $inner_button = $('<span></span>');
253+
$inner_button.addClass('row-action-icon');
254+
$inner_button.addClass(button.css_class);
255+
$inner_button.attr('title', button.name);
256+
var $button = $('<button></button>');
257+
$button.addClass('btn btn-primary row-action-container');
258+
$button.attr('title', button.name);
259+
$button.attr('onclick', button.handler + "('" + record.id + "')");
260+
$button.append($inner_button);
261+
$span.append($button);
262+
}
263+
else if(button.type == 'dropdown'){
264+
var $button = $('<button></button>');
265+
$button.addClass('btn btn-primary dropdown-toggle row-action-container');
266+
$button.attr('title', button.name);
267+
$button.attr('data-toggle', 'dropdown');
268+
$button.attr('aria-haspopup', 'true');
269+
$button.attr('aria-expanded', 'false');
270+
var $inner_button = $('<span></span>');
271+
$inner_button.addClass('row-action-icon');
272+
$inner_button.addClass(button.css_class);
273+
$inner_button.attr(button.name);
274+
$button.append($inner_button);
275+
var $menu = $('<ul></ul>');
276+
$menu.addClass('dropdown-menu');
277+
for(var j=0;j < button.actions.length;j++){
278+
var action = button.actions[j];
279+
if(action.active && $.isFunction(action.active) && !action.active(record)){
280+
continue;
281+
}
282+
var $li = $('<li></li>');
283+
var $action = $('<a></a>');
284+
if(action.href == undefined){
285+
$action.attr('href', '#');
286+
}
287+
else if($.isFunction(action.href)){
288+
$action.attr('href', action.href(record));
289+
}
290+
291+
if(action.download && $.isFunction(action.download)){
292+
$action.attr('download', action.download(record));
293+
}
294+
295+
if(action.handler){
296+
$action.attr('onclick', action.handler + "('" + record.id + "')");
297+
}
298+
299+
$action.text(action.text);
300+
$li.append($action);
301+
$menu.append($li);
302+
}
303+
var $group = $('<div></div>');
304+
$group.addClass('btn-group');
305+
$group.append($button);
306+
$group.append($menu);
307+
$span.append($group);
308+
}
214309
}
215310
tr += '<td>' + $span.html() + '</td>';
216311

0 commit comments

Comments
 (0)