Skip to content

Commit 02f0ce2

Browse files
authored
refactor: busca de municípios (#595)
1 parent fc4db81 commit 02f0ce2

2 files changed

Lines changed: 323 additions & 140 deletions

File tree

brutils/ibge/municipality.py

Lines changed: 91 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import gzip
22
import io
33
import json
4-
import pathlib
54
import unicodedata
65
from urllib.error import HTTPError
76
from urllib.request import urlopen
87

8+
IBGE_MUNICIPALITY_BY_CODE_URL = (
9+
"https://servicodados.ibge.gov.br/api/v1/localidades/municipios/{code}"
10+
)
11+
IBGE_MUNICIPALITIES_BY_UF_URL = "https://servicodados.ibge.gov.br/api/v1/localidades/estados/{uf}/municipios"
912

10-
def get_municipality_by_code(code): # type: (str) -> Tuple[str, str] | None
13+
14+
def get_municipality_by_code(code: str) -> tuple[str, str] | None:
1115
"""
1216
Returns the municipality name and UF for a given IBGE code.
1317
@@ -18,62 +22,34 @@ def get_municipality_by_code(code): # type: (str) -> Tuple[str, str] | None
1822
code (str): The IBGE code of the municipality.
1923
2024
Returns:
21-
tuple: A tuple formatted as ("Município", "UF").
22-
- Returns None if the code is not valid.
25+
tuple: A tuple formatted as ("Município", "UF") or
26+
None if the code is not valid.
2327
2428
Example:
2529
>>> get_municipality_by_code("3550308")
2630
("São Paulo", "SP")
31+
>>> get_municipality_by_code("3304557")
32+
("Rio de Janeiro", "RJ")
33+
>>> get_municipality_by_code("1234567")
34+
None
2735
"""
28-
baseUrl = (
29-
f"https://servicodados.ibge.gov.br/api/v1/localidades/municipios/{code}"
30-
)
31-
try:
32-
with urlopen(baseUrl) as f:
33-
compressed_data = f.read()
34-
if f.info().get("Content-Encoding") == "gzip":
35-
try:
36-
with gzip.GzipFile(
37-
fileobj=io.BytesIO(compressed_data)
38-
) as gzip_file:
39-
decompressed_data = gzip_file.read()
40-
except OSError as e:
41-
print(f"Erro ao descomprimir os dados: {e}")
42-
return None
43-
except Exception as e:
44-
print(f"Erro desconhecido ao descomprimir os dados: {e}")
45-
return None
46-
else:
47-
decompressed_data = compressed_data
36+
base_url = IBGE_MUNICIPALITY_BY_CODE_URL.format(code=code)
4837

49-
if _is_empty(decompressed_data):
50-
print(f"{code} é um código inválido")
51-
return None
38+
decompressed_data = _fetch_ibge_data(base_url)
5239

53-
except HTTPError as e:
54-
if e.code == 404:
55-
print(f"{code} é um código inválido")
56-
return None
57-
else:
58-
print(f"Erro HTTP ao buscar o código {code}: {e}")
59-
return None
60-
61-
except Exception as e:
62-
print(f"Erro desconhecido ao buscar o código {code}: {e}")
40+
if decompressed_data is None:
6341
return None
6442

6543
try:
6644
json_data = json.loads(decompressed_data)
6745
return _get_values(json_data)
68-
except json.JSONDecodeError as e:
69-
print(f"Erro ao decodificar os dados JSON: {e}")
70-
return None
71-
except KeyError as e:
72-
print(f"Erro ao acessar os dados do município: {e}")
46+
except (json.JSONDecodeError, KeyError):
7347
return None
7448

7549

76-
def get_code_by_municipality_name(municipality_name: str, uf: str): # type: (str, str) -> str | None
50+
def get_code_by_municipality_name(
51+
municipality_name: str, uf: str
52+
) -> str | None:
7753
"""
7854
Returns the IBGE code for a given municipality name and uf code.
7955
@@ -87,76 +63,109 @@ def get_code_by_municipality_name(municipality_name: str, uf: str): # type: (st
8763
uf (str): The uf code of the state.
8864
8965
Returns:
90-
str: The IBGE code of the municipality.
91-
- Returns None if the name is not valid or does not exist.
66+
str: The IBGE code of the municipality or
67+
None if the name is not valid or does not exist.
9268
9369
Example:
9470
>>> get_code_by_municipality_name("São Paulo", "SP")
9571
"3550308"
96-
>>> get_code_by_municipality_name("goiania", "go")
97-
"5208707"
98-
>>> get_code_by_municipality_name("Conceição do Coité", "BA")
72+
>>> get_code_by_municipality_name("Conceição do Coité", "Ba")
9973
"2908408"
100-
>>> get_code_by_municipality_name("conceicao do Coite", "Ba")
101-
"2908408"
102-
>>> get_code_by_municipality_name("Municipio Inexistente", "")
103-
None
10474
>>> get_code_by_municipality_name("Municipio Inexistente", "RS")
10575
None
10676
"""
107-
108-
abs_path = pathlib.Path(__file__).resolve()
109-
script_dir = abs_path.parent.parent
110-
111-
json_cities_code_path = script_dir / "data" / "cities_code.json"
11277
uf = uf.upper()
11378

114-
with open(json_cities_code_path, "r", encoding="utf-8") as file:
115-
cities_uf_code = json.load(file)
79+
base_url = IBGE_MUNICIPALITIES_BY_UF_URL.format(uf=uf)
11680

117-
if uf not in cities_uf_code.keys():
81+
decompressed_data = _fetch_ibge_data(base_url)
82+
if decompressed_data is None:
11883
return None
11984

120-
cities_code = cities_uf_code.get(uf)
121-
name_city = _transform_text(municipality_name)
85+
try:
86+
json_data = json.loads(decompressed_data)
87+
normalized_municipality_name = _transform_text(municipality_name)
88+
89+
for municipality in json_data:
90+
municipality_name_from_api = municipality.get("nome", "")
91+
normalized_name_from_api = _transform_text(
92+
municipality_name_from_api
93+
)
94+
95+
if normalized_name_from_api == normalized_municipality_name:
96+
return str(municipality.get("id"))
12297

123-
if name_city not in cities_code.keys():
12498
return None
12599

126-
code = cities_code.get(name_city)
100+
except (json.JSONDecodeError, KeyError):
101+
return None
127102

128-
return code
129103

104+
def _fetch_ibge_data(url: str) -> bytes | None:
105+
"""
106+
Fetch data from IBGE API with gzip decompression support.
130107
131-
def _get_values(data):
108+
Args:
109+
url (str): The URL to fetch data from.
110+
111+
Returns:
112+
bytes | None: The decompressed data or None if failed.
113+
"""
114+
try:
115+
with urlopen(url) as f:
116+
compressed_data = f.read()
117+
if f.info().get("Content-Encoding") == "gzip":
118+
try:
119+
with gzip.GzipFile(
120+
fileobj=io.BytesIO(compressed_data)
121+
) as gzip_file:
122+
decompressed_data = gzip_file.read()
123+
except (OSError, Exception):
124+
return None
125+
else:
126+
decompressed_data = compressed_data
127+
128+
if _is_empty(decompressed_data):
129+
return None
130+
131+
return decompressed_data
132+
133+
except HTTPError:
134+
return None
135+
except Exception:
136+
return None
137+
138+
139+
def _get_values(data: dict) -> tuple[str, str]:
140+
"""Extract municipality name and UF from IBGE API response."""
132141
municipio = data["nome"]
133142
estado = data["microrregiao"]["mesorregiao"]["UF"]["sigla"]
134143
return (municipio, estado)
135144

136145

137-
def _is_empty(zip):
138-
return zip == b"[]" or len(zip) == 0
146+
def _is_empty(data: bytes) -> bool:
147+
"""Check if the response data is empty."""
148+
return data == b"[]" or len(data) == 0
139149

140150

141-
def _transform_text(municipality_name: str): # type: (str) -> str
151+
def _transform_text(municipality_name: str) -> str:
142152
"""
143153
Normalize municipality name and returns the normalized string.
144154
145155
Args:
146-
municipality_name (str): The name of the municipality.
147-
148-
Returns:
149-
str: The normalized string
150-
151-
Example:
152-
>>> _transform_text("São Paulo")
153-
"sao paulo"
154-
>>> _transform_text("Goiânia")
155-
"goiania"
156-
>>> _transform_text("Conceição do Coité")
157-
"'conceicao do coite'
158-
"""
156+
municipality_name (str): The name of the municipality.
159157
158+
Returns:
159+
str: The normalized string
160+
161+
Example:
162+
>>> _transform_text("São Paulo")
163+
'sao paulo'
164+
>>> _transform_text("Goiânia")
165+
'goiania'
166+
>>> _transform_text("Conceição do Coité")
167+
'conceicao do coite'
168+
"""
160169
normalized_string = (
161170
unicodedata.normalize("NFKD", municipality_name)
162171
.encode("ascii", "ignore")

0 commit comments

Comments
 (0)