Skip to content

Commit ef43eec

Browse files
Merge pull request #1600 from IFRCGo/develop
Introducing Country Plans
2 parents 7fc4e00 + 16a8bb7 commit ef43eec

31 files changed

+1395
-208
lines changed

CHANGELOG.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
66

77
## Unreleased
88

9+
## 1.1.461
10+
11+
### Added
12+
- Introducing COUNTRY PLANs
13+
- Only active users to be shown in DREF forms (for sharing)
14+
- Add centroid processing for Admin2
15+
- DREF Ops Update validation fixes
16+
- Update snapshottest to 0.6.0 (and other small modules)
17+
918
## 1.1.460
1019

1120
### Added
@@ -2090,7 +2099,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
20902099

20912100
## 0.1.20
20922101

2093-
[Unreleased]: https://github.com/IFRCGo/go-api/compare/1.1.460...HEAD
2102+
[Unreleased]: https://github.com/IFRCGo/go-api/compare/1.1.461...HEAD
2103+
[1.1.461]: https://github.com/IFRCGo/go-api/compare/1.1.460...1.1.461
20942104
[1.1.460]: https://github.com/IFRCGo/go-api/compare/1.1.459...1.1.460
20952105
[1.1.459]: https://github.com/IFRCGo/go-api/compare/1.1.458...1.1.459
20962106
[1.1.458]: https://github.com/IFRCGo/go-api/compare/1.1.457...1.1.458

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ To update GO countries and districts Mapbox tilesets, run the management command
250250
* `--update-countries` — update tileset for countries, including labels
251251
* `--update-districts` — update tileset for districts, including labels
252252
* `--update-all` — update all countries and districts tilesets
253-
* `--create-and-update-admin2 <ISO3>` — if a new admin2 tileset should be created, use this argument. It will create a new source on Mapbox and then register a tileset. Ensure that a recipe is create in `mapbox/admin2/` directory. For example, see `mapbox/admin2/COL.json`. To run `python manage.py update-mapbox-tilesets --create-and-update-admin2 COL`
253+
* `--create-and-update-admin2 <ISO3>` — if a new admin2 tileset should be created, use this argument. It will create a new source on Mapbox and then register a tileset. Ensure that recipes are create in `mapbox/admin2/` directory. For example, see `mapbox/admin2/COL.json` and `mapbox/admin2/COL-centroids.json`. A recipe for polygons and centroids are required. For centroids, we don't need to create a staging recipe. To run `python manage.py update-mapbox-tilesets --create-and-update-admin2 COL`
254254
* `--update-admin2 <ISO3>` — use this to update an existing admin2 tileset. For example, `python manage.py update-mapbox-tilesets --update-admin2 COL`
255255

256256
## Import GEC codes

api/admin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ class DistrictAdmin(geoadmin.OSMGeoAdmin, CompareVersionAdmin, RegionRestrictedA
526526

527527
class CountryAdmin(geoadmin.OSMGeoAdmin, CompareVersionAdmin, RegionRestrictedAdmin, TranslationAdmin):
528528
country_in = 'pk__in'
529-
list_display = ('__str__', 'record_type')
529+
list_display = ('__str__', 'record_type', 'iso3')
530530
region_in = 'region__pk__in'
531531
list_editable = ('record_type',)
532532
search_fields = ('name',)

api/drf_views.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from django_filters import rest_framework as filters
1212
from django.contrib.auth.models import User
1313
from django.db import models
14-
from django.db.models import Prefetch, Count, Q
14+
from django.db.models import Prefetch, Count, Q, OuterRef
1515
from django.utils import timezone
1616

1717
from main.utils import is_tableau
@@ -61,6 +61,8 @@
6161
CountryOfFieldReportToReview,
6262
)
6363

64+
from country_plan.models import CountryPlan
65+
6466
from .serializers import (
6567
ActionSerializer,
6668
DisasterTypeSerializer,
@@ -167,7 +169,11 @@ class DisasterTypeViewset(viewsets.ReadOnlyModelViewSet):
167169

168170

169171
class RegionViewset(viewsets.ReadOnlyModelViewSet):
170-
queryset = Region.objects.all()
172+
queryset = Region.objects.annotate(
173+
country_plan_count=Count(
174+
'country__country_plan', filter=Q(country__country_plan__is_publish=True)
175+
)
176+
)
171177

172178
def get_serializer_class(self):
173179
if self.action == 'list':
@@ -185,17 +191,20 @@ class Meta:
185191

186192

187193
class CountryViewset(viewsets.ReadOnlyModelViewSet):
188-
queryset = Country.objects.filter(is_deprecated=False)
194+
queryset = Country.objects.filter(is_deprecated=False).annotate(
195+
has_country_plan=models.Exists(CountryPlan.objects.filter(country=OuterRef('pk'), is_publish=True))
196+
)
189197
filterset_class = CountryFilter
190198
search_fields = ('name',) # for /docs
191199

192200
def get_object(self):
193201
pk = self.kwargs['pk']
202+
qs = self.get_queryset()
194203
try:
195-
return Country.objects.get(pk=int(pk))
204+
return qs.get(pk=int(pk))
196205
except ValueError:
197206
# NOTE: If pk is not integer try searching for name or iso
198-
country = Country.objects.filter(
207+
country = qs.filter(
199208
models.Q(name__iexact=str(pk)) | models.Q(iso__iexact=str(pk))
200209
)
201210
if country.exists():

api/management/commands/update-mapbox-tilesets.py

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -206,28 +206,40 @@ def create_and_update_admin2(self, iso, staging=True):
206206
if (status):
207207
update_status = self.update_admin2(iso, staging)
208208
if (update_status):
209-
tileset_name = f'go-ifrc.go-admin2-{iso}-staging'
210-
recipe_name = f'mapbox/admin2/{iso}-staging.json'
209+
polygon_tileset_name = f'go-ifrc.go-admin2-{iso}-staging'
210+
polygon_recipe_name = f'mapbox/admin2/{iso}-staging.json'
211+
centroids_tileset_name = f'go-ifrc.go-admin2-{iso}-centroids'
212+
centroids_recipe_name = f'mapbox/admin2/{iso}-centroids.json'
211213
if not staging:
212-
tileset_name = f'go-ifrc.go-admin2-{iso}'
213-
recipe_name = f'mapbox/admin2/{iso}.json'
214+
polygon_tileset_name = f'go-ifrc.go-admin2-{iso}'
215+
polygon_recipe_name = f'mapbox/admin2/{iso}.json'
216+
217+
create_status = subprocess.run(['tilesets', 'create', polygon_tileset_name, '--recipe', polygon_recipe_name, '--name', f'GO Admin2 {iso}'])
218+
219+
if create_status:
220+
create_status = subprocess.run(['tilesets', 'create', centroids_tileset_name, '--recipe', centroids_recipe_name, '--name', f'GO Admin2 {iso} Centroids'])
214221

215-
create_status = subprocess.run(['tilesets', 'create', tileset_name, '--recipe', recipe_name, '--name', f'GO Admin2 {iso}'])
216222
if create_status:
217223
publish_status = self.publish_admin2(iso, staging, create=True)
218224
return publish_status
219225

220226
def update_admin2(self, iso, staging=True):
221227
# update tileset source
222228
# update tileset and publish
223-
tileset_source__name = f'go-admin2-{iso}-src-staging'
229+
polygon_tileset_source__name = f'go-admin2-{iso}-src-staging'
230+
centroids_tileset_source_name = f'go-admin2-{iso}-centroids-src'
224231
if not staging:
225-
tileset_source__name = f'go-admin2-{iso}-src'
232+
polygon_tileset_source__name = f'go-admin2-{iso}-src'
226233

227-
print('Tileset source', tileset_source__name)
234+
print('Tileset source', polygon_tileset_source__name)
235+
print('Tileset source centroids', centroids_tileset_source_name)
228236
status = self.prepare_admin2_geojson(iso)
229237
if status:
230-
status = subprocess.run(['tilesets', 'upload-source', '--replace', 'go-ifrc', tileset_source__name, f'/tmp/{iso}.geojson'])
238+
status = subprocess.run(['tilesets', 'upload-source', '--replace', 'go-ifrc', polygon_tileset_source__name, f'/tmp/{iso}.geojson'])
239+
240+
if status:
241+
status = subprocess.run(['tilesets', 'upload-source', '--replace', 'go-ifrc', centroids_tileset_source_name, f'/tmp/{iso}-centroids.geojson'])
242+
231243
return True if status.returncode == 0 else False
232244

233245
def publish_admin2(self, iso, staging=True, create=False):
@@ -237,11 +249,13 @@ def publish_admin2(self, iso, staging=True, create=False):
237249
update_status = True
238250

239251
if update_status:
240-
tileset_name = f'go-ifrc.go-admin2-{iso}-staging'
252+
polygon_tileset_name = f'go-ifrc.go-admin2-{iso}-staging'
253+
centroids_tileset_name = f'go-ifrc.go-admin2-{iso}-centroids'
241254
if not staging:
242-
tileset_name = f'go-ifrc.go-admin2-{iso}'
255+
polygon_tileset_name = f'go-ifrc.go-admin2-{iso}'
243256

244-
publish_status = subprocess.run(['tilesets', 'publish', tileset_name])
257+
publish_status = subprocess.run(['tilesets', 'publish', polygon_tileset_name])
258+
publish_status = subprocess.run(['tilesets', 'publish', centroids_tileset_name])
245259
return True if publish_status.returncode == 0 else False
246260
else:
247261
return False
@@ -254,5 +268,7 @@ def prepare_admin2_geojson(self, iso):
254268
pass
255269

256270
status = subprocess.run(['ogr2ogr', '-f', 'GeoJSON', f'/tmp/{iso}.geojson', self.connection_string, '-sql', f'select d.id as admin1_id, d.name as admin1_name, ad.name, ad.id, adg.geom from api_country as c, api_district as d, api_admin2 as ad, api_admin2geoms as adg where c.id=d.country_id and c.iso3=\'{iso}\' and ad.admin1_id=d.id and adg.admin2_id = ad.id'])
271+
if status:
272+
status = subprocess.run(['ogr2ogr', '-f', 'GeoJSON', f'/tmp/{iso}-centroids.geojson', self.connection_string, '-sql', f'select d.id as admin1_id, d.name as admin1_name, ad.name, ad.id, ad.centroid from api_country as c, api_district as d, api_admin2 as ad where c.id=d.country_id and c.iso3=\'{iso}\' and ad.admin1_id=d.id'])
257273

258274
return True if status.returncode == 0 else False

api/serializers.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ class RegionRelationSerializer(ModelSerializer):
357357
preparedness_snippets = RegionPreparednessSnippetSerializer(many=True, read_only=True)
358358
national_society_count = serializers.SerializerMethodField()
359359
country_cluster_count = serializers.SerializerMethodField()
360+
country_plan_count = serializers.IntegerField(read_only=True)
360361

361362
@staticmethod
362363
def get_national_society_count(obj):
@@ -370,13 +371,14 @@ class Meta:
370371
model = Region
371372
fields = ('links', 'contacts', 'snippets', 'emergency_snippets',
372373
'profile_snippets', 'preparedness_snippets', 'name',
373-
'region_name', 'id', 'additional_tab_name',
374+
'region_name', 'id', 'additional_tab_name', 'country_plan_count',
374375
'national_society_count', 'country_cluster_count',)
375376

376377

377378
class CountryRelationSerializer(ModelSerializer):
378379
links = CountryLinkSerializer(many=True, read_only=True)
379380
contacts = CountryContactSerializer(many=True, read_only=True)
381+
has_country_plan = serializers.BooleanField(read_only=True)
380382

381383
class Meta:
382384
model = Country
@@ -387,7 +389,7 @@ class Meta:
387389
'nsi_trained_in_first_aid', 'nsi_gov_financial_support', 'nsi_domestically_generated_income',
388390
'nsi_annual_fdrs_reporting', 'nsi_policy_implementation', 'nsi_risk_management_framework',
389391
'nsi_cmc_dashboard_compliance', 'wash_kit2', 'wash_kit5', 'wash_kit10', 'wash_staff_at_hq',
390-
'wash_staff_at_branch', 'wash_ndrt_trained', 'wash_rdrt_trained',
392+
'wash_staff_at_branch', 'wash_ndrt_trained', 'wash_rdrt_trained', 'has_country_plan',
391393
)
392394

393395

country_plan/__init__.py

Whitespace-only changes.

country_plan/admin.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from django.contrib import admin
2+
3+
from country_plan.models import (
4+
CountryPlan,
5+
DataImport,
6+
StrategicPriority,
7+
MembershipCoordination
8+
)
9+
10+
11+
@admin.register(DataImport)
12+
class DataImportAdmin(admin.ModelAdmin):
13+
readonly_fields = ('errors',)
14+
15+
16+
@admin.register(CountryPlan)
17+
class CountryPlanAdmin(admin.ModelAdmin):
18+
autocomplete_fields = ('country',)
19+
search_fields = ('country__name',)
20+
list_filter = ('is_publish',)
21+
22+
23+
@admin.register(StrategicPriority)
24+
class StrategicPriorityAdmin(admin.ModelAdmin):
25+
list_display = ('country_plan', 'type', 'people_targeted')
26+
autocomplete_fields = ('country_plan',)
27+
28+
29+
@admin.register(MembershipCoordination)
30+
class MembershipCoordinationAdmin(admin.ModelAdmin):
31+
list_display = ('country_plan', 'national_society', 'sector')
32+
search_fields = ('sector',)
33+
autocomplete_fields = ('national_society', 'country_plan')

country_plan/apps.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from django.apps import AppConfig
2+
3+
4+
class CountryPlanConfig(AppConfig):
5+
default_auto_field = 'django.db.models.BigAutoField'
6+
name = 'country_plan'

country_plan/drf_views.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from rest_framework import mixins, viewsets
2+
from django.db import models
3+
4+
from .models import CountryPlan, MembershipCoordination
5+
from .serializers import CountryPlanSerializer
6+
7+
8+
class CountryPlanViewset(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
9+
serializer_class = CountryPlanSerializer
10+
queryset = CountryPlan.objects.prefetch_related(
11+
'country_plan_sp',
12+
models.Prefetch(
13+
'country_plan_mc',
14+
queryset=MembershipCoordination.objects.annotate(
15+
national_society_name=models.F('national_society__society_name'),
16+
),
17+
),
18+
).filter(is_publish=True)

0 commit comments

Comments
 (0)