Skip to content

Commit d6bff1e

Browse files
feat(ibge): adiciona utilitário convert_name_to_uf (#606)
Adiciona função para converter nome completo de estados brasileiros para código UF. A conversão ignora diferenças de maiúsculas e acentos, permitindo buscas flexíveis por nomes de estados. Co-authored-by: Nilton Pimentel <63605485+niltonpimentel02@users.noreply.github.com>
1 parent df5297d commit d6bff1e

6 files changed

Lines changed: 279 additions & 28 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Utilitário `convert_name_to_uf`
13+
1014
## [2.3.0] - 2025-10-07
1115

1216
### Added

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ False
9393
- [IBGE](#ibge)
9494
- [convert_code_to_uf](#convert_code_to_uf)
9595
- [convert_uf_to_name](#convert_uf_to_name)
96+
- [convert_name_to_uf](#convert_name_to_uf)
9697
- [get_code_by_municipality_name](#get_code_by_municipality_name)
9798
- [get\_municipality\_by\_code](#get_municipality_by_code)
9899
- [Feriados](#feriados)
@@ -1219,6 +1220,33 @@ Exemplo:
12191220
'Rio de Janeiro'
12201221
```
12211222

1223+
### convert_name_to_uf
1224+
Converte o nome completo de um estado brasileiro para seu código UF.
1225+
1226+
Esta função recebe o nome completo de um estado brasileiro e retorna o código UF de duas letras correspondente. A comparação ignora maiúsculas/minúsculas e acentos.
1227+
1228+
Argumentos:
1229+
* state_name (str): O nome completo do estado (por exemplo, 'São Paulo', 'sao paulo').
1230+
1231+
Retorna:
1232+
* str | None: O código UF se encontrado, ou None se o nome do estado for inválido.
1233+
1234+
Exemplo:
1235+
1236+
```python
1237+
>>> from brutils.ibge.uf import convert_name_to_uf
1238+
>>> convert_name_to_uf('São Paulo')
1239+
'SP'
1240+
>>> convert_name_to_uf('sao paulo')
1241+
'SP'
1242+
>>> convert_name_to_uf('Rio de Janeiro')
1243+
'RJ'
1244+
>>> convert_name_to_uf('rio de janeiro')
1245+
'RJ'
1246+
>>> convert_name_to_uf('Estado Inválido')
1247+
>>>
1248+
```
1249+
12221250
## Feriados
12231251

12241252
### is_holiday

README_EN.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,10 @@ False
9191
- [generate_voter_id](#generate_voter_id)
9292
- [IBGE](#ibge)
9393
- [convert_code_to_uf](#convert_code_to_uf)
94+
- [convert_uf_to_name](#convert_uf_to_name)
95+
- [convert_name_to_uf](#convert_name_to_uf)
9496
- [get_code_by_municipality_name](#get_code_by_municipality_name)
9597
- [get\_municipality\_by\_code](#get_municipality_by_code)
96-
- [convert_uf_to_name](#convert_uf_to_name)
9798
- [Holidays](#holidays)
9899
- [is_holiday](#is_holiday)
99100
- [Monetary](#monetary)
@@ -1222,6 +1223,33 @@ Example:
12221223
'Rio de Janeiro'
12231224
```
12241225

1226+
### convert_name_to_uf
1227+
Converts a Brazilian state name to its UF code.
1228+
1229+
This function takes the full name of a Brazilian state and returns its corresponding two-letter UF code. The comparison is case-insensitive and ignores accents.
1230+
1231+
Args:
1232+
* state_name (str): The full name of the state (e.g., 'São Paulo', 'sao paulo').
1233+
1234+
Returns:
1235+
* str | None: The UF code if found, or None if the state name is invalid.
1236+
1237+
Example:
1238+
1239+
```python
1240+
>>> from brutils.ibge.uf import convert_name_to_uf
1241+
>>> convert_name_to_uf('São Paulo')
1242+
'SP'
1243+
>>> convert_name_to_uf('sao paulo')
1244+
'SP'
1245+
>>> convert_name_to_uf('Rio de Janeiro')
1246+
'RJ'
1247+
>>> convert_name_to_uf('rio de janeiro')
1248+
'RJ'
1249+
>>> convert_name_to_uf('Invalid State')
1250+
>>>
1251+
```
1252+
12251253
## Holidays
12261254

12271255
### is_holiday

brutils/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@
3434
get_code_by_municipality_name,
3535
get_municipality_by_code,
3636
)
37-
from brutils.ibge.uf import convert_code_to_uf, convert_uf_to_name
37+
from brutils.ibge.uf import (
38+
convert_code_to_uf,
39+
convert_name_to_uf,
40+
convert_uf_to_name,
41+
)
3842

3943
# Legal Process Imports
4044
from brutils.legal_process import format_legal_process
@@ -122,6 +126,7 @@
122126
"is_valid_voter_id",
123127
# IBGE
124128
"convert_code_to_uf",
129+
"convert_name_to_uf",
125130
"convert_uf_to_name",
126131
"get_code_by_municipality_name",
127132
"get_municipality_by_code",

brutils/ibge/uf.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import unicodedata
2+
13
from brutils.data.enums.uf import UF, UF_CODE
24

35

@@ -59,3 +61,60 @@ def convert_uf_to_name(uf: str) -> str | None:
5961
result = UF[federal_unit].value
6062

6163
return result
64+
65+
66+
def _normalize_text(text: str) -> str:
67+
"""
68+
Normalize text by removing accents and normalizing whitespace.
69+
70+
Args:
71+
text (str): The text to normalize.
72+
73+
Returns:
74+
str: The normalized text in uppercase.
75+
"""
76+
nfd = unicodedata.normalize("NFD", text)
77+
without_accents = "".join(
78+
char for char in nfd if unicodedata.category(char) != "Mn"
79+
)
80+
81+
normalized_spaces = " ".join(without_accents.split())
82+
return normalized_spaces.upper()
83+
84+
85+
def convert_name_to_uf(state_name: str) -> str | None:
86+
"""
87+
Convert a Brazilian state name to its UF code.
88+
89+
This function takes the full name of a Brazilian state and returns its
90+
corresponding two-letter UF code. The comparison is case-insensitive and
91+
ignores accents.
92+
93+
Args:
94+
state_name (str): The full name of the state (e.g., 'São Paulo', 'sao paulo').
95+
96+
Returns:
97+
str | None: The UF code if found, or None if the state name is invalid.
98+
99+
Examples:
100+
>>> convert_name_to_uf('São Paulo')
101+
'SP'
102+
>>> convert_name_to_uf('sao paulo')
103+
'SP'
104+
>>> convert_name_to_uf('Rio de Janeiro')
105+
'RJ'
106+
>>> convert_name_to_uf('rio de janeiro')
107+
'RJ'
108+
>>> convert_name_to_uf('Estado Inválido')
109+
>>>
110+
"""
111+
if not state_name or not isinstance(state_name, str):
112+
return None
113+
114+
normalized_input = _normalize_text(state_name.strip())
115+
116+
for uf in UF:
117+
if _normalize_text(uf.value) == normalized_input:
118+
return uf.name
119+
120+
return None

tests/ibge/test_uf.py

Lines changed: 153 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,158 @@
11
from unittest import TestCase
22

3-
from brutils.ibge.uf import convert_code_to_uf, convert_uf_to_name
3+
from brutils.ibge.uf import (
4+
convert_code_to_uf,
5+
convert_name_to_uf,
6+
convert_uf_to_name,
7+
)
48

59

610
class TestUF(TestCase):
7-
def test_convert_code_to_uf(self):
8-
# Testes para códigos válidos
9-
self.assertEqual(convert_code_to_uf("12"), "AC")
10-
self.assertEqual(convert_code_to_uf("33"), "RJ")
11-
self.assertEqual(convert_code_to_uf("31"), "MG")
12-
self.assertEqual(convert_code_to_uf("52"), "GO")
13-
14-
# Testes para códigos inválidos
15-
self.assertIsNone(convert_code_to_uf("99"))
16-
self.assertIsNone(convert_code_to_uf("00"))
17-
self.assertIsNone(convert_code_to_uf(""))
18-
self.assertIsNone(convert_code_to_uf("AB"))
19-
20-
def test_convert_uf_to_name(self):
21-
# Testes para códigos válidos
22-
self.assertEqual(convert_uf_to_name("SP"), "São Paulo")
23-
self.assertEqual(convert_uf_to_name("RJ"), "Rio de Janeiro")
24-
self.assertEqual(convert_uf_to_name("MG"), "Minas Gerais")
25-
self.assertEqual(convert_uf_to_name("DF"), "Distrito Federal")
26-
self.assertEqual(convert_uf_to_name("df"), "Distrito Federal")
27-
28-
# Testes para códigos inválidos
29-
self.assertIsNone(convert_code_to_uf("XX"))
30-
self.assertIsNone(convert_code_to_uf("XXX"))
31-
self.assertIsNone(convert_code_to_uf(""))
11+
def test_convert_code_to_uf_valid(self):
12+
test_cases = [
13+
("12", "AC"),
14+
("27", "AL"),
15+
("16", "AP"),
16+
("13", "AM"),
17+
("29", "BA"),
18+
("23", "CE"),
19+
("53", "DF"),
20+
("32", "ES"),
21+
("52", "GO"),
22+
("21", "MA"),
23+
("51", "MT"),
24+
("50", "MS"),
25+
("31", "MG"),
26+
("15", "PA"),
27+
("25", "PB"),
28+
("41", "PR"),
29+
("26", "PE"),
30+
("22", "PI"),
31+
("33", "RJ"),
32+
("24", "RN"),
33+
("43", "RS"),
34+
("11", "RO"),
35+
("14", "RR"),
36+
("42", "SC"),
37+
("35", "SP"),
38+
("28", "SE"),
39+
("17", "TO"),
40+
]
41+
for code, expected_uf in test_cases:
42+
with self.subTest(code=code):
43+
self.assertEqual(convert_code_to_uf(code), expected_uf)
44+
45+
def test_convert_code_to_uf_invalid(self):
46+
invalid_codes = ["99", "00", "", "AB", "1", "123"]
47+
for invalid_code in invalid_codes:
48+
with self.subTest(code=invalid_code):
49+
self.assertIsNone(convert_code_to_uf(invalid_code))
50+
51+
def test_convert_uf_to_name_valid(self):
52+
test_cases = [
53+
("SP", "São Paulo"),
54+
("RJ", "Rio de Janeiro"),
55+
("MG", "Minas Gerais"),
56+
("DF", "Distrito Federal"),
57+
("BA", "Bahia"),
58+
("RS", "Rio Grande do Sul"),
59+
]
60+
for uf, expected_name in test_cases:
61+
with self.subTest(uf=uf):
62+
self.assertEqual(convert_uf_to_name(uf), expected_name)
63+
64+
def test_convert_uf_to_name_case_insensitive(self):
65+
test_cases = [
66+
("sp", "São Paulo"),
67+
("df", "Distrito Federal"),
68+
("Rj", "Rio de Janeiro"),
69+
]
70+
for uf, expected_name in test_cases:
71+
with self.subTest(uf=uf):
72+
self.assertEqual(convert_uf_to_name(uf), expected_name)
73+
74+
def test_convert_uf_to_name_invalid(self):
75+
invalid_ufs = ["XX", "XXX", "", "A", "123", " "]
76+
for invalid_uf in invalid_ufs:
77+
with self.subTest(uf=invalid_uf):
78+
self.assertIsNone(convert_uf_to_name(invalid_uf))
79+
80+
def test_convert_name_to_uf_all_states(self):
81+
test_cases = [
82+
("Acre", "AC"),
83+
("Alagoas", "AL"),
84+
("Amapá", "AP"),
85+
("Amazonas", "AM"),
86+
("Bahia", "BA"),
87+
("Ceará", "CE"),
88+
("Distrito Federal", "DF"),
89+
("Espírito Santo", "ES"),
90+
("Goiás", "GO"),
91+
("Maranhão", "MA"),
92+
("Mato Grosso", "MT"),
93+
("Mato Grosso do Sul", "MS"),
94+
("Minas Gerais", "MG"),
95+
("Pará", "PA"),
96+
("Paraíba", "PB"),
97+
("Paraná", "PR"),
98+
("Pernambuco", "PE"),
99+
("Piauí", "PI"),
100+
("Rio de Janeiro", "RJ"),
101+
("Rio Grande do Norte", "RN"),
102+
("Rio Grande do Sul", "RS"),
103+
("Rondônia", "RO"),
104+
("Roraima", "RR"),
105+
("Santa Catarina", "SC"),
106+
("São Paulo", "SP"),
107+
("Sergipe", "SE"),
108+
("Tocantins", "TO"),
109+
]
110+
for state_name, expected_uf in test_cases:
111+
with self.subTest(state_name=state_name):
112+
self.assertEqual(convert_name_to_uf(state_name), expected_uf)
113+
114+
def test_convert_name_to_uf_normalization(self):
115+
test_cases = [
116+
("sao paulo", "SP"),
117+
("SAO PAULO", "SP"),
118+
("SãO pAuLo", "SP"),
119+
(" Rio de Janeiro ", "RJ"),
120+
("ceara", "CE"),
121+
("PARANÁ", "PR"),
122+
("parana", "PR"),
123+
]
124+
for state_name, expected_uf in test_cases:
125+
with self.subTest(state_name=state_name):
126+
self.assertEqual(convert_name_to_uf(state_name), expected_uf)
127+
128+
def test_convert_name_to_uf_wrong_spacing(self):
129+
test_cases = [
130+
("Sao Paulo", "SP"),
131+
("Rio de Janeiro", "RJ"),
132+
("Mato Grosso", "MT"),
133+
("Rio Grande do Sul", "RS"),
134+
("Mato Grosso do Sul", "MS"),
135+
("Espírito Santo", "ES"),
136+
("Rio Grande do Norte", "RN"),
137+
]
138+
for state_name, expected_uf in test_cases:
139+
with self.subTest(state_name=state_name):
140+
self.assertEqual(convert_name_to_uf(state_name), expected_uf)
141+
142+
def test_convert_name_to_uf_invalid_input(self):
143+
invalid_inputs = [
144+
None,
145+
"",
146+
" ",
147+
"Estado Inválido",
148+
"São Pauloo",
149+
"Rio",
150+
"Brasil",
151+
"XYZ",
152+
123,
153+
[],
154+
{},
155+
]
156+
for invalid_input in invalid_inputs:
157+
with self.subTest(input=repr(invalid_input)):
158+
self.assertIsNone(convert_name_to_uf(invalid_input))

0 commit comments

Comments
 (0)