moz-utils 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
moz_utils/__init__.py ADDED
@@ -0,0 +1,1071 @@
1
+ """
2
+ moz_utils
3
+
4
+ Funções de utilidade para Moçambique.
5
+ Validação de NUIT, BI, documentos, e formatação de telefones.
6
+ """
7
+
8
+ import re
9
+ from typing import Optional, List, Dict, Any, Union
10
+ import urllib.parse
11
+
12
+ def is_valid_mozambican_phone(phone: str) -> bool:
13
+ """
14
+ Valida um número de telefone moçambicano.
15
+ Operadoras válidas: Vodacom (84/85), Tmcel (82/83), Movitel (86/87/88)
16
+ """
17
+ cleaned = re.sub(r'[\s\-\(\)\+]', '', phone)
18
+ without_country_code = cleaned[3:] if cleaned.startswith('258') else cleaned
19
+ return bool(re.match(r'^8[2-8]\d{7}$', without_country_code))
20
+
21
+ def format_mozambican_phone(phone: str) -> str:
22
+ """Formata um número de telefone moçambicano para o padrão internacional."""
23
+ cleaned = re.sub(r'[\s\-\(\)\+]', '', phone)
24
+ without_country_code = cleaned[3:] if cleaned.startswith('258') else cleaned
25
+
26
+ if not is_valid_mozambican_phone(without_country_code):
27
+ raise ValueError(f"Número de telefone inválido: {phone}")
28
+
29
+ prefix = without_country_code[0:2]
30
+ part1 = without_country_code[2:5]
31
+ part2 = without_country_code[5:]
32
+
33
+ return f"+258 {prefix} {part1} {part2}"
34
+
35
+ def get_mobile_operator(phone: str) -> Optional[str]:
36
+ """Identifica a operadora de um número moçambicano."""
37
+ cleaned = re.sub(r'[\s\-\(\)\+]', '', phone)
38
+ without_country_code = cleaned[3:] if cleaned.startswith('258') else cleaned
39
+
40
+ if not is_valid_mozambican_phone(without_country_code):
41
+ return None
42
+
43
+ prefix = without_country_code[0:2]
44
+ operators = {
45
+ '84': 'Vodacom',
46
+ '85': 'Vodacom',
47
+ '82': 'Tmcel',
48
+ '83': 'Tmcel',
49
+ '86': 'Movitel',
50
+ '87': 'Movitel',
51
+ '88': 'Movitel',
52
+ }
53
+
54
+ return operators.get(prefix)
55
+
56
+ def is_valid_nuit(nuit: Union[str, int]) -> bool:
57
+ """
58
+ Valida o NUIT (Número Único de Identificação Tributária) de Moçambique.
59
+
60
+ Regras da AT:
61
+ - 9 dígitos
62
+ - Primeiro dígito: 1 a 5
63
+ - Nono dígito: Módulo 11
64
+ """
65
+ cleaned = re.sub(r'\D', '', str(nuit))
66
+
67
+ if len(cleaned) != 9:
68
+ return False
69
+ if re.match(r'^(\d)\1{8}$', cleaned):
70
+ return False
71
+ if not re.match(r'^[1-5]', cleaned):
72
+ return False
73
+
74
+ total_sum = sum(int(cleaned[i]) * (9 - i) for i in range(8))
75
+ remainder = total_sum % 11
76
+ expected_digit = 0 if remainder <= 1 else 11 - remainder
77
+
78
+ return int(cleaned[8]) == expected_digit
79
+
80
+ def get_nuit_entity_type(nuit: Union[str, int]) -> Optional[str]:
81
+ """Classifica o tipo de entidade com base no primeiro dígito do NUIT."""
82
+ cleaned = re.sub(r'\D', '', str(nuit))
83
+ if not is_valid_nuit(cleaned):
84
+ return None
85
+
86
+ first_digit = cleaned[0]
87
+ types = {
88
+ '1': 'Singular (Cidadãos nacionais/estrangeiros e ENI)',
89
+ '2': 'Singular (Cidadãos nacionais/estrangeiros e ENI)',
90
+ '3': 'Equiparada (Heranças Jacentes, Consórcios)',
91
+ '4': 'Colectiva (Sociedades por Quotas, SA, Lda, Associações)',
92
+ '5': 'Público (Instituições do Estado e Ministérios)'
93
+ }
94
+
95
+ return types.get(first_digit)
96
+
97
+ def is_valid_bi(bi: str) -> bool:
98
+ """Valida o Bilhete de Identidade Moçambicano."""
99
+ cleaned = re.sub(r'[\s\-]', '', bi).upper()
100
+ return bool(re.match(r'^\d{12}[A-Z]$', cleaned))
101
+
102
+ def format_mzn(value: float, currency: str = 'MT') -> str:
103
+ """
104
+ Formata um valor monetário em Meticais seguindo o padrão oficial de Moçambique.
105
+
106
+ Padrão oficial (SI + AT):
107
+ - Separador de milhares: espaço
108
+ - Separador decimal: vírgula
109
+ - Símbolo após o valor, separado por espaço
110
+
111
+ Args:
112
+ value: Valor numérico
113
+ currency: 'MT' (nacional) ou 'MZN' (ISO 4217)
114
+
115
+ Returns:
116
+ Valor formatado (ex: "1 500,00 MT")
117
+ """
118
+ sign = '-' if value < 0 else ''
119
+ absolute = abs(value)
120
+
121
+ # Formatar com 2 casas decimais
122
+ formatted = f"{absolute:,.2f}"
123
+
124
+ # Substituir: vírgula dos milhares → espaço, ponto decimal → vírgula
125
+ formatted = formatted.replace(',', ' ').replace('.', ',')
126
+
127
+ return f"{sign}{formatted} {currency}"
128
+
129
+ def build_whatsapp_url(phone: str, message: str = "") -> str:
130
+ """Gera um URL de contacto WhatsApp com mensagem pré-formatada."""
131
+ cleaned = re.sub(r'[\s\-\(\)\+]', '', phone)
132
+ international = cleaned if cleaned.startswith('258') else f"258{cleaned}"
133
+
134
+ encoded_message = f"?text={urllib.parse.quote(message)}" if message else ""
135
+ return f"https://wa.me/{international}{encoded_message}"
136
+
137
+ def get_mozambique_provinces() -> List[Dict[str, Any]]:
138
+ """Lista oficial das Províncias de Moçambique com os seus distritos."""
139
+ return [
140
+ {
141
+ 'id': 'cab',
142
+ 'name': 'Cabo Delgado',
143
+ 'region': 'Norte',
144
+ 'sigla': 'CBD',
145
+ 'districts': [
146
+ {
147
+ 'name': 'Ancuabe',
148
+ 'postos_administrativos': ['Ancuabe', 'Metoro', 'Meza'],
149
+ 'bairros': []
150
+ },
151
+ {
152
+ 'name': 'Balama',
153
+ 'postos_administrativos': ['Balama', 'Chapa', 'Kuekue', 'Mavala'],
154
+ 'bairros': []
155
+ },
156
+ {
157
+ 'name': 'Chiúre',
158
+ 'postos_administrativos': ['Chiúre', 'Chiúre-Velho', 'Katapua', 'Mazeze', 'Namogelia', 'Manoane'],
159
+ 'bairros': []
160
+ },
161
+ {
162
+ 'name': 'Ibo',
163
+ 'postos_administrativos': ['Ibo', 'Quirimba'],
164
+ 'bairros': []
165
+ },
166
+ {
167
+ 'name': 'Macomia',
168
+ 'postos_administrativos': ['Macomia', 'Chai', 'Mucojo', 'Quiterajo'],
169
+ 'bairros': []
170
+ },
171
+ {
172
+ 'name': 'Mecúfi',
173
+ 'postos_administrativos': ['Mecúfi', 'Murrébuè'],
174
+ 'bairros': []
175
+ },
176
+ {
177
+ 'name': 'Meluco',
178
+ 'postos_administrativos': ['Meluco', 'Muaguide'],
179
+ 'bairros': []
180
+ },
181
+ {
182
+ 'name': 'Metuge',
183
+ 'postos_administrativos': ['Metuge', 'Mieze'],
184
+ 'bairros': []
185
+ },
186
+ {
187
+ 'name': 'Mocímboa da Praia',
188
+ 'postos_administrativos': ['Mocímboa da Praia', 'Diaca', 'Mbau'],
189
+ 'bairros': []
190
+ },
191
+ {
192
+ 'name': 'Montepuez',
193
+ 'postos_administrativos': ['Montepuez', 'Mapupulo', 'Namanhumbir', 'Nairoto', 'Napaula'],
194
+ 'bairros': []
195
+ },
196
+ {
197
+ 'name': 'Mueda',
198
+ 'postos_administrativos': ['Mueda', 'Chapa', 'Imbuho', 'Negomano', 'N\'gapa'],
199
+ 'bairros': []
200
+ },
201
+ {
202
+ 'name': 'Muidumbe',
203
+ 'postos_administrativos': ['Muidumbe', 'Chitunda', 'Miteda'],
204
+ 'bairros': []
205
+ },
206
+ {
207
+ 'name': 'Namuno',
208
+ 'postos_administrativos': ['Namuno', 'Machoca', 'Meloco', 'Ncumpe', 'Luli'],
209
+ 'bairros': []
210
+ },
211
+ {
212
+ 'name': 'Nangade',
213
+ 'postos_administrativos': ['Nangade', 'Ntamba'],
214
+ 'bairros': []
215
+ },
216
+ {
217
+ 'name': 'Palma',
218
+ 'postos_administrativos': ['Palma', 'Olumbe', 'Quionga'],
219
+ 'bairros': []
220
+ },
221
+ {
222
+ 'name': 'Pemba (Cidade)',
223
+ 'postos_administrativos': ['Pemba'],
224
+ 'bairros': [
225
+ 'Paquitequete',
226
+ 'Natite',
227
+ 'Cariacó',
228
+ 'Alto Gingone',
229
+ 'Insubria',
230
+ 'Muxara',
231
+ 'Maringanha',
232
+ 'Chibuébue'
233
+ ]
234
+ },
235
+ {
236
+ 'name': 'Quissanga',
237
+ 'postos_administrativos': ['Quissanga', 'Mahate', 'Bilibiza'],
238
+ 'bairros': []
239
+ }
240
+ ]
241
+ },
242
+ {
243
+ 'id': 'nia',
244
+ 'name': 'Niassa',
245
+ 'region': 'Norte',
246
+ 'sigla': 'NS',
247
+ 'districts': [
248
+ {
249
+ 'name': 'Chimbonila',
250
+ 'postos_administrativos': ['Chimbonila', 'Meponda'],
251
+ 'bairros': []
252
+ },
253
+ {
254
+ 'name': 'Cuamba',
255
+ 'postos_administrativos': ['Cuamba', 'Lúrio', 'Etatara'],
256
+ 'bairros': ['Ribaue', 'Mutxora', 'Ademo', 'Aeroporto']
257
+ },
258
+ {
259
+ 'name': 'Lago',
260
+ 'postos_administrativos': ['Metangula', 'Cobué', 'Luninho', 'Maniamba'],
261
+ 'bairros': []
262
+ },
263
+ {
264
+ 'name': 'Lichinga (Cidade)',
265
+ 'postos_administrativos': ['Lichinga'],
266
+ 'bairros': ['Central', 'Popular', 'Chimba', 'Cerâmica', 'Ngaula', 'Sanjala', 'Chiuaula']
267
+ },
268
+ {
269
+ 'name': 'Majune',
270
+ 'postos_administrativos': ['Majune', 'Mua', 'Nairrobi'],
271
+ 'bairros': []
272
+ },
273
+ {
274
+ 'name': 'Mandimba',
275
+ 'postos_administrativos': ['Mandimba', 'Mitande'],
276
+ 'bairros': []
277
+ },
278
+ {
279
+ 'name': 'Marrupa',
280
+ 'postos_administrativos': ['Marrupa', 'Marangira', 'Nungo'],
281
+ 'bairros': []
282
+ },
283
+ {
284
+ 'name': 'Maúa',
285
+ 'postos_administrativos': ['Maúa', 'Maiaca'],
286
+ 'bairros': []
287
+ },
288
+ {
289
+ 'name': 'Mavago',
290
+ 'postos_administrativos': ['Mavago', 'M\'saize'],
291
+ 'bairros': []
292
+ },
293
+ {
294
+ 'name': 'Mecanhelas',
295
+ 'postos_administrativos': ['Mecanhelas', 'Chiuta'],
296
+ 'bairros': []
297
+ },
298
+ {
299
+ 'name': 'Mecula',
300
+ 'postos_administrativos': ['Mecula', 'Matondovela'],
301
+ 'bairros': []
302
+ },
303
+ {
304
+ 'name': 'Metarica',
305
+ 'postos_administrativos': ['Metarica', 'Nacuanha'],
306
+ 'bairros': []
307
+ },
308
+ {
309
+ 'name': 'Muembe',
310
+ 'postos_administrativos': ['Muembe', 'Chiconono'],
311
+ 'bairros': []
312
+ },
313
+ {
314
+ 'name': 'N\'gauma',
315
+ 'postos_administrativos': ['Massangulo', 'Itepela'],
316
+ 'bairros': []
317
+ },
318
+ {
319
+ 'name': 'Nipepe',
320
+ 'postos_administrativos': ['Nipepe', 'Muatuca'],
321
+ 'bairros': []
322
+ },
323
+ {
324
+ 'name': 'Sanga',
325
+ 'postos_administrativos': ['Unango', 'Malamuila', 'Matchedje'],
326
+ 'bairros': []
327
+ }
328
+ ]
329
+ },
330
+ {
331
+ 'id': 'npl',
332
+ 'name': 'Nampula',
333
+ 'region': 'Norte',
334
+ 'sigla': 'NPL',
335
+ 'districts': [
336
+ {
337
+ 'name': 'Angoche',
338
+ 'postos_administrativos': ['Angoche', 'Aube', 'Namaponda'],
339
+ 'bairros': []
340
+ },
341
+ {
342
+ 'name': 'Eráti',
343
+ 'postos_administrativos': ['Namapa', 'Alua', 'Nakarari'],
344
+ 'bairros': []
345
+ },
346
+ {
347
+ 'name': 'Ilha de Moçambique',
348
+ 'postos_administrativos': ['Ilha de Moçambique', 'Lumbo'],
349
+ 'bairros': ['Museu', 'Litine', 'Areal', 'Marangonha']
350
+ },
351
+ {
352
+ 'name': 'Lalaua',
353
+ 'postos_administrativos': ['Lalaua', 'Meti'],
354
+ 'bairros': []
355
+ },
356
+ {
357
+ 'name': 'Larde',
358
+ 'postos_administrativos': ['Larde', 'Mucuali'],
359
+ 'bairros': []
360
+ },
361
+ {
362
+ 'name': 'Liúpo',
363
+ 'postos_administrativos': ['Liúpo', 'Quinga'],
364
+ 'bairros': []
365
+ },
366
+ {
367
+ 'name': 'Malema',
368
+ 'postos_administrativos': ['Malema', 'Chinga', 'Mutuali'],
369
+ 'bairros': []
370
+ },
371
+ {
372
+ 'name': 'Meconta',
373
+ 'postos_administrativos': ['Meconta', 'Corrane', 'Namialo'],
374
+ 'bairros': []
375
+ },
376
+ {
377
+ 'name': 'Mecubúri',
378
+ 'postos_administrativos': ['Mecubúri', 'Milhana', 'Muite', 'Namina'],
379
+ 'bairros': []
380
+ },
381
+ {
382
+ 'name': 'Memba',
383
+ 'postos_administrativos': ['Memba', 'Chipene', 'Mazua', 'Lurio'],
384
+ 'bairros': []
385
+ },
386
+ {
387
+ 'name': 'Mogincual',
388
+ 'postos_administrativos': ['Mogincual', 'Quixaxe'],
389
+ 'bairros': []
390
+ },
391
+ {
392
+ 'name': 'Mogovolas',
393
+ 'postos_administrativos': ['Nametil', 'Calipo', 'Ilute', 'Muatua'],
394
+ 'bairros': []
395
+ },
396
+ {
397
+ 'name': 'Moma',
398
+ 'postos_administrativos': ['Macone', 'Chalai', 'Lunga'],
399
+ 'bairros': []
400
+ },
401
+ {
402
+ 'name': 'Monapo',
403
+ 'postos_administrativos': ['Monapo', 'Itoculo', 'Netia'],
404
+ 'bairros': []
405
+ },
406
+ {
407
+ 'name': 'Mossuril',
408
+ 'postos_administrativos': ['Mossuril', 'Lunga', 'Matibane'],
409
+ 'bairros': []
410
+ },
411
+ {
412
+ 'name': 'Muecate',
413
+ 'postos_administrativos': ['Muecate', 'Imala', 'Muculuone'],
414
+ 'bairros': []
415
+ },
416
+ {
417
+ 'name': 'Murrupula',
418
+ 'postos_administrativos': ['Murrupula', 'Chinga', 'Nihessiue'],
419
+ 'bairros': []
420
+ },
421
+ {
422
+ 'name': 'Nacala-a-Velha',
423
+ 'postos_administrativos': ['Nacala-a-Velha', 'Covo'],
424
+ 'bairros': []
425
+ },
426
+ {
427
+ 'name': 'Nacala Porto',
428
+ 'postos_administrativos': ['Nacala Porto', 'Muanona'],
429
+ 'bairros': ['Mutiva', 'Triângulo', 'Ontupaia', 'Quissanga']
430
+ },
431
+ {
432
+ 'name': 'Nampula (Cidade)',
433
+ 'postos_administrativos': ['Urbano Central', 'Muatala', 'Muhala', 'Namikopo', 'Napipine', 'Natikiri'],
434
+ 'bairros': ['Central', 'Muatala', 'Muhala', 'Namikopo', 'Napipine', 'Natikiri', 'Marrere', 'Namutequeliua']
435
+ },
436
+ {
437
+ 'name': 'Nacarôa',
438
+ 'postos_administrativos': ['Nacarôa', 'Saua-Saua'],
439
+ 'bairros': []
440
+ },
441
+ {
442
+ 'name': 'Rapale',
443
+ 'postos_administrativos': ['Rapale', 'Anchilo', 'Mutivaze'],
444
+ 'bairros': []
445
+ },
446
+ {
447
+ 'name': 'Ribáuè',
448
+ 'postos_administrativos': ['Ribáuè', 'Cunle', 'Iapala'],
449
+ 'bairros': []
450
+ }
451
+ ]
452
+ },
453
+ {
454
+ 'id': 'zam',
455
+ 'name': 'Zambézia',
456
+ 'region': 'Centro',
457
+ 'sigla': 'ZMB',
458
+ 'districts': [
459
+ {
460
+ 'name': 'Alto Molócuè',
461
+ 'postos_administrativos': ['Alto Molócuè', 'Nauela'],
462
+ 'bairros': []
463
+ },
464
+ {
465
+ 'name': 'Chinde',
466
+ 'postos_administrativos': ['Chinde', 'Micaune'],
467
+ 'bairros': []
468
+ },
469
+ {
470
+ 'name': 'Derre',
471
+ 'postos_administrativos': ['Derre', 'Guerissa'],
472
+ 'bairros': []
473
+ },
474
+ {
475
+ 'name': 'Gilé',
476
+ 'postos_administrativos': ['Gilé', 'Alto Ligonha'],
477
+ 'bairros': []
478
+ },
479
+ {
480
+ 'name': 'Gurué',
481
+ 'postos_administrativos': ['Gurué', 'Lioma', 'Nepuíte'],
482
+ 'bairros': ['Bairro Central', 'Mucuapa', 'Nacuacue']
483
+ },
484
+ {
485
+ 'name': 'Ile',
486
+ 'postos_administrativos': ['Ile', 'Socone'],
487
+ 'bairros': []
488
+ },
489
+ {
490
+ 'name': 'Inhassunge',
491
+ 'postos_administrativos': ['Mucupia', 'Gonhane'],
492
+ 'bairros': []
493
+ },
494
+ {
495
+ 'name': 'Luabo',
496
+ 'postos_administrativos': ['Luabo', 'Chimbazo'],
497
+ 'bairros': []
498
+ },
499
+ {
500
+ 'name': 'Lugela',
501
+ 'postos_administrativos': ['Lugela', 'Tacuane', 'Munhamade'],
502
+ 'bairros': []
503
+ },
504
+ {
505
+ 'name': 'Maganja da Costa',
506
+ 'postos_administrativos': ['Maganja da Costa', 'Baleia'],
507
+ 'bairros': []
508
+ },
509
+ {
510
+ 'name': 'Milange',
511
+ 'postos_administrativos': ['Milange', 'Majaua', 'Mongue'],
512
+ 'bairros': []
513
+ },
514
+ {
515
+ 'name': 'Mocuba',
516
+ 'postos_administrativos': ['Mocuba', 'Mualama', 'Namanjavira'],
517
+ 'bairros': ['Central', 'Aeroporto', 'Paraíso']
518
+ },
519
+ {
520
+ 'name': 'Mocubela',
521
+ 'postos_administrativos': ['Mocubela', 'Bajone'],
522
+ 'bairros': []
523
+ },
524
+ {
525
+ 'name': 'Molumbo',
526
+ 'postos_administrativos': ['Molumbo', 'Corromana'],
527
+ 'bairros': []
528
+ },
529
+ {
530
+ 'name': 'Mopeia',
531
+ 'postos_administrativos': ['Mopeia', 'Campo'],
532
+ 'bairros': []
533
+ },
534
+ {
535
+ 'name': 'Morrumbala',
536
+ 'postos_administrativos': ['Morrumbala', 'Chire', 'Megaza'],
537
+ 'bairros': []
538
+ },
539
+ {
540
+ 'name': 'Mulevala',
541
+ 'postos_administrativos': ['Mulevala', 'Chirimane'],
542
+ 'bairros': []
543
+ },
544
+ {
545
+ 'name': 'Namacurra',
546
+ 'postos_administrativos': ['Namacurra', 'Macuse'],
547
+ 'bairros': []
548
+ },
549
+ {
550
+ 'name': 'Namarrói',
551
+ 'postos_administrativos': ['Namarrói', 'Regone'],
552
+ 'bairros': []
553
+ },
554
+ {
555
+ 'name': 'Nicoadala',
556
+ 'postos_administrativos': ['Nicoadala', 'Maquival'],
557
+ 'bairros': []
558
+ },
559
+ {
560
+ 'name': 'Pebane',
561
+ 'postos_administrativos': ['Pebane', 'Mulela', 'Naburi'],
562
+ 'bairros': []
563
+ },
564
+ {
565
+ 'name': 'Quelimane (Cidade)',
566
+ 'postos_administrativos': ['Urbano nº 1', 'Urbano nº 2', 'Urbano nº 3', 'Urbano nº 4'],
567
+ 'bairros': ['Central', 'Cementório', 'Inhassunge', 'Icidua', 'Chingo', 'Matacuane']
568
+ }
569
+ ]
570
+ },
571
+ {
572
+ 'id': 'tet',
573
+ 'name': 'Tete',
574
+ 'region': 'Centro',
575
+ 'sigla': 'TT',
576
+ 'districts': [
577
+ {
578
+ 'name': 'Angónia',
579
+ 'postos_administrativos': ['Ulongue', 'Domue'],
580
+ 'bairros': []
581
+ },
582
+ {
583
+ 'name': 'Cahora-Bassa',
584
+ 'postos_administrativos': ['Songo', 'Chitima', 'Muxeza'],
585
+ 'bairros': []
586
+ },
587
+ {
588
+ 'name': 'Changara',
589
+ 'postos_administrativos': ['Luenha', 'Chioco', 'Mavago'],
590
+ 'bairros': []
591
+ },
592
+ {
593
+ 'name': 'Chifunde',
594
+ 'postos_administrativos': ['Chifunde', 'Mualadzi', 'Nsadzu'],
595
+ 'bairros': []
596
+ },
597
+ {
598
+ 'name': 'Chiuta',
599
+ 'postos_administrativos': ['Manje', 'Kazula'],
600
+ 'bairros': []
601
+ },
602
+ {
603
+ 'name': 'Dôa',
604
+ 'postos_administrativos': ['Dôa', 'Chueza'],
605
+ 'bairros': []
606
+ },
607
+ {
608
+ 'name': 'Macanga',
609
+ 'postos_administrativos': ['Furancungo', 'Chinde'],
610
+ 'bairros': []
611
+ },
612
+ {
613
+ 'name': 'Magoé',
614
+ 'postos_administrativos': ['Mpende', 'Chinthopo', 'Mukumbura'],
615
+ 'bairros': []
616
+ },
617
+ {
618
+ 'name': 'Marara',
619
+ 'postos_administrativos': ['Marara', 'M\'fuba'],
620
+ 'bairros': []
621
+ },
622
+ {
623
+ 'name': 'Marávia',
624
+ 'postos_administrativos': ['Fingoé', 'Chiputo', 'Molumbo'],
625
+ 'bairros': []
626
+ },
627
+ {
628
+ 'name': 'Moatize',
629
+ 'postos_administrativos': ['Moatize', 'Kambulatsitsi', 'Zóbuè'],
630
+ 'bairros': ['Bairro 25 de Setembro', 'Liberdade', 'Chithatha']
631
+ },
632
+ {
633
+ 'name': 'Mutarara',
634
+ 'postos_administrativos': ['Nhamayabué', 'Inhangoma'],
635
+ 'bairros': []
636
+ },
637
+ {
638
+ 'name': 'Tete (Cidade)',
639
+ 'postos_administrativos': ['Tete'],
640
+ 'bairros': ['Chingo', 'Degue', 'Matundo', 'Mpadue', 'Josina Machel', 'Francisco Manyanga']
641
+ },
642
+ {
643
+ 'name': 'Tsangano',
644
+ 'postos_administrativos': ['Tsangano', 'Ntengo-Wambuzi'],
645
+ 'bairros': []
646
+ },
647
+ {
648
+ 'name': 'Zumbo',
649
+ 'postos_administrativos': ['Zumbo', 'Muze', 'Zambue'],
650
+ 'bairros': []
651
+ }
652
+ ]
653
+ },
654
+ {
655
+ 'id': 'man',
656
+ 'name': 'Manica',
657
+ 'region': 'Centro',
658
+ 'sigla': 'MN',
659
+ 'districts': [
660
+ {
661
+ 'name': 'Bárue',
662
+ 'postos_administrativos': ['Catandica', 'Nhampassa', 'Chuala'],
663
+ 'bairros': []
664
+ },
665
+ {
666
+ 'name': 'Chimoio (Cidade)',
667
+ 'postos_administrativos': ['Urbano nº 1', 'Urbano nº 2', 'Urbano nº 3'],
668
+ 'bairros': ['Central', '7 de Setembro', 'Soalpo', 'Nandfe', 'Vila Nova', 'Cordor']
669
+ },
670
+ {
671
+ 'name': 'Gondola',
672
+ 'postos_administrativos': ['Gondola', 'Cafumpe', 'Amatongas'],
673
+ 'bairros': []
674
+ },
675
+ {
676
+ 'name': 'Guro',
677
+ 'postos_administrativos': ['Guro', 'Mandie', 'Nhamassonge'],
678
+ 'bairros': []
679
+ },
680
+ {
681
+ 'name': 'Macate',
682
+ 'postos_administrativos': ['Macate', 'Marera'],
683
+ 'bairros': []
684
+ },
685
+ {
686
+ 'name': 'Machaze',
687
+ 'postos_administrativos': ['Machaze', 'Save'],
688
+ 'bairros': []
689
+ },
690
+ {
691
+ 'name': 'Macossa',
692
+ 'postos_administrativos': ['Macossa', 'Nhamagua'],
693
+ 'bairros': []
694
+ },
695
+ {
696
+ 'name': 'Manica',
697
+ 'postos_administrativos': ['Manica', 'Messica', 'Mavonde'],
698
+ 'bairros': []
699
+ },
700
+ {
701
+ 'name': 'Mossurize',
702
+ 'postos_administrativos': ['Espungabera', 'Dacata'],
703
+ 'bairros': []
704
+ },
705
+ {
706
+ 'name': 'Sussundenga',
707
+ 'postos_administrativos': ['Sussundenga', 'Dombe', 'Muhoa'],
708
+ 'bairros': []
709
+ },
710
+ {
711
+ 'name': 'Tambara',
712
+ 'postos_administrativos': ['Nhacolo', 'Buzua'],
713
+ 'bairros': []
714
+ },
715
+ {
716
+ 'name': 'Vanduzi',
717
+ 'postos_administrativos': ['Vanduzi', 'Matsinho'],
718
+ 'bairros': []
719
+ }
720
+ ]
721
+ },
722
+ {
723
+ 'id': 'sof',
724
+ 'name': 'Sofala',
725
+ 'region': 'Centro',
726
+ 'sigla': 'SF',
727
+ 'districts': [
728
+ {
729
+ 'name': 'Beira (Cidade)',
730
+ 'postos_administrativos': ['Central', 'Munhava', 'Manga Loot', 'Inhamizua'],
731
+ 'bairros': ['Chaimite', 'Macuti', 'Ponta Gêa', 'Munhava', 'Manga', 'Vaz', 'Esturro', 'Cipangara']
732
+ },
733
+ {
734
+ 'name': 'Búzi',
735
+ 'postos_administrativos': ['Búzi', 'Estaquinha', 'Nova Sofala'],
736
+ 'bairros': []
737
+ },
738
+ {
739
+ 'name': 'Caia',
740
+ 'postos_administrativos': ['Caia', 'Sena', 'Murraça'],
741
+ 'bairros': []
742
+ },
743
+ {
744
+ 'name': 'Chemba',
745
+ 'postos_administrativos': ['Chemba', 'Chiramba', 'Mulima'],
746
+ 'bairros': []
747
+ },
748
+ {
749
+ 'name': 'Cheringoma',
750
+ 'postos_administrativos': ['Inhaminga', 'Muanza'],
751
+ 'bairros': []
752
+ },
753
+ {
754
+ 'name': 'Chibabava',
755
+ 'postos_administrativos': ['Chibabava', 'Goonda', 'Muxúnguè'],
756
+ 'bairros': []
757
+ },
758
+ {
759
+ 'name': 'Dondo',
760
+ 'postos_administrativos': ['Dondo', 'Mafambisse'],
761
+ 'bairros': ['Chibuabuamua', 'Central', 'Planalto']
762
+ },
763
+ {
764
+ 'name': 'Gorongosa',
765
+ 'postos_administrativos': ['Gorongosa', 'Nhamadzi', 'Vanduzi'],
766
+ 'bairros': []
767
+ },
768
+ {
769
+ 'name': 'Machanga',
770
+ 'postos_administrativos': ['Machanga', 'Divinhe'],
771
+ 'bairros': []
772
+ },
773
+ {
774
+ 'name': 'Maringué',
775
+ 'postos_administrativos': ['Maringué', 'Canxixe', 'Subui'],
776
+ 'bairros': []
777
+ },
778
+ {
779
+ 'name': 'Marromeu',
780
+ 'postos_administrativos': ['Marromeu', 'Chupanga'],
781
+ 'bairros': []
782
+ },
783
+ {
784
+ 'name': 'Muanza',
785
+ 'postos_administrativos': ['Muanza', 'Galinha'],
786
+ 'bairros': []
787
+ },
788
+ {
789
+ 'name': 'Nhamatanda',
790
+ 'postos_administrativos': ['Nhamatanda', 'Tica'],
791
+ 'bairros': []
792
+ }
793
+ ]
794
+ },
795
+ {
796
+ 'id': 'inh',
797
+ 'name': 'Inhambane',
798
+ 'region': 'Sul',
799
+ 'sigla': 'INH',
800
+ 'districts': [
801
+ {
802
+ 'name': 'Funhalouro',
803
+ 'postos_administrativos': ['Funhalouro', 'Tome'],
804
+ 'bairros': []
805
+ },
806
+ {
807
+ 'name': 'Govuro',
808
+ 'postos_administrativos': ['Nova Mambone', 'Jofane'],
809
+ 'bairros': []
810
+ },
811
+ {
812
+ 'name': 'Homoíne',
813
+ 'postos_administrativos': ['Homoíne', 'Pembe'],
814
+ 'bairros': []
815
+ },
816
+ {
817
+ 'name': 'Inhambane (Cidade)',
818
+ 'postos_administrativos': ['Inhambane'],
819
+ 'bairros': ['Balane', 'Chamane', 'Josina Machel', 'Muelé', 'Liberdade', 'Aeroporto']
820
+ },
821
+ {
822
+ 'name': 'Inharrime',
823
+ 'postos_administrativos': ['Inharrime', 'Chambone'],
824
+ 'bairros': []
825
+ },
826
+ {
827
+ 'name': 'Inhassoro',
828
+ 'postos_administrativos': ['Inhassoro', 'Bazaruto'],
829
+ 'bairros': []
830
+ },
831
+ {
832
+ 'name': 'Jangamo',
833
+ 'postos_administrativos': ['Jangamo', 'Cumbana'],
834
+ 'bairros': []
835
+ },
836
+ {
837
+ 'name': 'Mabote',
838
+ 'postos_administrativos': ['Mabote', 'Zimane'],
839
+ 'bairros': []
840
+ },
841
+ {
842
+ 'name': 'Massinga',
843
+ 'postos_administrativos': ['Massinga', 'Chicomo'],
844
+ 'bairros': []
845
+ },
846
+ {
847
+ 'name': 'Maxixe (Cidade)',
848
+ 'postos_administrativos': ['Maxixe'],
849
+ 'bairros': ['Bairro Central', 'Chamba', 'Macupula', 'Nalazi']
850
+ },
851
+ {
852
+ 'name': 'Morrumbene',
853
+ 'postos_administrativos': ['Morrumbene', 'Mucodoene'],
854
+ 'bairros': []
855
+ },
856
+ {
857
+ 'name': 'Panda',
858
+ 'postos_administrativos': ['Panda', 'Muelé'],
859
+ 'bairros': []
860
+ },
861
+ {
862
+ 'name': 'Vilankulo',
863
+ 'postos_administrativos': ['Vilankulo', 'Mapinhane'],
864
+ 'bairros': ['Bairro Central', 'Mucoque', 'Alto Macassa']
865
+ },
866
+ {
867
+ 'name': 'Zavala',
868
+ 'postos_administrativos': ['Quissico', 'Zandamela'],
869
+ 'bairros': []
870
+ }
871
+ ]
872
+ },
873
+ {
874
+ 'id': 'gaz',
875
+ 'name': 'Gaza',
876
+ 'region': 'Sul',
877
+ 'sigla': 'GZ',
878
+ 'districts': [
879
+ {
880
+ 'name': 'Bilene',
881
+ 'postos_administrativos': ['Macia', 'Bilene Macia', 'Chissano'],
882
+ 'bairros': []
883
+ },
884
+ {
885
+ 'name': 'Chibuto',
886
+ 'postos_administrativos': ['Chibuto', 'Chaimite', 'Changanine'],
887
+ 'bairros': []
888
+ },
889
+ {
890
+ 'name': 'Chicualacuala',
891
+ 'postos_administrativos': ['Chicualacuala', 'Mapai'],
892
+ 'bairros': []
893
+ },
894
+ {
895
+ 'name': 'Chigubo',
896
+ 'postos_administrativos': ['Chigubo', 'Ndindiza'],
897
+ 'bairros': []
898
+ },
899
+ {
900
+ 'name': 'Chókwè',
901
+ 'postos_administrativos': ['Chókwè', 'Lionde', 'Macarretane'],
902
+ 'bairros': []
903
+ },
904
+ {
905
+ 'name': 'Chonguene',
906
+ 'postos_administrativos': ['Chonguene', 'Chongoene'],
907
+ 'bairros': []
908
+ },
909
+ {
910
+ 'name': 'Guijá',
911
+ 'postos_administrativos': ['Canicado', 'Chivonguene'],
912
+ 'bairros': []
913
+ },
914
+ {
915
+ 'name': 'Limpopo',
916
+ 'postos_administrativos': ['Chicumbane', 'Zongoene'],
917
+ 'bairros': []
918
+ },
919
+ {
920
+ 'name': 'Mabalane',
921
+ 'postos_administrativos': ['Mabalane', 'Combomune'],
922
+ 'bairros': []
923
+ },
924
+ {
925
+ 'name': 'Manjacaze',
926
+ 'postos_administrativos': ['Manjacaze', 'Chidenguele'],
927
+ 'bairros': []
928
+ },
929
+ {
930
+ 'name': 'Mapai',
931
+ 'postos_administrativos': ['Mapai', 'Machaila'],
932
+ 'bairros': []
933
+ },
934
+ {
935
+ 'name': 'Massangena',
936
+ 'postos_administrativos': ['Massangena', 'Mavue'],
937
+ 'bairros': []
938
+ },
939
+ {
940
+ 'name': 'Massingir',
941
+ 'postos_administrativos': ['Massingir', 'Zulo'],
942
+ 'bairros': []
943
+ },
944
+ {
945
+ 'name': 'Xai-Xai (Cidade)',
946
+ 'postos_administrativos': ['Xai-Xai'],
947
+ 'bairros': ['Central', 'Alto-Gaza', 'Inhamissa', 'Panjane', 'Chicumbane', 'Patrice Lumumba']
948
+ }
949
+ ]
950
+ },
951
+ {
952
+ 'id': 'mpp',
953
+ 'name': 'Maputo (Província)',
954
+ 'region': 'Sul',
955
+ 'sigla': 'MPT',
956
+ 'districts': [
957
+ {
958
+ 'name': 'Boane',
959
+ 'postos_administrativos': ['Boane', 'Matola-Rio'],
960
+ 'bairros': ['Bairro Central', 'Campinho', 'Massaca']
961
+ },
962
+ {
963
+ 'name': 'Magude',
964
+ 'postos_administrativos': ['Magude', 'Mapulanguene', 'Motaze'],
965
+ 'bairros': []
966
+ },
967
+ {
968
+ 'name': 'Manhiça',
969
+ 'postos_administrativos': ['Manhiça', 'Xinavane', '3 de Fevereiro'],
970
+ 'bairros': []
971
+ },
972
+ {
973
+ 'name': 'Marracuene',
974
+ 'postos_administrativos': ['Marracuene', 'Machubo'],
975
+ 'bairros': ['Aliança', 'Cumbe', 'Habel Jafar']
976
+ },
977
+ {
978
+ 'name': 'Matola (Cidade)',
979
+ 'postos_administrativos': ['Matola', 'Infulene', 'Machava'],
980
+ 'bairros': ['Matola Sede', 'Fomento', 'Liberdade', 'T3', 'Trevo', 'Machava Socimol', 'Cingatela']
981
+ },
982
+ {
983
+ 'name': 'Matutuíne',
984
+ 'postos_administrativos': ['Bela Vista', 'Catembe', 'Zitundo'],
985
+ 'bairros': []
986
+ },
987
+ {
988
+ 'name': 'Moamba',
989
+ 'postos_administrativos': ['Moamba', 'Ressano Garcia', 'Pessene'],
990
+ 'bairros': []
991
+ },
992
+ {
993
+ 'name': 'Namaacha',
994
+ 'postos_administrativos': ['Namaacha', 'Changalane'],
995
+ 'bairros': []
996
+ }
997
+ ]
998
+ },
999
+ {
1000
+ 'id': 'mpc',
1001
+ 'name': 'Maputo (Cidade)',
1002
+ 'region': 'Sul',
1003
+ 'sigla': 'MC',
1004
+ 'districts': [
1005
+ {
1006
+ 'name': 'KaMpfumo',
1007
+ 'postos_administrativos': ['KaMpfumo'],
1008
+ 'bairros': [
1009
+ 'Central A/B',
1010
+ 'Alto Maé A/B',
1011
+ 'Malhangalene A/B',
1012
+ 'Polana Cimento A/B/C',
1013
+ 'Coop',
1014
+ 'Sommerschield'
1015
+ ]
1016
+ },
1017
+ {
1018
+ 'name': 'Nlhamankulu',
1019
+ 'postos_administrativos': ['Nlhamankulu'],
1020
+ 'bairros': ['Aeroporto A/B', 'Chamanculo A/B/C/D', 'Malanga', 'Xipamanine', 'Munhuana', 'Unidade 7']
1021
+ },
1022
+ {
1023
+ 'name': 'KaMaxaquene',
1024
+ 'postos_administrativos': ['KaMaxaquene'],
1025
+ 'bairros': ['Maxaquene A/B/C/D', 'Polana Caniço A/B', 'Urbanização', 'Mafalala']
1026
+ },
1027
+ {
1028
+ 'name': 'KaMavota',
1029
+ 'postos_administrativos': ['KaMavota'],
1030
+ 'bairros': ['Mavalane A/B', 'FPLM', 'Hulene A/B', 'Ferroviário', 'Costa do Sol', 'Polana Caniço B']
1031
+ },
1032
+ {
1033
+ 'name': 'KaMubukwana',
1034
+ 'postos_administrativos': ['KaMubukwana'],
1035
+ 'bairros': ['Bagamoyo', 'George Dimitrov', 'Inhagoia A/B', 'Magoanine A/B/C', 'Zimpeto']
1036
+ },
1037
+ {
1038
+ 'name': 'KaTembe',
1039
+ 'postos_administrativos': ['KaTembe'],
1040
+ 'bairros': ['Gwaza Muthini', 'Incassane', 'Inguide', 'Chali', 'Chamissava']
1041
+ },
1042
+ {
1043
+ 'name': 'KaNyaka',
1044
+ 'postos_administrativos': ['KaNyaka'],
1045
+ 'bairros': ['Ribzene', 'Nghanyane', 'Chadwane']
1046
+ }
1047
+ ]
1048
+ }
1049
+ ]
1050
+
1051
+ def get_districts_by_province(province_id: str) -> List[str]:
1052
+ """Retorna a lista de distritos pertencentes a uma determinada província."""
1053
+ clean_id = province_id.strip().lower()
1054
+ for province in get_mozambique_provinces():
1055
+ if province['id'] == clean_id:
1056
+ return [d['name'] for d in province['districts']]
1057
+ raise ValueError(f"Província inválida: {province_id}")
1058
+
1059
+ def get_all_districts() -> List[Dict[str, Any]]:
1060
+ """Retorna uma lista plana com todos os 161 distritos e respetivos IDs de província."""
1061
+ flat_list = []
1062
+ for province in get_mozambique_provinces():
1063
+ p_id = province['id']
1064
+ for district in province['districts']:
1065
+ flat_list.append({
1066
+ 'name': district['name'],
1067
+ 'provinceId': p_id,
1068
+ 'postos_administrativos': district['postos_administrativos'],
1069
+ 'bairros': district['bairros']
1070
+ })
1071
+ return flat_list