Skip to content

Commit f653c23

Browse files
committed
add geography to collaborative
1 parent c3ebd57 commit f653c23

File tree

5 files changed

+331
-3
lines changed

5 files changed

+331
-3
lines changed
Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
"""
2+
Django management command to populate Asia Pacific geography data.
3+
Covers: India, Indonesia, Thailand at state/province level.
4+
5+
Usage:
6+
python manage.py populate_geographies
7+
python manage.py populate_geographies --clear # Clear existing data first
8+
"""
9+
10+
from typing import Any
11+
12+
from django.core.management.base import BaseCommand, CommandParser
13+
from django.db import transaction
14+
15+
from api.models import Geography
16+
from api.utils.enums import GeoTypes
17+
18+
19+
class Command(BaseCommand):
20+
help = "Populate Asia Pacific geography data (India, Indonesia, Thailand)"
21+
22+
def add_arguments(self, parser: CommandParser) -> None:
23+
parser.add_argument(
24+
"--clear",
25+
action="store_true",
26+
help="Clear all existing geography data before populating",
27+
)
28+
29+
def handle(self, *args: Any, **options: Any) -> None:
30+
clear_existing = options.get("clear", False)
31+
32+
if clear_existing:
33+
self.stdout.write(self.style.WARNING("Clearing existing geography data..."))
34+
Geography.objects.all().delete()
35+
self.stdout.write(self.style.SUCCESS("✓ Cleared existing data"))
36+
37+
with transaction.atomic():
38+
self._populate_geographies()
39+
40+
self.stdout.write(
41+
self.style.SUCCESS(
42+
f"\n✅ Successfully populated {Geography.objects.count()} geography entries"
43+
)
44+
)
45+
46+
def _populate_geographies(self) -> None:
47+
"""Populate geography data in an idempotent way."""
48+
49+
# Create or get Asia Pacific region
50+
asia_pacific, created = Geography.objects.get_or_create(
51+
name="Asia Pacific", defaults={"code": "APAC", "type": GeoTypes.REGION}
52+
)
53+
if created:
54+
self.stdout.write(f"Created region: {asia_pacific.name}")
55+
else:
56+
self.stdout.write(f"Region already exists: {asia_pacific.name}")
57+
58+
# India and its states
59+
self._populate_india(asia_pacific)
60+
61+
# Indonesia and its provinces
62+
self._populate_indonesia(asia_pacific)
63+
64+
# Thailand and its provinces
65+
self._populate_thailand(asia_pacific)
66+
67+
def _populate_india(self, parent: Geography) -> None:
68+
"""Populate India and its states/UTs."""
69+
india, created = Geography.objects.get_or_create(
70+
name="India",
71+
defaults={"code": "IN", "type": GeoTypes.COUNTRY, "parent_id": parent},
72+
)
73+
if created:
74+
self.stdout.write(f"Created country: {india.name}")
75+
else:
76+
self.stdout.write(f"Country already exists: {india.name}")
77+
78+
indian_states = [
79+
("Andhra Pradesh", "AP"),
80+
("Arunachal Pradesh", "AR"),
81+
("Assam", "AS"),
82+
("Bihar", "BR"),
83+
("Chhattisgarh", "CT"),
84+
("Goa", "GA"),
85+
("Gujarat", "GJ"),
86+
("Haryana", "HR"),
87+
("Himachal Pradesh", "HP"),
88+
("Jharkhand", "JH"),
89+
("Karnataka", "KA"),
90+
("Kerala", "KL"),
91+
("Madhya Pradesh", "MP"),
92+
("Maharashtra", "MH"),
93+
("Manipur", "MN"),
94+
("Meghalaya", "ML"),
95+
("Mizoram", "MZ"),
96+
("Nagaland", "NL"),
97+
("Odisha", "OR"),
98+
("Punjab", "PB"),
99+
("Rajasthan", "RJ"),
100+
("Sikkim", "SK"),
101+
("Tamil Nadu", "TN"),
102+
("Telangana", "TG"),
103+
("Tripura", "TR"),
104+
("Uttar Pradesh", "UP"),
105+
("Uttarakhand", "UT"),
106+
("West Bengal", "WB"),
107+
("Andaman and Nicobar Islands", "AN"),
108+
("Chandigarh", "CH"),
109+
("Dadra and Nagar Haveli and Daman and Diu", "DN"),
110+
("Delhi", "DL"),
111+
("Jammu and Kashmir", "JK"),
112+
("Ladakh", "LA"),
113+
("Lakshadweep", "LD"),
114+
("Puducherry", "PY"),
115+
]
116+
117+
created_count = 0
118+
for state_name, state_code in indian_states:
119+
_, created = Geography.objects.get_or_create(
120+
name=state_name,
121+
defaults={
122+
"code": state_code,
123+
"type": GeoTypes.STATE,
124+
"parent_id": india,
125+
},
126+
)
127+
if created:
128+
created_count += 1
129+
130+
self.stdout.write(
131+
f"Indian states/UTs: {created_count} created, {len(indian_states) - created_count} already existed"
132+
)
133+
134+
def _populate_indonesia(self, parent: Geography) -> None:
135+
"""Populate Indonesia and its provinces."""
136+
indonesia, created = Geography.objects.get_or_create(
137+
name="Indonesia",
138+
defaults={"code": "ID", "type": GeoTypes.COUNTRY, "parent_id": parent},
139+
)
140+
if created:
141+
self.stdout.write(f"Created country: {indonesia.name}")
142+
else:
143+
self.stdout.write(f"Country already exists: {indonesia.name}")
144+
145+
indonesian_provinces = [
146+
("Aceh", "AC"),
147+
("Bali", "BA"),
148+
("Banten", "BT"),
149+
("Bengkulu", "BE"),
150+
("Central Java", "JT"),
151+
("Central Kalimantan", "KT"),
152+
("Central Sulawesi", "ST"),
153+
("East Java", "JI"),
154+
("East Kalimantan", "KI"),
155+
("East Nusa Tenggara", "NT"),
156+
("Gorontalo", "GO"),
157+
("Jakarta", "JK"),
158+
("Jambi", "JA"),
159+
("Lampung", "LA"),
160+
("Maluku", "MA"),
161+
("North Kalimantan", "KU"),
162+
("North Maluku", "MU"),
163+
("North Sulawesi", "SA"),
164+
("North Sumatra", "SU"),
165+
("Papua", "PA"),
166+
("Riau", "RI"),
167+
("Riau Islands", "KR"),
168+
("South Kalimantan", "KS"),
169+
("South Sulawesi", "SN"),
170+
("South Sumatra", "SS"),
171+
("Southeast Sulawesi", "SG"),
172+
("West Java", "JB"),
173+
("West Kalimantan", "KB"),
174+
("West Nusa Tenggara", "NB"),
175+
("West Papua", "PB"),
176+
("West Sulawesi", "SR"),
177+
("West Sumatra", "SB"),
178+
("Yogyakarta", "YO"),
179+
]
180+
181+
created_count = 0
182+
for province_name, province_code in indonesian_provinces:
183+
_, created = Geography.objects.get_or_create(
184+
name=province_name,
185+
defaults={
186+
"code": province_code,
187+
"type": GeoTypes.STATE,
188+
"parent_id": indonesia,
189+
},
190+
)
191+
if created:
192+
created_count += 1
193+
194+
self.stdout.write(
195+
f"Indonesian provinces: {created_count} created, {len(indonesian_provinces) - created_count} already existed"
196+
)
197+
198+
def _populate_thailand(self, parent: Geography) -> None:
199+
"""Populate Thailand and its provinces."""
200+
thailand, created = Geography.objects.get_or_create(
201+
name="Thailand",
202+
defaults={"code": "TH", "type": GeoTypes.COUNTRY, "parent_id": parent},
203+
)
204+
if created:
205+
self.stdout.write(f"Created country: {thailand.name}")
206+
else:
207+
self.stdout.write(f"Country already exists: {thailand.name}")
208+
209+
thai_provinces = [
210+
("Bangkok", "BKK"),
211+
("Amnat Charoen", "37"),
212+
("Ang Thong", "15"),
213+
("Bueng Kan", "38"),
214+
("Buriram", "31"),
215+
("Chachoengsao", "24"),
216+
("Chai Nat", "18"),
217+
("Chaiyaphum", "36"),
218+
("Chanthaburi", "22"),
219+
("Chiang Mai", "50"),
220+
("Chiang Rai", "57"),
221+
("Chonburi", "20"),
222+
("Chumphon", "86"),
223+
("Kalasin", "46"),
224+
("Kamphaeng Phet", "62"),
225+
("Kanchanaburi", "71"),
226+
("Khon Kaen", "40"),
227+
("Krabi", "81"),
228+
("Lampang", "52"),
229+
("Lamphun", "51"),
230+
("Loei", "42"),
231+
("Lopburi", "16"),
232+
("Mae Hong Son", "58"),
233+
("Maha Sarakham", "44"),
234+
("Mukdahan", "49"),
235+
("Nakhon Nayok", "26"),
236+
("Nakhon Pathom", "73"),
237+
("Nakhon Phanom", "48"),
238+
("Nakhon Ratchasima", "30"),
239+
("Nakhon Sawan", "60"),
240+
("Nakhon Si Thammarat", "80"),
241+
("Nan", "55"),
242+
("Narathiwat", "96"),
243+
("Nong Bua Lamphu", "39"),
244+
("Nong Khai", "43"),
245+
("Nonthaburi", "12"),
246+
("Pathum Thani", "13"),
247+
("Pattani", "94"),
248+
("Phang Nga", "82"),
249+
("Phatthalung", "93"),
250+
("Phayao", "56"),
251+
("Phetchabun", "67"),
252+
("Phetchaburi", "76"),
253+
("Phichit", "66"),
254+
("Phitsanulok", "65"),
255+
("Phra Nakhon Si Ayutthaya", "14"),
256+
("Phrae", "54"),
257+
("Phuket", "83"),
258+
("Prachinburi", "25"),
259+
("Prachuap Khiri Khan", "77"),
260+
("Ranong", "85"),
261+
("Ratchaburi", "70"),
262+
("Rayong", "21"),
263+
("Roi Et", "45"),
264+
("Sa Kaeo", "27"),
265+
("Sakon Nakhon", "47"),
266+
("Samut Prakan", "11"),
267+
("Samut Sakhon", "74"),
268+
("Samut Songkhram", "75"),
269+
("Saraburi", "19"),
270+
("Satun", "91"),
271+
("Sing Buri", "17"),
272+
("Sisaket", "33"),
273+
("Songkhla", "90"),
274+
("Sukhothai", "64"),
275+
("Suphan Buri", "72"),
276+
("Surat Thani", "84"),
277+
("Surin", "32"),
278+
("Tak", "63"),
279+
("Trang", "92"),
280+
("Trat", "23"),
281+
("Ubon Ratchathani", "34"),
282+
("Udon Thani", "41"),
283+
("Uthai Thani", "61"),
284+
("Uttaradit", "53"),
285+
("Yala", "95"),
286+
("Yasothon", "35"),
287+
]
288+
289+
created_count = 0
290+
for province_name, province_code in thai_provinces:
291+
_, created = Geography.objects.get_or_create(
292+
name=province_name,
293+
defaults={
294+
"code": province_code,
295+
"type": GeoTypes.STATE,
296+
"parent_id": thailand,
297+
},
298+
)
299+
if created:
300+
created_count += 1
301+
302+
self.stdout.write(
303+
f"Thai provinces: {created_count} created, {len(thai_provinces) - created_count} already existed"
304+
)

api/models/Collaborative.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ class Collaborative(models.Model):
4444
"api.Sector", blank=True, related_name="collaboratives"
4545
)
4646
sdgs = models.ManyToManyField("api.SDG", blank=True, related_name="collaboratives")
47+
geographies = models.ManyToManyField(
48+
"api.Geography", blank=True, related_name="collaboratives"
49+
)
4750
contributors = models.ManyToManyField(
4851
"authorization.User", blank=True, related_name="contributed_collaboratives"
4952
)
@@ -79,5 +82,9 @@ def tags_indexing(self):
7982
def sdgs_indexing(self):
8083
return [sdg.code for sdg in self.sdgs.all()] # type: ignore
8184

85+
@property
86+
def geographies_indexing(self):
87+
return [geo.name for geo in self.geographies.all()] # type: ignore
88+
8289
class Meta:
8390
db_table = "collaborative"

api/models/Geography.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import uuid
12
from typing import Optional
23

34
from django.db import models
@@ -6,7 +7,7 @@
67

78

89
class Geography(models.Model):
9-
id = models.AutoField(primary_key=True)
10+
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
1011
name = models.CharField(max_length=75, unique=True)
1112
code = models.CharField(
1213
max_length=100, null=True, blank=True, unique=False, default=""

api/schema/collaborative_schema.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
CollaborativeMetadata,
2222
CollaborativeOrganizationRelationship,
2323
Dataset,
24+
Geography,
2425
Metadata,
2526
Organization,
2627
Sector,
@@ -66,6 +67,7 @@ class UpdateCollaborativeMetadataInput:
6667
tags: Optional[List[str]]
6768
sectors: List[uuid.UUID]
6869
sdgs: Optional[List[uuid.UUID]]
70+
geographies: Optional[List[uuid.UUID]]
6971

7072

7173
@strawberry_django.partial(Collaborative, fields="__all__", exclude=["datasets"])
@@ -393,6 +395,10 @@ def add_update_collaborative_metadata(
393395
_update_collaborative_sectors(collaborative, update_metadata_input.sectors)
394396
if update_metadata_input.sdgs is not None:
395397
_update_collaborative_sdgs(collaborative, update_metadata_input.sdgs)
398+
if update_metadata_input.geographies is not None:
399+
_update_collaborative_geographies(
400+
collaborative, update_metadata_input.geographies
401+
)
396402
return TypeCollaborative.from_django(collaborative)
397403

398404
@strawberry_django.mutation(handle_django_errors=False)
@@ -959,3 +965,12 @@ def update_collaborative_organization_relationships(
959965
)
960966

961967
return TypeCollaborative.from_django(collaborative)
968+
969+
970+
def _update_collaborative_geographies(
971+
collaborative: Collaborative, geography_ids: List[uuid.UUID]
972+
) -> None:
973+
"""Update geographies for a collaborative."""
974+
collaborative.geographies.clear()
975+
geographies = Geography.objects.filter(id__in=geography_ids)
976+
collaborative.geographies.add(*geographies)

api/utils/enums.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ class OrganizationTypes(models.TextChoices):
2424

2525

2626
class GeoTypes(models.TextChoices):
27-
DISTRICT = "DISTRICT"
28-
STATE = "STATE"
27+
REGION = "REGION"
2928
COUNTRY = "COUNTRY"
29+
STATE = "STATE"
30+
DISTRICT = "DISTRICT"
3031
UT = "UT"
3132

3233

0 commit comments

Comments
 (0)