diff --git a/tigacrafting/views.py b/tigacrafting/views.py
index 315e58e51..51477b363 100644
--- a/tigacrafting/views.py
+++ b/tigacrafting/views.py
@@ -1310,20 +1310,7 @@ def get_reports_unfiltered_sites_other(reports_imbornal):
return new_reports_unfiltered_sites_other
def get_reports_imbornal():
- reports_imbornal = ReportResponse.objects.filter(
- Q(question='Is this a storm drain or sewer?', answer='Yes') | Q(question=u'\xc9s un embornal o claveguera?',
- answer=u'S\xed') | Q(
- question=u'\xbfEs un imbornal o alcantarilla?', answer=u'S\xed') | Q(question='Selecciona lloc de cria',
- answer='Embornals') | Q(
- question='Selecciona lloc de cria', answer='Embornal o similar') | Q(question='Tipo de lugar de cría',
- answer='Sumidero o imbornal') | Q(
- question='Tipo de lugar de cría', answer='Sumideros') | Q(question='Type of breeding site',
- answer='Storm drain') | Q(
- question='Type of breeding site', answer='Storm drain or similar receptacle')).values('report').distinct()
-
- reports_imbornal_new = ReportResponse.objects.filter(question_id=12).filter(answer_id=121).values('report').distinct()
-
- return reports_imbornal | reports_imbornal_new
+ return Report.objects.filter(breeding_site_type=Report.BREEDING_SITE_TYPE_STORM_DRAIN)
def get_reports_unfiltered_adults_except_being_validated():
new_reports_unfiltered_adults = Report.objects.exclude(creation_time__year=2014).exclude(type='site').exclude(note__icontains='#345').exclude(photos=None).annotate(n_annotations=Count('expert_report_annotations')).filter(n_annotations=0).order_by('-server_upload_time')
diff --git a/tigamap/templates/tigamap/report_map.html b/tigamap/templates/tigamap/report_map.html
index f25502349..e1aefac0d 100644
--- a/tigamap/templates/tigamap/report_map.html
+++ b/tigamap/templates/tigamap/report_map.html
@@ -110,7 +110,7 @@
'{{ report.tigaprob_text }}
| cc score: | {{ score|floatformat:2 }} |
| lat: | {{ lat }} |
| lon: | {{ lon }} |
{{ report.get_validated_photo_html | safe }}';
{% else %}
popup_text = '| {% trans 'type' %}: | '+
- '{{ report.site_type_trans }} |
| cc score: | {{ score|floatformat:2 }} |
| lat: | {{ lat }} |
| lon: | {{ report.lon }} |
{{ report.get_validated_photo_html | safe }}';
+ '{{ report.breeding_site_type }}| cc score: | {{ score|floatformat:2 }} |
| lat: | {{ lat }} |
| lon: | {{ report.lon }} |
{{ report.get_validated_photo_html | safe }}';
{% endif %}
{% endwith %}
@@ -123,7 +123,7 @@
'{{ report.tigaprob_text }}| lat: | {{ lat }} |
| lon: | {{ lon }} |
';
{% else %}
popup_text = '| {% trans 'type' %}: | '+
- '{{ report.site_type_trans }} |
| lat: | {{ lat }} |
| lon: | {{ report.lon }} |
';
+ '{{ report.breeding_site_type }}| lat: | {{ lat }} |
| lon: | {{ report.lon }} |
';
{% endif %}
{% endif %}
{% if detailed == 'detailed' %}
diff --git a/tigascoring/xp_scoring.py b/tigascoring/xp_scoring.py
index 5574b36b4..e88bd642c 100644
--- a/tigascoring/xp_scoring.py
+++ b/tigascoring/xp_scoring.py
@@ -22,23 +22,12 @@
SITE_STORM_DRAIN_REWARD = 4
BREEDING_SITE_MOSQUITO_REWARD = 3
-MOSQUITO_HOW_LOOKS_QUESTION_ID = 7
-MOSQUITO_THORAX_ANSWER_IDS = [711, 712, 713, 714]
-MOSQUITO_ABDOMEN_ANSWER_IDS = [721, 722, 723, 724]
-MOSQUITO_LEG_ANSWER_IDS = [731, 732, 733, 734]
-
MOSQUITO_BITE_QUESTION_ID = 8
MOSQUITO_BITE_ANSWER_ID = 101
BREEDING_SITE_MOSQUITO_QUESTION_ID = 11
BREEDING_SITE_MOSQUITO_ANSWER_ID = 101
-SITE_WATER_QUESTION_ID = 10
-SITE_WATER_ANSWER_IDS = [81, 101]
-
-STORM_DRAIN_QUESTION_ID = 12
-STORM_DRAIN_ANSWER_ID = 121
-
CULEX_CATEGORY_ID = 10
AEDES_CATEGORY_IDS = [4, 5, 6, 7]
@@ -88,53 +77,31 @@
def is_thorax_answered(report):
- for response in report.responses.all():
- if response.question_id == MOSQUITO_HOW_LOOKS_QUESTION_ID and response.answer_id in MOSQUITO_THORAX_ANSWER_IDS:
- return True
- return False
+ return report.user_perceived_mosquito_thorax is not None
def is_abdomen_answered(report):
- for response in report.responses.all():
- if response.question_id == MOSQUITO_HOW_LOOKS_QUESTION_ID and response.answer_id in MOSQUITO_ABDOMEN_ANSWER_IDS:
- return True
- return False
+ return report.user_perceived_mosquito_abdomen is not None
def is_leg_answered(report):
- for response in report.responses.all():
- if response.question_id == MOSQUITO_HOW_LOOKS_QUESTION_ID and response.answer_id in MOSQUITO_LEG_ANSWER_IDS:
- return True
- return False
+ return report.user_perceived_mosquito_legs is not None
def is_water_answered(report):
- for response in report.responses.all():
- if response.question_id == SITE_WATER_QUESTION_ID and response.answer_id in SITE_WATER_ANSWER_IDS:
- return True
- return False
+ return report.breeding_site_has_water is not None
def is_bite_report_followed(report):
- for response in report.responses.all():
- if response.question_id == MOSQUITO_BITE_QUESTION_ID and response.answer_id == MOSQUITO_BITE_ANSWER_ID:
- return True
- return False
+ return report.responses.filter(question_id=MOSQUITO_BITE_QUESTION_ID, answer_id=MOSQUITO_BITE_ANSWER_ID).exists()
def is_mosquito_report_followed(report):
- for response in report.responses.all():
- if response.question_id == BREEDING_SITE_MOSQUITO_QUESTION_ID and response.answer_id == BREEDING_SITE_MOSQUITO_ANSWER_ID:
- return True
- return False
+ return report.responses.filter(question_id=BREEDING_SITE_MOSQUITO_QUESTION_ID, answer_id=BREEDING_SITE_MOSQUITO_ANSWER_ID).exists()
def is_storm_drain(report):
- for response in report.responses.all():
- if response.question_id == STORM_DRAIN_QUESTION_ID and response.answer_id == STORM_DRAIN_ANSWER_ID:
- return True
- return False
-
+ return report.breeding_site_type == Report.BREEDING_SITE_TYPE_STORM_DRAIN
def is_culex(validation_result):
if validation_result['category'] is not None:
diff --git a/tigaserver_app/admin.py b/tigaserver_app/admin.py
index 47ca08c34..5af5c615e 100644
--- a/tigaserver_app/admin.py
+++ b/tigaserver_app/admin.py
@@ -7,6 +7,7 @@
from django.utils.encoding import smart_str
from django.http.response import HttpResponse
from django.utils.html import mark_safe
+from django.utils.translation import ugettext_lazy as _
def export_full_csv(modeladmin, request, queryset):
@@ -116,16 +117,147 @@ class PhotoInline(admin.StackedInline):
class ReportAdmin(admin.ModelAdmin):
- list_display = ('version_UUID', 'report_id', 'deleted', 'user', 'version_number', 'creation_time', 'version_time', 'type', 'mission',
- 'package_version', 'os', 'n_photos', 'map_link', 'movelab_score', 'crowd_score')
- inlines = [ReportResponseInline, PhotoInline]
- ordering = ('creation_time', 'report_id', 'version_number')
- readonly_fields = ('deleted', 'version_UUID', 'user', 'report_id', 'version_number', 'other_versions_of_this_report', 'creation_time', 'version_time', 'server_upload_time', 'updated_at', 'datetime_fix_offset', 'phone_upload_time', 'type', 'mission', 'location_choice', 'current_location_lon', 'current_location_lat', 'selected_location_lon', 'selected_location_lat', 'note', 'package_name', 'package_version', 'device_manufacturer', 'device_model', 'os', 'os_version', 'os_language', 'app_language', 'n_photos', 'lon', 'lat', 'tigaprob', 'tigaprob_text', 'site_type', 'site_type_trans', 'embornals', 'fonts', 'basins', 'buckets', 'wells', 'other', 'masked_lat', 'masked_lon', 'map_link', 'movelab_score', 'crowd_score')
- fields = ('hide', 'deleted', 'map_link', 'version_UUID', 'user', 'report_id', 'version_number', 'other_versions_of_this_report', ('creation_time', 'version_time', 'datetime_fix_offset'), ('server_upload_time','phone_upload_time'), 'updated_at', 'type', 'mission', 'location_choice', 'current_location_lon', 'current_location_lat', 'selected_location_lon', 'selected_location_lat', 'note', 'package_name', 'package_version', 'device_manufacturer', 'device_model', 'os', 'os_version', 'os_language', 'app_language', 'n_photos', 'lon', 'lat', 'tigaprob', 'tigaprob_text', 'site_type', 'site_type_trans', 'embornals', 'fonts', 'basins', 'buckets', 'wells', 'other', 'masked_lat', 'masked_lon', 'movelab_score', 'crowd_score')
- search_fields = ('version_UUID',)
+ list_display = (
+ 'version_UUID', 'report_id', 'deleted', 'user', 'version_number', 'creation_time', 'version_time', 'type', 'mission',
+ 'package_version', 'os', 'n_photos'
+ )
list_filter = ['os', 'type', 'mission', 'package_name', 'package_version']
+ search_fields = ('version_UUID',)
+
+ inlines = [ReportResponseInline, PhotoInline]
actions = [export_full_csv, export_full_csv_sc]
+ readonly_fields = [
+ "deleted",
+ "report_id",
+ "version_number",
+ "type",
+ "user",
+ "mission",
+ "session",
+ "server_upload_time",
+ "updated_at",
+ "version_time",
+ "phone_upload_time",
+ "creation_time",
+ "other_versions_of_this_report"
+ ]
+
+ fieldsets = [
+ (
+ _('General info'),
+ {
+ "fields": [
+ ("report_id", "version_number"),
+ ("hide", "deleted"),
+ "type",
+ "user",
+ ("mission","session"),
+ "other_versions_of_this_report",
+ ("server_upload_time", "updated_at"),
+ ("version_time", "datetime_fix_offset"),
+ ("creation_time", "phone_upload_time")
+ ]
+ }
+ ),
+ (
+ _("Location information"),
+ {
+ "fields": [
+ ("country", "nuts_2", "nuts_3"),
+ "location_choice",
+ "point"
+ ]
+ }
+ ),
+ (
+ _("Other"),
+ {
+ "fields": [
+ ("package_name", "package_version", "app_language"),
+ ("device_manufacturer", "device_model"),
+ ("os", "os_version", "os_language"),
+ "note"
+ ],
+ "classes": ["collapse",]
+ }
+ )
+ ]
+
+ def get_readonly_fields(self, request, obj=None):
+ # Only allow to edit 'hide' field.
+ result = super().get_readonly_fields(request, obj)
+
+ readonly_fields = [field.name for field in self.model._meta.get_fields()]
+ allow_edit_fields = ['hide',]
+
+ for field_name in readonly_fields:
+ if not field_name in allow_edit_fields:
+ result.append(field_name)
+
+ return result
+
+ def get_fieldsets(self, request, obj = None):
+ result = super().get_fieldsets(request, obj)
+
+ if not obj:
+ return result
+
+ extra_fieldsets = []
+ if obj.type == Report.TYPE_ADULT:
+ extra_fieldsets.append(
+ (
+ _("Classification"),
+ {
+ "fields": [
+ "ia_filter_1", "ia_filter_2"
+ ]
+ }
+ )
+ )
+ extra_fieldsets.append(
+ (
+ _("Specific information"),
+ {
+ "fields": [
+ ("event_environment", "event_moment"),
+ "user_perceived_mosquito_specie",
+ ("user_perceived_mosquito_thorax", "user_perceived_mosquito_abdomen", "user_perceived_mosquito_legs")
+ ]
+ }
+ )
+ )
+ elif obj.type == Report.TYPE_BITE:
+ extra_fieldsets.append(
+ (
+ _("Specific information"),
+ {
+ "fields": [
+ ("event_environment", "event_moment"),
+ "bite_count",
+ ("head_bite_count", "left_arm_bite_count", "right_arm_bite_count", "chest_bite_count", "left_leg_bite_count", "right_leg_bite_count")
+ ]
+ }
+ )
+ )
+ elif obj.type == Report.TYPE_SITE:
+ extra_fieldsets.append(
+ (
+ _("Specific information"),
+ {
+ "fields": [
+ "breeding_site_type",
+ "breeding_site_has_water",
+ "breeding_site_in_public_area",
+ "breeding_site_has_near_mosquitoes",
+ "breeding_site_has_larvae"
+ ]
+ }
+ )
+ )
+
+ return result + extra_fieldsets
+
def has_add_permission(self, request):
return False
@@ -133,26 +265,15 @@ def has_delete_permission(self, request, obj=None):
return False
def other_versions_of_this_report(self, obj):
- result = []
+ result = ""
for this_version in obj.other_versions:
result += 'Version %s ' % (
this_version.version_UUID,
this_version.version_number,
)
- return result
+ return mark_safe(result)
other_versions_of_this_report.allow_tags = True
- def movelab_score(self, obj):
- return obj.movelab_score
-
- def crowd_score(self, obj):
- return obj.crowd_score
-
- def map_link(self, obj):
- return 'Show map' % obj.version_UUID
- map_link.allow_tags = True
-
-
def export_csv_photo(modeladmin, request, queryset):
response = HttpResponse(mimetype='text/csv')
response['Content-Disposition'] = 'attachment; filename=tigatrapp_photos.csv'
@@ -247,7 +368,7 @@ def other_report_versions(self, obj):
this_version.version_UUID,
this_version.version_number,
)
- return result
+ return mark_safe(result)
other_report_versions.allow_tags = True
def map_link(self, obj):
diff --git a/tigaserver_app/migrations/0048_report_responses_to_column.py b/tigaserver_app/migrations/0048_report_responses_to_column.py
new file mode 100644
index 000000000..c69fdf5c1
--- /dev/null
+++ b/tigaserver_app/migrations/0048_report_responses_to_column.py
@@ -0,0 +1,103 @@
+# Generated by Django 2.2.7 on 2024-02-12 15:12
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('tigaserver_app', '0047_merge_20240813_1025'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='report',
+ name='breeding_site_has_larvae',
+ field=models.BooleanField(help_text='Either if the user perceived larvaes the breeding site.', null=True),
+ ),
+ migrations.AddField(
+ model_name='report',
+ name='breeding_site_has_near_mosquitoes',
+ field=models.BooleanField(help_text='Either if the user perceived mosquitoes near the breeding site (less than 10 meters).', null=True),
+ ),
+ migrations.AddField(
+ model_name='report',
+ name='breeding_site_has_water',
+ field=models.BooleanField(help_text='Either if the user perceived water in the breeding site.', null=True),
+ ),
+ migrations.AddField(
+ model_name='report',
+ name='breeding_site_in_public_area',
+ field=models.BooleanField(help_text='Either if the breeding site is found in a public area.', null=True),
+ ),
+ migrations.AddField(
+ model_name='report',
+ name='breeding_site_type',
+ field=models.CharField(choices=[('basin', 'Basin'), ('bucket', 'Bucket'), ('fountain', 'Fountain'), ('small_container', 'Small container'), ('storm_drain', 'Storm Drain'), ('well', 'Well'), ('other', 'Other')], help_text='Breeding site type.', max_length=32, null=True),
+ ),
+ migrations.AddField(
+ model_name='report',
+ name='chest_bite_count',
+ field=models.PositiveSmallIntegerField(help_text='Number of bites reported in the chest.', null=True),
+ ),
+ migrations.AddField(
+ model_name='report',
+ name='event_environment',
+ field=models.CharField(choices=[('indoors', 'Indoors'), ('outdoors', 'Outdoors'), ('vehicle', 'Inside vehicle')], help_text='The environment where the event took place.', max_length=16, null=True),
+ ),
+ migrations.AddField(
+ model_name='report',
+ name='event_moment',
+ field=models.CharField(choices=[('now', 'Now'), ('last_morning', 'Last morning'), ('last_midday', 'Last midday'), ('last_afternoon', 'Last afternoon'), ('last_night', 'Last night')], help_text='The moment of the day when the event took place.', max_length=32, null=True),
+ ),
+ migrations.AddField(
+ model_name='report',
+ name='head_bite_count',
+ field=models.PositiveSmallIntegerField(help_text='Number of bites reported in the head.', null=True),
+ ),
+ migrations.AddField(
+ model_name='report',
+ name='left_arm_bite_count',
+ field=models.PositiveSmallIntegerField(help_text='Number of bites reported in the left arm.', null=True),
+ ),
+ migrations.AddField(
+ model_name='report',
+ name='left_leg_bite_count',
+ field=models.PositiveSmallIntegerField(help_text='Number of bites reported in the left leg.', null=True),
+ ),
+ migrations.AddField(
+ model_name='report',
+ name='right_arm_bite_count',
+ field=models.PositiveSmallIntegerField(help_text='Number of bites reported in the right arm.', null=True),
+ ),
+ migrations.AddField(
+ model_name='report',
+ name='right_leg_bite_count',
+ field=models.PositiveSmallIntegerField(help_text='Number of bites reported in the right leg.', null=True),
+ ),
+ migrations.AddField(
+ model_name='report',
+ name='user_perceived_mosquito_abdomen',
+ field=models.CharField(choices=[('albopictus', 'Aedes albopictus'), ('aegypti', 'Aedes aegypti'), ('japonicus', 'Aedes japonicus'), ('koreicus', 'Aedes koreicus'), ('culex', 'Culex pipiens'), ('other', 'Other')], help_text='The species of mosquito that the abdomen resembles, according to the user.', max_length=16, null=True),
+ ),
+ migrations.AddField(
+ model_name='report',
+ name='user_perceived_mosquito_legs',
+ field=models.CharField(choices=[('albopictus', 'Aedes albopictus'), ('aegypti', 'Aedes aegypti'), ('japonicus', 'Aedes japonicus'), ('koreicus', 'Aedes koreicus'), ('culex', 'Culex pipiens'), ('other', 'Other')], help_text='The species of mosquito that the leg resembles, according to the user.', max_length=16, null=True),
+ ),
+ migrations.AddField(
+ model_name='report',
+ name='user_perceived_mosquito_thorax',
+ field=models.CharField(choices=[('albopictus', 'Aedes albopictus'), ('aegypti', 'Aedes aegypti'), ('japonicus', 'Aedes japonicus'), ('koreicus', 'Aedes koreicus'), ('culex', 'Culex pipiens'), ('other', 'Other')], help_text='The species of mosquito that the thorax resembles, according to the user.', max_length=16, null=True),
+ ),
+ migrations.AddField(
+ model_name='report',
+ name='user_perceived_mosquito_specie',
+ field=models.CharField(choices=[('albopictus', 'Aedes albopictus'), ('aegypti', 'Aedes aegypti'), ('japonicus', 'Aedes japonicus'), ('koreicus', 'Aedes koreicus'), ('culex', 'Culex pipiens'), ('other', 'Other')], help_text='The mosquito specie perceived by the user.', max_length=16, null=True),
+ ),
+ migrations.AddField(
+ model_name='report',
+ name='bite_count',
+ field=models.PositiveSmallIntegerField(editable=False, help_text='Total number of bites reported.', null=True),
+ ),
+ ]
diff --git a/tigaserver_app/migrations/0049_populate_responses_column.py b/tigaserver_app/migrations/0049_populate_responses_column.py
new file mode 100644
index 000000000..b5c4f90fb
--- /dev/null
+++ b/tigaserver_app/migrations/0049_populate_responses_column.py
@@ -0,0 +1,649 @@
+# Generated by Django 2.2.7 on 2024-02-12 15:12
+
+from typing import Tuple
+from django.db import migrations, models
+from django.db.models.functions import Cast, Coalesce
+
+
+def update_field_based_on_responses(apps, fieldname, responses: Tuple[str, str], value):
+ def responses_list_to_Q(responses: Tuple[str, str], connector: str =models.Q.OR):
+ return models.Q(
+ *[
+ models.Q(question=question, answer=answer)
+ for question, answer in responses
+ ],
+ _connector=connector
+ )
+
+ # Loading models
+ Report = apps.get_model("tigaserver_app", "Report")
+ ReportResponse = apps.get_model("tigaserver_app", "ReportResponse")
+
+ responses_subquery = ReportResponse.objects.filter(report=models.OuterRef('pk')).values('report')
+
+ subquery = responses_subquery.filter(responses_list_to_Q(responses=responses))
+ Report.objects.filter(pk__in=models.Subquery(subquery)).update(**{fieldname: value})
+
+def update_boolean_field_based_on_responses(apps, fieldname, responses_dict):
+
+ if False in responses_dict.keys():
+ update_field_based_on_responses(apps=apps, fieldname=fieldname, responses=responses_dict[False], value=False)
+
+ if True in responses_dict.keys():
+ update_field_based_on_responses(apps=apps, fieldname=fieldname, responses=responses_dict[True], value=True)
+
+def populate_breeding_site_has_larvae_field(apps, schema_editor):
+ responses_dict = {
+ True: [
+ ("¿Contiene agua estancada y/o larvas de mosquito?", "Hay agua y veo larvas de mosquito"),
+ ("Contiene agua estancada y/o larvas o pupas de mosquito (cualquier especie)?", "Sí, agua y larvas o pupas."),
+ ("Conté aigua estancada i/o larves de mosquit?", "Hi ha aigua i veig larves de mosquit"),
+ ("Does it contain stagnant water and/or mosquito larvae?", "Has stagnant water with larvae"),
+ ("¿Contiene larvas o pupas de mosquito (de cualquier especie)?", "Sí"),
+ ("Conté larves o pupes de mosquit (de qualsevol espècie)?", "Sí"),
+ ("Conté aigua estancada y/o larves o pupes de mosquit (qualsevol espècie)?", "Sí, aigua i larves o pupes."),
+ ("Does it contain stagnant water and/or mosquito larvae or pupae (any mosquito species)?", "Yes, stagnant water with larvae or pupae."),
+ ("Conté aigua estancada i/o larves o pupes de mosquit (qualsevol espècie)?", "Sí, aigua i larves o pupes."),
+ ("Have you seen mosquito larvae (not necessarily tiger mosquito) inside?", "Yes"),
+ ],
+ False: [
+ ("¿Contiene agua estancada y/o larvas de mosquito?", "Hay agua pero no veo larvas de mosquito"),
+ ("Conté aigua estancada i/o larves de mosquit?", "Hi ha aigua però no veig larves de mosquit"),
+ ("Contiene agua estancada y/o larvas o pupas de mosquito (cualquier especie)?", "Sí, agua sin larvas ni pupas."),
+ ("Does it contain stagnant water and/or mosquito larvae?", "Has stagnant water, but with no larvae"),
+ ("Have you seen mosquito larvae (not necessarily tiger mosquito) inside?", "No"),
+ ("Conté aigua estancada y/o larves o pupes de mosquit (qualsevol espècie)?", "Sí, aigua sense larves ni pupes."),
+ ("Conté aigua estancada y/o larves o pupes de mosquit (qualsevol espècie)?", "No, ni aigua ni larves o pupes."),
+ ("¿Contiene larvas o pupas de mosquito (de cualquier especie)?", "No"),
+ ("這繁殖地有沒有藏有積水,和/或任何蚊子品種旳幼蟲或蛹?", "有積水,但沒有幼蟲或蛹"),
+ ("Conté larves o pupes de mosquit (de qualsevol espècie)?", "No"),
+ ("Conté aigua estancada i/o larves o pupes de mosquit (qualsevol espècie)?", "Sí, aigua sense larves ni pupes."),
+ ("Does it contain stagnant water and/or mosquito larvae or pupae (any mosquito species)?", "No, neither stagnant water nor larve nor pupae."),
+ ("Does it contain stagnant water and/or mosquito larvae or pupae (any mosquito species)?", "Has stagnant water, but no larvae or pupae."),
+ ("Does it contain stagnant water and/or mosquito larvae or pupae (any mosquito species)?", "Yes, stagnant water, but no larvae or pupae."),
+ ("這繁殖地有沒有藏有積水,和/或任何蚊子品種旳幼蟲或蛹?", "有積水,但沒有幼蟲或蛹。"),
+ ]
+ }
+
+ update_boolean_field_based_on_responses(
+ apps=apps,
+ fieldname='breeding_site_has_larvae',
+ responses_dict=responses_dict
+ )
+
+
+def populate_breeding_site_has_near_mosquitoes(apps, schema_editor):
+ responses_dict = {
+ True: [
+ ("¿Has visto mosquitos cerca (a menos de 10 metros)?", "Sí"),
+ ("¿Has visto mosquitos cerca (a <10 metros)?", "Sí"),
+ ("Has vist mosquits a prop (a menys de 10 metres)?", "Sí"),
+ ("Have you seen mosquitoes nearby (<10 meters)?", "Yes"),
+ ("Has vist mosquits a prop (a <10metres)?", "Sí"),
+ ("Have you seen adult mosquitoes nearby (<10 meters)?", "Yes"),
+ ("你有否在周遭地方見到成年蚊子(少於十米內)?", "是"),
+ ],
+ False: [
+ ("¿Has visto mosquitos cerca (a menos de 10 metros)?", "No"),
+ ("Has vist mosquits a prop (a menys de 10 metres)?", "No"),
+ ("Have you seen mosquitoes nearby (<10 meters)?", "No"),
+ ("¿Has visto mosquitos cerca (a <10 metros)?", "No"),
+ ("Has vist mosquits a prop (a <10metres)?", "No"),
+ ("Have you seen adult mosquitoes nearby (<10 meters)?", "No"),
+ ("你有否在周遭地方見到成年蚊子(少於十米內)?", "否"),
+ ("Have you seen adult mosquitoes around the breeding site?", "No"),
+ ],
+ }
+
+ update_boolean_field_based_on_responses(
+ apps=apps,
+ fieldname='breeding_site_has_near_mosquitoes',
+ responses_dict=responses_dict
+ )
+
+def populate_breeding_site_has_water(apps, schema_editor):
+ responses_dict = {
+ True: [
+ ("question_10", "question_10_answer_101"),
+ ("question_17", "question_10_answer_101"),
+ ("¿Contiene agua estancada y/o larvas de mosquito?", "Hay agua pero no veo larvas de mosquito"),
+ ("¿Contiene agua estancada y/o larvas de mosquito?", "Hay agua y veo larvas de mosquito"),
+ ("Conté aigua estancada i/o larves de mosquit?", "Hi ha aigua però no veig larves de mosquit"),
+ ("Contiene agua estancada y/o larvas o pupas de mosquito (cualquier especie)?", "Sí, agua y larvas o pupas."),
+ ("¿Contiene agua estancada?", "Sí"),
+ ("Conté aigua estancada i/o larves de mosquit?", "Hi ha aigua i veig larves de mosquit"),
+ ("Contiene agua estancada y/o larvas o pupas de mosquito (cualquier especie)?", "Sí, agua sin larvas ni pupas."),
+ ("Does it contain stagnant water and/or mosquito larvae?", "Has stagnant water with larvae"),
+ ("Does it contain stagnant water and/or mosquito larvae?", "Has stagnant water, but with no larvae"),
+ ("Conté aigua estancada?", "Sí"),
+ ("Does it have stagnant water inside?", "Yes"),
+ ("Conté aigua estancada y/o larves o pupes de mosquit (qualsevol espècie)?", "Sí, aigua i larves o pupes."),
+ ("Does it contain stagnant water and/or mosquito larvae or pupae (any mosquito species)?", "Yes, stagnant water with larvae or pupae."),
+ ("Conté aigua estancada i/o larves o pupes de mosquit (qualsevol espècie)?", "Sí, aigua i larves o pupes."),
+ ("Conté aigua estancada y/o larves o pupes de mosquit (qualsevol espècie)?", "Sí, aigua sense larves ni pupes."),
+ ("這繁殖地有沒有藏有積水,和/或任何蚊子品種旳幼蟲或蛹?", "有積水,但沒有幼蟲或蛹"),
+ ("Conté aigua estancada i/o larves o pupes de mosquit (qualsevol espècie)?", "Sí, aigua sense larves ni pupes."),
+ ("Does it contain stagnant water and/or mosquito larvae or pupae (any mosquito species)?", "Has stagnant water, but no larvae or pupae."),
+ ("Does it contain stagnant water and/or mosquito larvae or pupae (any mosquito species)?", "Yes, stagnant water, but no larvae or pupae."),
+ ("¿Contiene agua?", "Sí"),
+ ("這繁殖地有沒有藏有積水,和/或任何蚊子品種旳幼蟲或蛹?", "有積水,但沒有幼蟲或蛹。"),
+ ],
+ False: [
+ ("Conté aigua estancada y/o larves o pupes de mosquit (qualsevol espècie)?", "No, ni aigua ni larves o pupes."),
+ ("Conté aigua estancada i/o larves o pupes de mosquit (qualsevol espècie)?", "No, no conté aigua."),
+ ("這繁殖地有沒有藏有積水,和/或任何蚊子品種旳幼蟲或蛹?", "沒有積水"),
+ ("Does it contain stagnant water and/or mosquito larvae or pupae (any mosquito species)?", "No, neither stagnant water nor larve nor pupae."),
+ ("Conté aigua estancada?", "No"),
+ ("Does it have stagnant water inside?", "No"),
+ ("Does it contain stagnant water and/or mosquito larvae or pupae (any mosquito species)?", "Does not contain stagnant water."),
+ ("¿Contiene agua estancada y/o larvas de mosquito?", "No hay agua"),
+ ("¿Contiene agua estancada?", "No"),
+ ("Conté aigua estancada i/o larves de mosquit?", "No hi ha aigua"),
+ ("Contiene agua estancada y/o larvas o pupas de mosquito (cualquier especie)?", "No, no contiene agua."),
+ ("Does it contain stagnant water and/or mosquito larvae?", "Does not contain stagnant water"),
+ ("question_10", "question_10_answer_102"),
+ ("question_17", "question_10_answer_102"),
+ ],
+ }
+
+ update_boolean_field_based_on_responses(
+ apps=apps,
+ fieldname='breeding_site_has_water',
+ responses_dict=responses_dict
+ )
+
+def populate_breeding_site_in_public_area(apps, schema_editor):
+ responses_dict = {
+ True: [
+ ("¿Se encuentra en la vía pública?", "Sí"),
+ ("Es troba a la via pública?", "Sí"),
+ ("Is it in a public area?", "Yes"),
+ ("這繁殖地是否公共空間?", "是"),
+ ],
+ False: [
+ ("¿Se encuentra en la vía pública?", "No"),
+ ("Es troba a la via pública?", "No"),
+ ("Is it in a public area?", "No"),
+ ("這繁殖地是否公共空間?", "否"),
+ ],
+ }
+
+ update_boolean_field_based_on_responses(
+ apps=apps,
+ fieldname='breeding_site_in_public_area',
+ responses_dict=responses_dict
+ )
+
+def populate_breeding_site_type(apps, schema_editor):
+
+ from tigaserver_app.models import Report
+
+ # Basin
+ basin_responses = [
+ ("Selecciona lloc de cria", "Basses artificials petites"),
+ ("Selecciona lloc de cria", "Bassa petita o similar"),
+ ("Tipo de lugar de cría", "Balsa pequeña o similar"),
+ ("Tipo de lugar de cría", "Pequeñas balsas artificiales"),
+ ("Type of breeding site", "Basin"),
+ ("Type of breeding site", "Small basin or similar receptacle"),
+ ]
+ update_field_based_on_responses(apps=apps, fieldname='breeding_site_type', responses=basin_responses, value=Report.BREEDING_SITE_TYPE_BASIN)
+
+ # Bucket
+ bucket_responses = [
+ ("Tipo de lugar de cría", "Bidones"),
+ ("Selecciona lloc de cria", "Bidons"),
+ ("Type of breeding site", "Bucket"),
+ ]
+ update_field_based_on_responses(apps=apps, fieldname='breeding_site_type', responses=bucket_responses, value=Report.BREEDING_SITE_TYPE_BUCKET)
+
+ # Fountain
+ fountain_responses = [
+ ("Tipo de lugar de cría", "Fuente ornamental"),
+ ("Tipo de lugar de cría", "Fuentes"),
+ ("Selecciona lloc de cria", "Font ornamental"),
+ ("Selecciona lloc de cria", "Fonts"),
+ ("Type of breeding site", "Fountain"),
+ ]
+ update_field_based_on_responses(apps=apps, fieldname='breeding_site_type', responses=fountain_responses, value=Report.BREEDING_SITE_TYPE_FOUNTAIN)
+
+ # other
+ other_responses = [
+ ("question_12","question_12_answer_122"),
+ ("¿Es un imbornal o alcantarilla?","Otros"),
+ ("Tipo de lugar de cría","Otros"),
+ ("¿Es un imbornal o alcantarilla?","No"),
+ ("És un embornal o claveguera?","Altres"),
+ ("Is this a storm drain or sewer?","Other"),
+ ("Selecciona lloc de cria","Altres"),
+ ("Is this a storm drain or sewer?","No"),
+ ("És un embornal o claveguera?","No"),
+ ("Type of breeding site ","Other"),
+ ("這繁殖地是否排水渠或下水道?","其他"),
+ ("這繁殖地是否排水渠或下水道?","否"),
+ ]
+ update_field_based_on_responses(apps=apps, fieldname='breeding_site_type', responses=other_responses, value=Report.BREEDING_SITE_TYPE_OTHER)
+
+ # small_container
+ small_container_responses = [
+ ("Tipo de lugar de cría","Recipiente pequeño"),
+ ("Selecciona lloc de cria","Recipient petit"),
+ ("Type of breeding site","Small container"),
+ ]
+ update_field_based_on_responses(apps=apps, fieldname='breeding_site_type', responses=small_container_responses, value=Report.BREEDING_SITE_TYPE_SMALL_CONTAINER)
+
+ # storm drain
+ storm_drain_responses = [
+ ("question_12","question_12_answer_121"),
+ ("¿Es un imbornal o alcantarilla?","Sí"),
+ ("És un embornal o claveguera?","Sí"),
+ ("Is this a storm drain or sewer?","Yes"),
+ ("Tipo de lugar de cría","Sumidero o imbornal"),
+ ("Tipo de lugar de cría","Sumideros"),
+ ("Selecciona lloc de cria","Embornal o similar"),
+ ("Selecciona lloc de cria","Embornals"),
+ ("Type of breeding site","Storm drain"),
+ ("Type of breeding site","Storm drain or similar receptacle"),
+ ("這繁殖地是否排水渠或下水道?","是"),
+ ]
+ update_field_based_on_responses(apps=apps, fieldname='breeding_site_type', responses=storm_drain_responses, value=Report.BREEDING_SITE_TYPE_STORM_DRAIN)
+
+ # well
+ well_responses = [
+ ("Tipo de lugar de cría","Pozos"),
+ ("Selecciona lloc de cria","Pous"),
+ ]
+ update_field_based_on_responses(apps=apps, fieldname='breeding_site_type', responses=well_responses, value=Report.BREEDING_SITE_TYPE_WELL)
+
+def populate_event_environment(apps, schema_editor):
+ from tigaserver_app.models import Report
+
+ # indoors
+ indoors_responses = [
+ ("question_13","question_13_answer_132"),
+ ("question_4","question_4_answer_42"),
+ ]
+ update_field_based_on_responses(apps=apps, fieldname='event_environment', responses=indoors_responses, value=Report.EVENT_ENVIRONMENT_INDOORS)
+
+ # outdoors
+ outdoors_responses = [
+ ("question_13","question_13_answer_133"),
+ ("question_4","question_4_answer_43"),
+ ]
+ update_field_based_on_responses(apps=apps, fieldname='event_environment', responses=outdoors_responses, value=Report.EVENT_ENVIRONMENT_OUTDOORS)
+
+ # vehicle
+ vehicle_responses = [
+ ("question_13","question_13_answer_131"),
+ ("question_4","question_4_answer_41"),
+ ]
+ update_field_based_on_responses(apps=apps, fieldname='event_environment', responses=vehicle_responses, value=Report.EVENT_ENVIRONMENT_VEHICLE)
+
+def populate_event_moment(apps, schema_editor):
+ from tigaserver_app.models import Report
+
+ # now
+ now_responses = [
+ ("question_5","question_5_answer_51"),
+ ]
+ update_field_based_on_responses(apps=apps, fieldname='event_moment', responses=now_responses, value=Report.EVENT_MOMENT_NOW)
+
+ # last night
+ last_night_responses = [
+ ("question_3","question_3_answer_34"),
+ ]
+ update_field_based_on_responses(apps=apps, fieldname='event_moment', responses=last_night_responses, value=Report.EVENT_MOMENT_LAST_NIGHT)
+
+ # last afternoon
+ last_afternoon_responses = [
+ ("question_3","question_3_answer_33"),
+ ]
+ update_field_based_on_responses(apps=apps, fieldname='event_moment', responses=last_afternoon_responses, value=Report.EVENT_MOMENT_LAST_AFTERNOON)
+
+ # last midday
+ last_midday_responses = [
+ ("question_3","question_3_answer_32"),
+ ]
+ update_field_based_on_responses(apps=apps, fieldname='event_moment', responses=last_midday_responses, value=Report.EVENT_MOMENT_LAST_MIDDAY)
+
+ # last morning
+ last_morning_responses = [
+ ("question_3","question_3_answer_31"),
+ ]
+ update_field_based_on_responses(apps=apps, fieldname='event_moment', responses=last_morning_responses, value=Report.EVENT_MOMENT_LAST_MORNING)
+
+
+def populate_user_perceived_mosquito_abdomen(apps, schema_editor):
+ from tigaserver_app.models import Report
+
+ # albopictus responses
+ albopictus_responses = [
+ ("question_7","question_7_answer_721"),
+ ("¿Tiene rayas blancas en el abdomen y en las patas?", "Sí"),
+ ("¿Cómo es el abdomen del mosquito? Consulta el botón (i) y selecciona una respuesta:","Con líneas y manchas blancas finas"),
+ ("Com és l’abdomen del mosquit? Consulta el botó (i) i selecciona una resposta:","Amb línies i taques blanques fines"),
+ ("What does the abdomen of your mosquito look like? Check the (i) button and select an answer:","With white lines and thin white spots"),
+ ("¿Cómo es el abdomen de tu mosquito? Consulta el botón (i) y selecciona una respuesta:","Abdomen como Ae. albopictus"),
+ ("Does it have white stripes on the abdomen and legs?","Yes"),
+ ("¿Como era el mosquito?","question_7_answer_721"),
+ ("Com és l’abdomen del teu mosquit? Consulta el botó (i) i selecciona una resposta:","Abdomen com Ae. albopictus"),
+ ("What does the abdomen of your mosquito look like? Check the (i) button and select an answer:","Abdomen like a Ae. albopictus"),
+ ("蚊子的腹部是什麼樣子?檢查(i)按鈕,然後選擇一個答案:","有白色條紋和小斑點。"),
+ ("¿Como era el mosquito?","question_7_answer_721"),
+ ("蚊子的腹部是什麼樣子?檢查(i)按鈕,然後選擇一個答案:","有白色條紋和小斑點"),
+ ]
+
+ update_field_based_on_responses(apps=apps, fieldname='user_perceived_mosquito_abdomen', responses=albopictus_responses, value=Report.SPECIE_ALBOPICTUS)
+
+ # aegypti responses
+ aegypti_responses = [
+ ("question_7","question_7_answer_722"),
+ ("¿Cómo es el abdomen del mosquito? Consulta el botón (i) y selecciona una respuesta:","Con líneas y manchas blancas gruesas"),
+ ("Com és l’abdomen del mosquit? Consulta el botó (i) i selecciona una resposta:","Amb línies i taques blanques gruixudes"),
+ ("¿Cómo es el abdomen del mosquito? Consulta el botón (i) y selecciona una respuesta:","Con líneas y manchas blancas gruesas"),
+ ("What does the abdomen of your mosquito look like? Check the (i) button and select an answer:","With white lines and thick white spots"),
+ ("¿Cómo es el abdomen de tu mosquito? Consulta el botón (i) y selecciona una respuesta:","Abdomen como Ae. aegypti"),
+ ("¿Como era el mosquito?","question_7_answer_722"),
+ ("What does the abdomen of your mosquito look like? Check the (i) button and select an answer:","Abdomen like Ae. aegypti"),
+ ("Com és l’abdomen del teu mosquit? Consulta el botó (i) i selecciona una resposta:","Abdomen com Ae. aegypti"),
+ ("¿Como era el mosquito?","question_7_answer_722"),
+ ("蚊子的腹部是什麼樣子?檢查(i)按鈕,然後選擇一個答案:","有白色條紋和大斑點"),
+ ("蚊子的腹部是什麼樣子?檢查(i)按鈕,然後選擇一個答案:","有白色條紋和大斑點。"),
+ ]
+
+ update_field_based_on_responses(apps=apps, fieldname='user_perceived_mosquito_abdomen', responses=aegypti_responses, value=Report.SPECIE_AEGYPTI)
+
+ # japonicus responses
+ japonicus_responses = [
+ ("question_7","question_7_answer_723"),
+ ("¿Como era el mosquito?","question_7_answer_723"),
+ ]
+
+ update_field_based_on_responses(apps=apps, fieldname='user_perceived_mosquito_abdomen', responses=japonicus_responses, value=Report.SPECIE_JAPONICUS)
+
+ # koreicus responses
+ koreicus_responses = [
+ ("question_7","question_7_answer_724"),
+ ("¿Como era el mosquito?","question_7_answer_724"),
+ ("¿Como era el mosquito?","question_7_answer_724"),
+ ]
+
+ update_field_based_on_responses(apps=apps, fieldname='user_perceived_mosquito_abdomen', responses=koreicus_responses, value=Report.SPECIE_KOREICUS)
+
+ # other species responses
+ other_species_responses = [
+ ("¿Cómo es el abdomen de tu mosquito? Consulta el botón (i) y selecciona una respuesta:","Ninguno de los dos"),
+ ("¿Cómo es el abdomen del mosquito? Consulta el botón (i) y selecciona una respuesta:","No tiene líneas ni manchas blancas"),
+ ("What does the abdomen of your mosquito look like? Check the (i) button and select an answer:","Neither"),
+ ("蚊子的腹部是什麼樣子?檢查(i)按鈕,然後選擇一個答案:","皆不是")
+ ]
+
+ update_field_based_on_responses(apps=apps, fieldname='user_perceived_mosquito_abdomen', responses=other_species_responses, value=Report.SPECIE_OTHER)
+
+def populate_user_perceived_mosquito_legs(apps, schema_editor):
+ from tigaserver_app.models import Report
+
+ # albopictus responses
+ albopictus_responses = [
+ ("question_7", "question_7_answer_731"),
+ ("¿Como era el mosquito?", "question_7_answer_731"),
+ ("¿Como era el mosquito?", "question_7_answer_731"),
+ ]
+
+ update_field_based_on_responses(apps=apps, fieldname='user_perceived_mosquito_legs', responses=albopictus_responses, value=Report.SPECIE_ALBOPICTUS)
+
+ # aegypti responses
+ aegypti_responses = [
+ ("question_7", "question_7_answer_732"),
+ ("¿Como era el mosquito?", "question_7_answer_732"),
+ ("¿Como era el mosquito?", "question_7_answer_732"),
+ ]
+
+ update_field_based_on_responses(apps=apps, fieldname='user_perceived_mosquito_legs', responses=aegypti_responses, value=Report.SPECIE_AEGYPTI)
+
+ # japonicus responses
+ japonicus_responses = [
+ ("question_7", "question_7_answer_733"),
+ ("¿Como era el mosquito?", "question_7_answer_733"),
+ ("¿Como era el mosquito?", "question_7_answer_733"),
+ ]
+
+ update_field_based_on_responses(apps=apps, fieldname='user_perceived_mosquito_legs', responses=japonicus_responses, value=Report.SPECIE_JAPONICUS)
+
+ # koreicus responses
+ koreicus_responses = [
+ ("question_7", "question_7_answer_734"),
+ ("¿Como era el mosquito?", "question_7_answer_734"),
+ ]
+
+ update_field_based_on_responses(apps=apps, fieldname='user_perceived_mosquito_legs', responses=koreicus_responses, value=Report.SPECIE_KOREICUS)
+
+def populate_user_perceived_mosquito_thorax(apps, schema_editor):
+ from tigaserver_app.models import Report
+
+ # albopictus responses
+ albopictus_responses = [
+ ("question_7", "question_7_answer_711"),
+ ("Mira justo después de su cabeza, en el tórax. ¿Tiene una sola línea blanca? Consulta el botón (i) y selecciona una respuesta:", "Sí"),
+ ("¿Tiene una raya blanca en la cabeza y en el tórax?", "Sí"),
+ ("Mira just després del seu cap, al tòrax. Té una sola línia blanca? Consulta el botó (i) i selecciona una resposta:", "Sí"),
+ ("Té una ratlla blanca al cap i al tòrax?", "Sí"),
+ ("Look right after the head, at the thorax. Does it have a single white line? Check the (i) button and select an answer:", "Yes"),
+ ("¿Cómo es el tórax de tu mosquito? Consulta el botón (i) y selecciona una respuesta:", "Tórax like Ae. albopictus"),
+ ("¿Como era el mosquito?", "question_7_answer_711"),
+ ("Does it have a white stripe on the head and thorax?", "Yes"),
+ ("Com és el tòrax del teu mosquit? Consulta el botó (i) i selecciona una resposta:", "Tórax com Ae. albopictus"),
+ ("What does the thorax of your mosquito look like? Check the (i) button and select an answer:", "Thorax like Ae. albopictus"),
+ ("¿Como era el mosquito?", "question_7_answer_711"),
+ ("蚊子的胸部是什麼樣子?是否帶有一條白色條紋?檢查(i)按鈕,然後選擇答案:", "是"),
+ ]
+
+ update_field_based_on_responses(apps=apps, fieldname='user_perceived_mosquito_thorax', responses=albopictus_responses, value=Report.SPECIE_ALBOPICTUS)
+
+ # aegypti responses
+ aegypti_responses = [
+ ("question_7", "question_7_answer_712"),
+ ("Mira justo después de su cabeza, en el tórax. ¿Tiene una sola línea blanca? Consulta el botón (i) y selecciona una respuesta:", "No, tiene 4 en forma de lira"),
+ ("Look right after the head, at the thorax. Does it have a single white line? Check the (i) button and select an answer:", "No, it has 4 in a lyre-shaped pattern"),
+ ("Mira just després del seu cap, al tòrax. Té una sola línia blanca? Consulta el botó (i) i selecciona una resposta:", "No, en té 4 en forma de lira"),
+ ("¿Cómo es el tórax de tu mosquito? Consulta el botón (i) y selecciona una respuesta:", "Tórax como Ae. aegypti"),
+ ("¿Como era el mosquito?", "question_7_answer_712"),
+ ("What does the thorax of your mosquito look like? Check the (i) button and select an answer:", "Thorax like Ae. aegypti"),
+ ("Com és el tòrax del teu mosquit? Consulta el botó (i) i selecciona una resposta:", "Tórax com Ae. aegypti"),
+ ("蚊子的胸部是什麼樣子?是否帶有一條白色條紋?檢查(i)按鈕,然後選擇答案:", "否,蚊子有成豎琴形的四條白色條紋。"),
+ ("蚊子的胸部是什麼樣子?是否帶有一條白色條紋?檢查(i)按鈕,然後選擇答案:", "否,蚊子有四條白色條紋組成豎琴形"),
+ ("¿Como era el mosquito?", "question_7_answer_712"),
+ ]
+
+ update_field_based_on_responses(apps=apps, fieldname='user_perceived_mosquito_thorax', responses=aegypti_responses, value=Report.SPECIE_AEGYPTI)
+
+ # japonicus responses
+ japonicus_responses = [
+ ("question_7", "question_7_answer_713"),
+ ("¿Como era el mosquito?", "question_7_answer_713"),
+ ]
+
+ update_field_based_on_responses(apps=apps, fieldname='user_perceived_mosquito_thorax', responses=japonicus_responses, value=Report.SPECIE_JAPONICUS)
+
+ # koreicus responses
+ koreicus_responses = [
+ ("question_7", "question_7_answer_714"),
+ ("¿Como era el mosquito?", "question_7_answer_714"),
+ ("¿Como era el mosquito?", "question_7_answer_714"),
+ ]
+
+ update_field_based_on_responses(apps=apps, fieldname='user_perceived_mosquito_thorax', responses=koreicus_responses, value=Report.SPECIE_KOREICUS)
+
+ # other species responses
+ other_species_responses = [
+ ("¿Cómo es el tórax de tu mosquito? Consulta el botón (i) y selecciona una respuesta:","Ninguno de los dos"),
+ ("Com és el tòrax del teu mosquit? Consulta el botó (i) i selecciona una resposta:","Cap dels dos"),
+ ("What does the thorax of your mosquito look like? Check the (i) button and select an answer:","Neither"),
+ ("Mira just després del seu cap, al tòrax. Té una sola línia blanca? Consulta el botó (i) i selecciona una resposta:","Cap dels dos"),
+ ("Mira justo después de su cabeza, en el tórax. ¿Tiene una sola línea blanca? Consulta el botón (i) y selecciona una respuesta:","Ninguno de los dos"),
+ ("Look right after the head, at the thorax. Does it have a single white line? Check the (i) button and select an answer:","Neither"),
+ ("蚊子的胸部是什麼樣子?是否帶有一條白色條紋?檢查(i)按鈕,然後選擇答案:","皆不是")
+ ]
+
+ update_field_based_on_responses(apps=apps, fieldname='user_perceived_mosquito_thorax', responses=other_species_responses, value=Report.SPECIE_OTHER)
+
+def populate_user_perceived_mosquito_specie(apps, schema_editor):
+ from tigaserver_app.models import Report
+
+ # albopictus responses
+ albopictus_responses = [
+ ("question_6", "question_6_answer_61"),
+ ("¿Es pequeño y negro con rayas blancas?", "Sí"),
+ ("¿De qué color es? Consulta el botón (i) y selecciona una respuesta:", "Negro con líneas blancas en cuerpo y patas"),
+ ("De quin color és? Consulta el botó (i) i selecciona una resposta:", "Negre amb línies blanques al cos i les potes"),
+ ("What color is your mosquito? Check the (i) button and select an answer:", "Black with white lines on the body and the legs"),
+ ("És petit i negre amb ratlles blanques?", "Sí"),
+ ("¿De qué color es? Consulta el botón (i) y selecciona una respuesta:", "Negro con líneas blancas en cuerpo y patas"),
+ ("¿Cómo es tu mosquito? Consulta el botón (i) y selecciona una respuesta:", "Como Ae. albopictus"),
+ ("Is it small and black with white stripes?", "Yes"),
+ ("Com és el teu mosquit? Consulta el botó (i) i selecciona una resposta:", "Com Ae. albopictus"),
+ ("What does your mosquito look like? Check the (i) button and select an answer:", "Like Ae. albopictus"),
+ ("你的蚊子是什麼顏色?檢查(i)按鈕,然後選擇一個答案:", "黑色身軀,並且在腹部和腿部有白色條紋"),
+ ("你的蚊子是什麼顏色?檢查(i)按鈕,然後選擇一個答案:", "黑色身軀,並且在腹部和腿部有白色條紋。"),
+ ("What color is your mosquito? Check the (i) button and select an answer:", "Black with with white lines on the body and the legs"),
+ ]
+
+ update_field_based_on_responses(apps=apps, fieldname='user_perceived_mosquito_specie', responses=albopictus_responses, value=Report.SPECIE_ALBOPICTUS)
+
+ # aegypti responses
+ aegypti_responses = [
+ ("¿Cómo es tu mosquito? Consulta el botón (i) y selecciona una respuesta:", "Como Ae. aegypti"),
+ ("What does your mosquito look like? Check the (i) button and select an answer:", "Like Ae. aegypti"),
+ ("Com és el teu mosquit? Consulta el botó (i) i selecciona una resposta:", "Com Ae. aegypti"),
+ ]
+
+ update_field_based_on_responses(apps=apps, fieldname='user_perceived_mosquito_specie', responses=aegypti_responses, value=Report.SPECIE_AEGYPTI)
+
+
+ # culex responses
+ culex_responses = [
+ ("question_6", "question_6_answer_62"),
+ ("¿De qué color es? Consulta el botón (i) y selecciona una respuesta:", "Marrón con líneas blancas en cuerpo y patas"),
+ ("De quin color és? Consulta el botó (i) i selecciona una resposta:", "Marró amb línies blanques al cos i les potes"),
+ ("What color is your mosquito? Check the (i) button and select an answer:", "Brown with white lines on the body and the legs"),
+ ("你的蚊子是什麼顏色?檢查(i)按鈕,然後選擇一個答案:", "棕色身軀,並且在腹部和腿部有白色條紋"),
+ ]
+
+ update_field_based_on_responses(apps=apps, fieldname='user_perceived_mosquito_specie', responses=culex_responses, value=Report.SPECIE_CULEX)
+
+ # other specie responses
+ other_specie_responses = [
+ ("question_6", "question_6_answer_63"),
+ ("¿Cómo es tu mosquito? Consulta el botón (i) y selecciona una respuesta:", "None of them"),
+ ("Com és el teu mosquit? Consulta el botó (i) i selecciona una resposta:", "Cap dels dos"),
+ ("What does your mosquito look like? Check the (i) button and select an answer:", "Neither"),
+ ("¿De qué color es? Consulta el botón (i) y selecciona una respuesta:", "Ninguno de los dos"),
+ ("De quin color és? Consulta el botó (i) i selecciona una resposta:", "Cap dels dos"),
+ ("What color is your mosquito? Check the (i) button and select an answer:", "Neither"),
+ ("你的蚊子是什麼顏色?檢查(i)按鈕,然後選擇一個答案:", "皆不是"),
+ ]
+
+ update_field_based_on_responses(apps=apps, fieldname='user_perceived_mosquito_specie', responses=other_specie_responses, value=Report.SPECIE_OTHER)
+
+
+def populate_bite_count(apps, schema_editor):
+
+ def update_bite_body_part(apps, fieldname, answer_id):
+ # Loading models
+ Report = apps.get_model("tigaserver_app", "Report")
+ ReportResponse = apps.get_model("tigaserver_app", "ReportResponse")
+
+ subquery = ReportResponse.objects.filter(
+ report=models.OuterRef('pk'),
+ question_id=2,
+ answer_id=answer_id
+ ).values('answer_value')[:1]
+
+ Report.objects.update(
+ **{f"{fieldname}": Cast(
+ models.Subquery(subquery),
+ output_field=Report._meta.get_field(fieldname).__class__()
+ )}
+ )
+
+ from tigaserver_app.models import Report as ModelReport
+
+ # Ensure bite report field are set to 0 as default.
+ Report.objects.filter(
+ type=ModelReport.TYPE_BITE,
+ **{f"{fieldname}__isnull": True}
+ ).update(**{f"{fieldname}": 0})
+
+ # Loading models
+ Report = apps.get_model("tigaserver_app", "Report")
+
+ update_bite_body_part(
+ apps=apps,
+ fieldname="chest_bite_count",
+ answer_id=24
+ )
+
+ update_bite_body_part(
+ apps=apps,
+ fieldname="head_bite_count",
+ answer_id=21
+ )
+
+ update_bite_body_part(
+ apps=apps,
+ fieldname="left_arm_bite_count",
+ answer_id=22
+ )
+
+ update_bite_body_part(
+ apps=apps,
+ fieldname="right_arm_bite_count",
+ answer_id=23
+ )
+
+ update_bite_body_part(
+ apps=apps,
+ fieldname="left_leg_bite_count",
+ answer_id=25
+ )
+
+ update_bite_body_part(
+ apps=apps,
+ fieldname="right_leg_bite_count",
+ answer_id=26
+ )
+
+ # NOTE: bite count must be the last.
+ Report.objects.update(
+ bite_count=models.ExpressionWrapper(
+ Coalesce(models.F('head_bite_count'), 0) +
+ Coalesce(models.F('left_arm_bite_count'), 0) +
+ Coalesce(models.F('right_arm_bite_count'), 0) +
+ Coalesce(models.F('chest_bite_count'), 0) +
+ Coalesce(models.F('left_leg_bite_count'), 0) +
+ Coalesce(models.F('right_leg_bite_count'), 0),
+ output_field=models.PositiveSmallIntegerField()
+ )
+ )
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('tigaserver_app', '0048_report_responses_to_column'),
+ ]
+
+ operations = [
+ migrations.RunPython(populate_breeding_site_has_larvae_field, migrations.RunPython.noop),
+ migrations.RunPython(populate_breeding_site_has_near_mosquitoes, migrations.RunPython.noop),
+ migrations.RunPython(populate_breeding_site_has_water, migrations.RunPython.noop),
+ migrations.RunPython(populate_breeding_site_in_public_area, migrations.RunPython.noop),
+ migrations.RunPython(populate_breeding_site_type, migrations.RunPython.noop),
+ migrations.RunPython(populate_event_environment, migrations.RunPython.noop),
+ migrations.RunPython(populate_event_moment, migrations.RunPython.noop),
+ migrations.RunPython(populate_user_perceived_mosquito_abdomen, migrations.RunPython.noop),
+ migrations.RunPython(populate_user_perceived_mosquito_legs, migrations.RunPython.noop),
+ migrations.RunPython(populate_user_perceived_mosquito_thorax, migrations.RunPython.noop),
+ migrations.RunPython(populate_user_perceived_mosquito_specie, migrations.RunPython.noop),
+ migrations.RunPython(populate_bite_count, migrations.RunPython.noop),
+ ]
diff --git a/tigaserver_app/models.py b/tigaserver_app/models.py
index 206dea653..133e1c165 100644
--- a/tigaserver_app/models.py
+++ b/tigaserver_app/models.py
@@ -24,6 +24,7 @@
from django.db.models import Count, Q
from django.db.models.signals import post_save
from django.dispatch import receiver
+from django.forms.models import model_to_dict
from django.template.loader import render_to_string, TemplateDoesNotExist
from django.urls import reverse
from django.utils import translation
@@ -700,6 +701,142 @@ class Report(TimeZoneModelMixin, models.Model):
'''
)
+ # Attributes - user responses - Optional
+ EVENT_ENVIRONMENT_INDOORS = 'indoors'
+ EVENT_ENVIRONMENT_OUTDOORS = 'outdoors'
+ EVENT_ENVIRONMENT_VEHICLE = 'vehicle'
+
+ EVENT_ENVIRONMENT_CHOICES = (
+ (EVENT_ENVIRONMENT_INDOORS, _("Indoors")),
+ (EVENT_ENVIRONMENT_OUTDOORS, _("Outdoors")),
+ (EVENT_ENVIRONMENT_VEHICLE, _("Inside vehicle")),
+ )
+
+ event_environment = models.CharField(
+ max_length=16, choices=EVENT_ENVIRONMENT_CHOICES, null=True,
+ help_text=_("The environment where the event took place.")
+ )
+
+ EVENT_MOMENT_NOW = 'now'
+ EVENT_MOMENT_LAST_MORNING = 'last_morning'
+ EVENT_MOMENT_LAST_MIDDAY = 'last_midday'
+ EVENT_MOMENT_LAST_AFTERNOON = 'last_afternoon'
+ EVENT_MOMENT_LAST_NIGHT = 'last_night'
+ EVENT_MOMENT_CHOICES = (
+ (EVENT_MOMENT_NOW, _("Now")),
+ (EVENT_MOMENT_LAST_MORNING, _("Last morning")),
+ (EVENT_MOMENT_LAST_MIDDAY, _("Last midday")),
+ (EVENT_MOMENT_LAST_AFTERNOON, _("Last afternoon")),
+ (EVENT_MOMENT_LAST_NIGHT, _("Last night")),
+ )
+
+ event_moment = models.CharField(
+ max_length=32, choices=EVENT_MOMENT_CHOICES, null=True,
+ help_text=_("The moment of the day when the event took place.")
+ )
+
+ bite_count = models.PositiveSmallIntegerField(
+ null=True, editable=False,
+ help_text=_("Total number of bites reported.")
+ )
+
+ head_bite_count = models.PositiveSmallIntegerField(
+ null=True,
+ help_text=_("Number of bites reported in the head.")
+ )
+ left_arm_bite_count = models.PositiveSmallIntegerField(
+ null=True,
+ help_text=_("Number of bites reported in the left arm.")
+ )
+ right_arm_bite_count = models.PositiveSmallIntegerField(
+ null=True,
+ help_text=_("Number of bites reported in the right arm.")
+ )
+ chest_bite_count = models.PositiveSmallIntegerField(
+ null=True,
+ help_text=_("Number of bites reported in the chest.")
+ )
+ left_leg_bite_count = models.PositiveSmallIntegerField(
+ null=True,
+ help_text=_("Number of bites reported in the left leg.")
+ )
+ right_leg_bite_count = models.PositiveSmallIntegerField(
+ null=True,
+ help_text=_("Number of bites reported in the right leg.")
+ )
+
+ SPECIE_ALBOPICTUS = "albopictus"
+ SPECIE_AEGYPTI = "aegypti"
+ SPECIE_JAPONICUS = "japonicus"
+ SPECIE_KOREICUS = "koreicus"
+ SPECIE_CULEX = "culex"
+ SPECIE_OTHER = "other"
+
+ MOSQUITO_SPECIE_CHOICES = (
+ (SPECIE_ALBOPICTUS, "Aedes albopictus"),
+ (SPECIE_AEGYPTI, "Aedes aegypti"),
+ (SPECIE_JAPONICUS, "Aedes japonicus"),
+ (SPECIE_KOREICUS, "Aedes koreicus"),
+ (SPECIE_CULEX, "Culex pipiens"),
+ (SPECIE_OTHER, _("Other")),
+ )
+
+ user_perceived_mosquito_specie = models.CharField(
+ max_length=16, choices=MOSQUITO_SPECIE_CHOICES, null=True,
+ help_text=_("The mosquito specie perceived by the user.")
+ )
+
+ user_perceived_mosquito_thorax = models.CharField(
+ max_length=16, choices=MOSQUITO_SPECIE_CHOICES, null=True,
+ help_text=_("The species of mosquito that the thorax resembles, according to the user.")
+ )
+ user_perceived_mosquito_abdomen = models.CharField(
+ max_length=16, choices=MOSQUITO_SPECIE_CHOICES, null=True,
+ help_text=_("The species of mosquito that the abdomen resembles, according to the user.")
+ )
+ user_perceived_mosquito_legs = models.CharField(
+ max_length=16, choices=MOSQUITO_SPECIE_CHOICES, null=True,
+ help_text=_("The species of mosquito that the leg resembles, according to the user.")
+ )
+
+ BREEDING_SITE_TYPE_BASIN = 'basin'
+ BREEDING_SITE_TYPE_BUCKET = 'bucket'
+ BREEDING_SITE_TYPE_FOUNTAIN = 'fountain'
+ BREEDING_SITE_TYPE_SMALL_CONTAINER = 'small_container'
+ BREEDING_SITE_TYPE_STORM_DRAIN = 'storm_drain'
+ BREEDING_SITE_TYPE_WELL = 'well'
+ BREEDING_SITE_TYPE_OTHER = 'other'
+
+ BREEDING_SITE_TYPE_CHOICES = (
+ (BREEDING_SITE_TYPE_BASIN, _('Basin')),
+ (BREEDING_SITE_TYPE_BUCKET, _('Bucket')),
+ (BREEDING_SITE_TYPE_FOUNTAIN, _('Fountain')),
+ (BREEDING_SITE_TYPE_SMALL_CONTAINER, _('Small container')),
+ (BREEDING_SITE_TYPE_STORM_DRAIN, _('Storm Drain')),
+ (BREEDING_SITE_TYPE_WELL, _('Well')),
+ (BREEDING_SITE_TYPE_OTHER, _('Other'))
+ )
+
+ breeding_site_type = models.CharField(
+ max_length=32, choices=BREEDING_SITE_TYPE_CHOICES, null=True,
+ help_text=_("Breeding site type.")
+ )
+ breeding_site_has_water = models.BooleanField(
+ null=True,
+ help_text=_("Either if the user perceived water in the breeding site.")
+ )
+ breeding_site_in_public_area = models.BooleanField(
+ null=True,
+ help_text=_("Either if the breeding site is found in a public area.")
+ )
+ breeding_site_has_near_mosquitoes = models.BooleanField(
+ null=True,
+ help_text=_("Either if the user perceived mosquitoes near the breeding site (less than 10 meters).")
+ )
+ breeding_site_has_larvae = models.BooleanField(
+ null=True,
+ help_text=_("Either if the user perceived larvaes the breeding site.")
+ )
# Object Manager
objects = ReportManager()
@@ -1057,124 +1194,27 @@ def crowd_score(self):
# Custom properties related to breeding sites
@property
def basins(self) -> bool:
- result = False
- for this_response in self.responses.all():
- if (
- this_response.question.startswith("Tipo")
- or this_response.question.startswith("Selecciona")
- or this_response.question.startswith("Type")
- ):
- result = (
- this_response.answer.startswith("Basin")
- or this_response.answer.startswith("Basses")
- or this_response.answer.startswith("Balsa")
- or this_response.answer.startswith("Bassa")
- or this_response.answer.startswith("Small basin")
- or "balsas" in this_response.answer
- )
- return result
+ return self.breeding_site_type == self.BREEDING_SITE_TYPE_BASIN
@property
def buckets(self) -> bool:
- result = False
- for this_response in self.responses.all():
- if (
- this_response.question.startswith("Tipo")
- or this_response.question.startswith("Selecciona")
- or this_response.question.startswith("Type")
- ):
- result = (
- this_response.answer.startswith("Bucket")
- or this_response.answer.startswith("Small container")
- or this_response.answer.startswith("Bidones")
- or this_response.answer.startswith("Recipiente")
- or this_response.answer.startswith("Recipient")
- or this_response.answer.startswith("Bidons")
- )
- return result
+ return self.breeding_site_type == self.BREEDING_SITE_TYPE_BUCKET
@property
def embornals(self) -> bool:
- result = False
- for this_response in self.responses.all():
- if this_response.question_id == 12 and this_response.answer_id == 121:
- return True
- if (
- this_response.question.startswith("Tipo")
- or this_response.question.startswith("Selecciona")
- or this_response.question.startswith("Type")
- or this_response.question.startswith("Is this a storm drain")
- or this_response.question.startswith("\xc9s un embornal")
- or this_response.question.startswith("\xbfEs un imbornal")
- ):
- result = (
- this_response.answer.startswith("Embornal")
- or this_response.answer.startswith("Sumidero")
- or this_response.answer.startswith("Storm")
- or this_response.answer.startswith("Yes")
- or this_response.answer.startswith("S\xed")
- )
- return result
+ return self.breeding_site_type == self.BREEDING_SITE_TYPE_STORM_DRAIN
@property
def fonts(self) -> bool:
- result = False
- for this_response in self.responses.all():
- if (
- this_response.question.startswith("Tipo")
- or this_response.question.startswith("Selecciona")
- or this_response.question.startswith("Type")
- ):
- result = (
- this_response.answer.startswith("Font")
- or this_response.answer.startswith("Fountain")
- or this_response.answer.startswith("Fuente")
- )
- return result
+ return self.breeding_site_type == self.BREEDING_SITE_TYPE_FOUNTAIN
@property
def other(self) -> bool:
- result = False
- for this_response in self.responses.all():
- if (
- this_response.question.startswith("Tipo")
- or this_response.question.startswith("Selecciona")
- or this_response.question.startswith("Type")
- ):
- result = (
- this_response.answer == "Other"
- or this_response.answer == "Altres"
- or this_response.answer == "Otros"
- )
- return result
+ return self.breeding_site_type == self.BREEDING_SITE_TYPE_OTHER
@property
def wells(self) -> bool:
- result = False
- for this_response in self.responses.all():
- if (
- this_response.question.startswith("Tipo")
- or this_response.question.startswith("Selecciona")
- or this_response.question.startswith("Type")
- ):
- result = (
- this_response.answer == "Well"
- or this_response.answer == "Pozos"
- or this_response.answer == "Pous"
- )
- return result
-
- @property
- def site_type(self) -> str:
- result = ""
- for this_response in self.responses.all():
- if (
- this_response.question.startswith("Tipo")
- or this_response.question.startswith("Selecciona")
- or this_response.question.startswith("Type")
- ):
- result = this_response.answer
- return result
+ return self.breeding_site_type == self.BREEDING_SITE_TYPE_WELL
@property
def site_cat(self) -> int:
@@ -1191,19 +1231,6 @@ def site_cat(self) -> int:
else:
return 5
- @property
- def site_type_trans(self) -> str:
- if self.embornals:
- return _("storm-drain")
- if self.fonts:
- return _("Fountain")
- if self.basins:
- return _("Basin")
- if self.wells:
- return _("Well")
- if self.other:
- return _("Other")
-
# Other properties
@property
def tiger_responses_text(self) -> Optional[dict]:
@@ -1538,6 +1565,25 @@ def save(self, *args, **kwargs):
self.user.language_iso2 = self.app_language
self.user.save(update_fields=['language_iso2'])
+ bite_fieldnames = [
+ 'head_bite_count',
+ 'left_arm_bite_count',
+ 'right_arm_bite_count',
+ 'chest_bite_count',
+ 'left_leg_bite_count',
+ 'right_leg_bite_count'
+ ]
+
+ if self.type == self.TYPE_BITE:
+ # Make sure all bites are set to 0 if none.
+ for bite_fieldname in bite_fieldnames:
+ if getattr(self, bite_fieldname) is None:
+ setattr(self, bite_fieldname, 0)
+
+ self.bite_count = sum(
+ getattr(self, fname) for fname in bite_fieldnames
+ )
+
super(Report, self).save(*args, **kwargs)
# Meta and String
@@ -2525,6 +2571,128 @@ class ReportResponse(models.Model):
answer = models.CharField(max_length=1000, help_text='Answer that user selected.')
answer_value = models.CharField(max_length=1000, blank=True, null=True, help_text='The value right now can contain 2 things: an integer representing the number or bites, or either a WKT representation of a point for a location answer. In all other cases, it will be blank')
+ def _update_report_value(self, commit: bool = True):
+ report_obj = self.report
+
+ # Convert the original state of the object to a dictionary
+ original_report_state = model_to_dict(report_obj)
+
+ if self.question_id == 2:
+ if self.answer_id == 21:
+ report_obj.head_bite_count = int(self.answer_value)
+ elif self.answer_id == 22:
+ report_obj.left_arm_bite_count = int(self.answer_value)
+ elif self.answer_id == 23:
+ report_obj.right_arm_bite_count = int(self.answer_value)
+ elif self.answer_id == 24:
+ report_obj.chest_bite_count = int(self.answer_value)
+ elif self.answer_id == 25:
+ report_obj.left_leg_bite_count = int(self.answer_value)
+ elif self.answer_id == 26:
+ report_obj.right_leg_bite_count = int(self.answer_value)
+ elif self.question_id == 3:
+ if self.answer_id == 31:
+ report_obj.event_moment = Report.EVENT_MOMENT_LAST_MORNING
+ elif self.answer_id == 32:
+ report_obj.event_moment = Report.EVENT_MOMENT_LAST_MIDDAY
+ elif self.answer_id == 33:
+ report_obj.event_moment = Report.EVENT_MOMENT_LAST_AFTERNOON
+ elif self.answer_id == 34:
+ report_obj.event_moment = Report.EVENT_MOMENT_LAST_NIGHT
+ elif self.question_id == 4:
+ if self.answer_id == 41:
+ report_obj.event_environment = Report.EVENT_ENVIRONMENT_VEHICLE
+ elif self.answer_id == 42:
+ report_obj.event_environment = Report.EVENT_ENVIRONMENT_INDOORS
+ elif self.answer_id == 43:
+ report_obj.event_environment = Report.EVENT_ENVIRONMENT_OUTDOORS
+ elif self.answer_id == 44:
+ report_obj.event_environment = None
+ elif self.question_id == 5:
+ if self.answer_id == 51:
+ report_obj.event_moment = Report.EVENT_MOMENT_NOW
+ elif self.question == 'question_6':
+ # NOTE: using question since question_id '6' not present in DB.
+ if self.answer_id == 61:
+ report_obj.user_perceived_mosquito_specie = Report.SPECIE_ALBOPICTUS
+ elif self.answer_id == 62:
+ report_obj.user_perceived_mosquito_specie = Report.SPECIE_CULEX
+ elif self.answer_id == 63:
+ report_obj.user_perceived_mosquito_specie = Report.SPECIE_OTHER
+ elif self.answer_id == 64:
+ report_obj.user_perceived_mosquito_specie = None
+ elif self.question_id == 7 or self.question == 'question_7':
+ # Thorax
+ if self.answer_id == 711:
+ report_obj.user_perceived_mosquito_thorax = Report.SPECIE_ALBOPICTUS
+ elif self.answer_id == 712:
+ report_obj.user_perceived_mosquito_thorax = Report.SPECIE_AEGYPTI
+ elif self.answer_id == 713:
+ report_obj.user_perceived_mosquito_thorax = Report.SPECIE_JAPONICUS
+ elif self.answer_id == 714:
+ report_obj.user_perceived_mosquito_thorax = Report.SPECIE_KOREICUS
+
+ # Abdomen
+ if self.answer_id == 721:
+ report_obj.user_perceived_mosquito_abdomen = Report.SPECIE_ALBOPICTUS
+ elif self.answer_id == 722:
+ report_obj.user_perceived_mosquito_abdomen = Report.SPECIE_AEGYPTI
+ elif self.answer_id == 723:
+ report_obj.user_perceived_mosquito_abdomen = Report.SPECIE_JAPONICUS
+ elif self.answer_id == 724:
+ report_obj.user_perceived_mosquito_abdomen = Report.SPECIE_KOREICUS
+
+ # Legs
+ if self.answer_id == 731:
+ report_obj.user_perceived_mosquito_legs = Report.SPECIE_ALBOPICTUS
+ elif self.answer_id == 732:
+ report_obj.user_perceived_mosquito_legs = Report.SPECIE_AEGYPTI
+ elif self.answer_id == 733:
+ report_obj.user_perceived_mosquito_legs = Report.SPECIE_JAPONICUS
+ elif self.answer_id == 734:
+ report_obj.user_perceived_mosquito_legs = Report.SPECIE_KOREICUS
+ elif self.question_id == 10:
+ if self.answer_id == 81 or self.answer_id == 102:
+ report_obj.breeding_site_has_water = False
+ elif self.answer_id == 101:
+ report_obj.breeding_site_has_water = True
+ elif self.question_id == 12:
+ if self.answer_id == 121:
+ report_obj.breeding_site_type = Report.BREEDING_SITE_TYPE_STORM_DRAIN
+ elif self.answer_id == 122:
+ report_obj.breeding_site_type = Report.BREEDING_SITE_TYPE_OTHER
+ elif self.question_id == 13:
+ if self.answer_id == 131:
+ report_obj.event_environment = Report.EVENT_ENVIRONMENT_VEHICLE
+ elif self.answer_id == 132:
+ report_obj.event_environment = Report.EVENT_ENVIRONMENT_INDOORS
+ elif self.answer_id == 133:
+ report_obj.event_environment = Report.EVENT_ENVIRONMENT_OUTDOORS
+ elif self.question_id == 17:
+ if self.answer_id == 81 or self.answer_id == 102:
+ self.breeding_site_has_water = False
+ elif self.answer_id == 101:
+ self.breeding_site_has_water = True
+
+ # Check if any field has changed
+ if commit and any(getattr(report_obj, field) != original_report_state[field] for field in original_report_state.keys()):
+ # Save the object only if there are changes, not to trigger auto_now fields.
+ report_obj.save()
+
+ def save(self, skip_report_update: bool = False, *args, **kwargs):
+ # NOTE: this is needed to ensure question_id/answer_id are integers.
+ # _update_report_value works as expected.
+ if self.question_id is not None:
+ self.question_id = int(self.question_id)
+
+ if self.answer_id is not None:
+ self.answer_id = int(self.answer_id)
+
+ super().save(*args, **kwargs)
+
+ if not skip_report_update:
+ self._update_report_value()
+
def __unicode__(self):
return str(self.id)
diff --git a/tigaserver_app/serializers.py b/tigaserver_app/serializers.py
index 5a6e6b6af..7d0b2ccfe 100644
--- a/tigaserver_app/serializers.py
+++ b/tigaserver_app/serializers.py
@@ -273,9 +273,25 @@ def validate_type(self, attrs):
def create(self,validated_data):
responses_data = validated_data.pop('responses')
- report = Report.objects.create(**validated_data)
+ report = Report(**validated_data)
+
+ # Create the report response and updating report fields
+ # without saving. We want to save all changes at once.
+ responses = []
for response in responses_data:
- ReportResponse.objects.create(report=report, **response)
+ report_responses = ReportResponse(
+ report=report, **response
+ )
+ report_responses._update_report_value(
+ commit=False
+ )
+ responses.append(report_responses)
+
+ # Saving reports (already updated) + responses.
+ report.save()
+ for response in responses:
+ response.save(skip_report_update=True)
+
return report
class Meta:
diff --git a/tigaserver_app/tests/tests.py b/tigaserver_app/tests/tests.py
index 528255fbe..40c4f186c 100644
--- a/tigaserver_app/tests/tests.py
+++ b/tigaserver_app/tests/tests.py
@@ -1125,6 +1125,8 @@ def test_flip_site_to_site(self):
self.assertTrue(n_responses == 2, "Number of responses should be 2, is {0}".format(n_responses))
self.assertTrue(r_site_reloaded.flipped, "Report should be marked as flipped")
self.assertTrue(r_site_reloaded.flipped_to == 'site#site',"Report should be marked as flipped from site to site, field has value of {0}".format(r_site_reloaded.flipped_to))
+ self.assertEqual(r_site_reloaded.breeding_site_type, Report.BREEDING_SITE_TYPE_STORM_DRAIN)
+ self.assertTrue(r_site_reloaded.breeding_site_has_water)
def test_flip(self):
u = User.objects.get(pk=25)
@@ -1143,7 +1145,10 @@ def test_flip(self):
response = self.client.patch('/api/flip_report/', data=data)
self.assertEqual(response.status_code, 200, "Response should be 200, is {0}".format(response.status_code))
adult_reloaded = Report.objects.get(pk=r_adult.version_UUID)
- self.assertTrue(adult_reloaded.type=='site',"Report type should have changed to site, is {0}".format(adult_reloaded.type))
+ self.assertTrue(adult_reloaded.type==Report.TYPE_SITE,"Report type should have changed to site, is {0}".format(adult_reloaded.type))
+ self.assertEqual(adult_reloaded.breeding_site_type, Report.BREEDING_SITE_TYPE_STORM_DRAIN)
+ self.assertTrue(adult_reloaded.breeding_site_has_water)
+
n_responses = ReportResponse.objects.filter(report=adult_reloaded).count()
self.assertTrue( n_responses == 2, "Number of responses should be 2, is {0}".format(n_responses) )
self.assertTrue( adult_reloaded.flipped, "Report should be marked as flipped" )
@@ -1164,7 +1169,8 @@ def test_flip(self):
response = self.client.patch('/api/flip_report/', data=data)
self.assertEqual(response.status_code, 200, "Response should be 200, is {0}".format(response.status_code))
site_reloaded = Report.objects.get(pk=r_site.version_UUID)
- self.assertTrue(site_reloaded.type == 'adult', "Report type should have changed to adult, is {0}".format(site_reloaded.type))
+ self.assertTrue(site_reloaded.type == Report.TYPE_ADULT, "Report type should have changed to adult, is {0}".format(site_reloaded.type))
+
n_responses = ReportResponse.objects.filter(report=site_reloaded).count()
self.assertTrue(n_responses == 0, "Number of responses should be 0, is {0}".format(n_responses))
self.assertTrue(site_reloaded.flipped, "Report should be marked as flipped")
diff --git a/tigaserver_app/views.py b/tigaserver_app/views.py
index bb53cf0cc..4195e9a80 100644
--- a/tigaserver_app/views.py
+++ b/tigaserver_app/views.py
@@ -2256,16 +2256,16 @@ def flip_report(request):
return Response(data={'message': 'success', 'opcode': -1}, status=status.HTTP_400_BAD_REQUEST)
if flip_to_type == '': # adult | site
raise ParseError(detail='flip_to_type param is mandatory')
- if flip_to_type not in ['adult', 'site']:
+ if flip_to_type not in [Report.TYPE_ADULT, Report.TYPE_SITE]:
raise ParseError(detail='value not allowed, possible values are \'adult\', \'site\'')
- if flip_to_type == 'site':
+ if flip_to_type == Report.TYPE_SITE:
if flip_to_subtype == '':
raise ParseError(detail='flip_to_subtype param is mandatory if type is site')
else:
if flip_to_subtype not in ['storm_drain_water','storm_drain_dry', 'other_water', 'other_dry']:
raise ParseError(detail='value not allowed, possible values are \'storm_drain_water\',\'storm_drain_dry\', \'other_water\', \'other_dry\' ')
- if report.type == 'adult' and flip_to_type == 'adult':
+ if report.type == Report.TYPE_ADULT and flip_to_type == Report.TYPE_ADULT:
return Response(
data={'message': 'Type is already adult, doing nothing', 'opcode': -2}, status=status.HTTP_400_BAD_REQUEST)
# delete questions and answers ?
@@ -2278,15 +2278,15 @@ def flip_report(request):
# id 4ada4a1b-c438-4fcc-87e7-eb4696c1466f question_10 question_10_answer_101 101 10 -> Water
with transaction.atomic():
ReportResponse.objects.filter(report=report).delete()
- rr_type_stormdrain = ReportResponse(report=report,question='question_12',answer='question_12_answer_121',question_id='12',answer_id='121')
- rr_type_other = ReportResponse(report=report, question='question_12', answer='question_12_answer_122', question_id='12', answer_id='122')
- rr_yes_water = ReportResponse(report=report, question='question_10', answer='question_10_answer_101', question_id='10', answer_id='101')
- rr_no_water = ReportResponse(report=report, question='question_10', answer='question_10_answer_102', question_id='10', answer_id='102')
- if flip_to_type == 'site':
- report.flipped = True
- report.flipped_on = timezone.now()
+ rr_type_stormdrain = ReportResponse(report=report,question='question_12',answer='question_12_answer_121',question_id=12,answer_id=121)
+ rr_type_other = ReportResponse(report=report, question='question_12', answer='question_12_answer_122', question_id=12, answer_id=122)
+ rr_yes_water = ReportResponse(report=report, question='question_10', answer='question_10_answer_101', question_id=10, answer_id=101)
+ rr_no_water = ReportResponse(report=report, question='question_10', answer='question_10_answer_102', question_id=10, answer_id=102)
+ report.flipped = True
+ report.flipped_on = timezone.now()
+ if flip_to_type == Report.TYPE_SITE:
report.flipped_to = report.type + '#site'
- report.type = 'site'
+ report.type = Report.TYPE_SITE
report.save()
if flip_to_subtype == 'storm_drain_water':
rr_type_stormdrain.save()
@@ -2304,11 +2304,9 @@ def flip_report(request):
rr_type_other.save()
rr_yes_water.save()
message = "Report changed to Site - Other, Water"
- elif flip_to_type == 'adult':
- report.flipped = True
- report.flipped_on = timezone.now()
+ elif flip_to_type == Report.TYPE_ADULT:
report.flipped_to = report.type + '#adult'
- report.type = 'adult'
+ report.type = Report.TYPE_ADULT
report.save()
message = "Report changed to Adult"