|
1 | 1 | # coding: utf-8 |
| 2 | +""" |
| 3 | +O postmon-python é um wrapper da API do Postmon. |
| 4 | +
|
| 5 | +As chamadas devem ser feitas para as funções do módulo, que fazem as chamadas |
| 6 | +para o Postmon e retornam objetos com os resultados. Em caso de falha, todas as |
| 7 | +funções retornam `None`. |
| 8 | +""" |
| 9 | +from decimal import Decimal |
| 10 | +import logging |
| 11 | + |
2 | 12 | import requests |
3 | 13 |
|
4 | | -BASE_URL = 'http://api.postmon.com.br/v1' |
| 14 | +logger = logging.getLogger(__name__) |
| 15 | + |
| 16 | + |
| 17 | +class PostmonModel(object): |
| 18 | + """Objeto base para os modelos do Postmon.""" |
| 19 | + |
| 20 | + base_url = 'http://api.postmon.com.br/v1' |
| 21 | + |
| 22 | + def buscar(self): |
| 23 | + """Faz a busca das informações do objeto no Postmon. |
| 24 | +
|
| 25 | + Retorna um ``bool`` indicando se a busca foi bem sucedida. |
| 26 | + """ |
| 27 | + try: |
| 28 | + self._response = requests.get(self.url) |
| 29 | + except requests.RequestException: |
| 30 | + logger.exception("%s.buscar() falhou: GET %s" % |
| 31 | + (self.__class__.__name__, self.url)) |
| 32 | + return False |
| 33 | + |
| 34 | + if self._response.ok: |
| 35 | + self.atualizar(**self._response.json()) |
| 36 | + return self._response.ok |
| 37 | + |
| 38 | + @property |
| 39 | + def url(self): |
| 40 | + """Retorna a URL chamada pelo objeto. |
| 41 | +
|
| 42 | + >>> e = Endereco('11111111') |
| 43 | + >>> e.url |
| 44 | + 'http://api.postmon.com.br/v1/cep/11111111' |
| 45 | + """ |
| 46 | + return self.base_url + (self.endpoint % self._params) |
| 47 | + |
| 48 | + @property |
| 49 | + def status(self): |
| 50 | + """Status da resposta recebida do Postmon. |
5 | 51 |
|
| 52 | + Os status previstos pelo Postmon são: |
6 | 53 |
|
7 | | -class Cidade(object): |
| 54 | + * ``200 OK`` |
| 55 | + * ``404 CEP NAO ENCONTRADO`` |
| 56 | + * ``503 SERVICO INDISPONIVEL`` |
8 | 57 |
|
9 | | - def __init__(self, nome, area_km2=None, codigo_ibge=None): |
| 58 | + Além dos status listados, outros status HTTP podem ocorrer, como |
| 59 | + em qualquer chamada HTTP. |
| 60 | +
|
| 61 | + O único caso de sucesso é o ``200 OK``, caso em que o resultado no |
| 62 | + objeto é válido e pode ser utilizado. |
| 63 | + """ |
| 64 | + try: |
| 65 | + r = self._response |
| 66 | + except AttributeError: |
| 67 | + return None |
| 68 | + else: |
| 69 | + return r.status_code, r.reason |
| 70 | + |
| 71 | + @property |
| 72 | + def _ok(self): |
| 73 | + """Retorna ``True`` ou ``False``, indicando se a busca funcionou |
| 74 | + corretamente. |
| 75 | +
|
| 76 | + Retorna ``None`` caso o ``buscar`` ainda não tenha sido chamado. |
| 77 | + """ |
| 78 | + try: |
| 79 | + r = self._response |
| 80 | + except AttributeError: |
| 81 | + return None |
| 82 | + else: |
| 83 | + return r.ok |
| 84 | + |
| 85 | + |
| 86 | +class Cidade(PostmonModel): |
| 87 | + """ |
| 88 | + Objeto que representa uma cidade do Postmon. |
| 89 | + """ |
| 90 | + endpoint = '/cidade/%s/%s' |
| 91 | + |
| 92 | + def __init__(self, uf, nome, area_km2=None, codigo_ibge=None, **kwargs): |
| 93 | + self.uf = uf |
10 | 94 | self.nome = nome |
| 95 | + self._params = (uf, nome) |
| 96 | + self.atualizar(area_km2, codigo_ibge, **kwargs) |
| 97 | + |
| 98 | + def atualizar(self, area_km2, codigo_ibge, **kwargs): |
11 | 99 | self.area_km2 = area_km2 |
12 | 100 | self.codigo_ibge = codigo_ibge |
13 | 101 |
|
| 102 | + @property |
| 103 | + def area_km2(self): |
| 104 | + return self._area_km2 |
| 105 | + |
| 106 | + @area_km2.setter |
| 107 | + def area_km2(self, value): |
| 108 | + self._area_km2 = _parse_area_km2(value) |
| 109 | + |
14 | 110 | def __repr__(self): |
15 | | - return '<postmon.Cidade %r>' % self.nome |
| 111 | + return '<%s %r>' % (self.__class__.__name__, self.nome) |
16 | 112 |
|
17 | 113 | def __str__(self): |
18 | | - return self.nome |
| 114 | + return '%s - %s' % (self.nome, self.uf) |
19 | 115 |
|
20 | 116 |
|
21 | | -class Estado(object): |
| 117 | +class Estado(PostmonModel): |
| 118 | + """ |
| 119 | + Objeto que representa um estado do Postmon. |
| 120 | + """ |
| 121 | + endpoint = '/uf/%s' |
22 | 122 |
|
23 | | - def __init__(self, uf, nome=None, area_km2=None, codigo_ibge=None): |
| 123 | + def __init__(self, uf, nome=None, area_km2=None, codigo_ibge=None, |
| 124 | + **kwargs): |
24 | 125 | self.uf = uf |
| 126 | + self._params = uf |
| 127 | + self.atualizar(nome, area_km2, codigo_ibge, **kwargs) |
| 128 | + |
| 129 | + def atualizar(self, nome, area_km2, codigo_ibge, **kwargs): |
25 | 130 | self.nome = nome |
26 | 131 | self.area_km2 = area_km2 |
27 | 132 | self.codigo_ibge = codigo_ibge |
28 | 133 |
|
| 134 | + @property |
| 135 | + def area_km2(self): |
| 136 | + return self._area_km2 |
| 137 | + |
| 138 | + @area_km2.setter |
| 139 | + def area_km2(self, value): |
| 140 | + self._area_km2 = _parse_area_km2(value) |
| 141 | + |
29 | 142 | def __repr__(self): |
30 | | - return '<postmon.Estado %r>' % self.uf |
| 143 | + return '<%s %r>' % (self.__class__.__name__, self.uf) |
31 | 144 |
|
32 | 145 | def __str__(self): |
33 | 146 | return self.uf |
34 | 147 |
|
35 | 148 |
|
36 | | -class Endereco(object): |
| 149 | +class Endereco(PostmonModel): |
| 150 | + """ |
| 151 | + Objeto que representa um endereço do Postmon. |
| 152 | +
|
| 153 | + O ``Endereco`` pode ser criado apenas com o CEP para posteriormente |
| 154 | + ser buscado. |
| 155 | +
|
| 156 | + >>> import postmon |
| 157 | + >>> e = postmon.Endereco('30110-012') |
| 158 | + >>> if e.buscar(): |
| 159 | + ... print("Bairro: %s" % e.bairro) |
| 160 | + ... else: |
| 161 | + ... print("Busca falhou: %s" % e.status) |
| 162 | + Bairro: Floresta |
| 163 | + """ |
| 164 | + endpoint = '/cep/%s' |
| 165 | + |
| 166 | + def __init__(self, cep, logradouro=None, bairro=None, cidade=None, |
| 167 | + estado=None, cidade_info=None, estado_info=None, **kwargs): |
| 168 | + self.cep = cep |
| 169 | + self._params = cep |
| 170 | + self.atualizar(logradouro=logradouro, bairro=bairro, cidade=cidade, |
| 171 | + estado=estado, cidade_info=cidade_info, |
| 172 | + estado_info=estado_info, **kwargs) |
37 | 173 |
|
38 | | - def __init__(self, **kwargs): |
39 | | - self.cep = kwargs['cep'] |
40 | | - self.logradouro = kwargs.get('logradouro') |
41 | | - self.bairro = kwargs.get('bairro') |
| 174 | + def atualizar(self, logradouro=None, bairro=None, cidade=None, |
| 175 | + estado=None, cidade_info=None, estado_info=None, **kwargs): |
| 176 | + self.logradouro = logradouro |
| 177 | + self.bairro = bairro |
42 | 178 |
|
43 | | - estado_info = kwargs.get('estado_info', {}) |
44 | | - self.estado = Estado(kwargs['estado'], |
45 | | - estado_info.get('nome'), |
46 | | - estado_info.get('area_km2'), |
47 | | - estado_info.get('codigo_ibge')) |
| 179 | + if estado: |
| 180 | + if not estado_info: |
| 181 | + estado_info = {} |
| 182 | + self.estado = Estado(estado, |
| 183 | + estado_info.get('nome'), |
| 184 | + estado_info.get('area_km2'), |
| 185 | + estado_info.get('codigo_ibge')) |
48 | 186 |
|
49 | | - cidade_info = kwargs.get('cidade_info', {}) |
50 | | - self.cidade = Cidade(kwargs['cidade'], |
51 | | - cidade_info.get('area_km2'), |
52 | | - cidade_info.get('codigo_ibge')) |
| 187 | + if cidade: |
| 188 | + if not cidade_info: |
| 189 | + cidade_info = {} |
| 190 | + self.cidade = Cidade(estado, cidade, |
| 191 | + cidade_info.get('area_km2'), |
| 192 | + cidade_info.get('codigo_ibge')) |
53 | 193 |
|
54 | 194 | def __repr__(self): |
55 | | - return '<postmon.Endereco %r>' % self.cep |
| 195 | + return '<%s %r>' % (self.__class__.__name__, self.cep) |
56 | 196 |
|
57 | 197 | def __str__(self): |
58 | 198 | return '%s, %s - %s, %s - CEP: %s' % (self.logradouro, self.bairro, |
59 | 199 | self.cidade, self.estado, |
60 | 200 | self.cep) |
61 | 201 |
|
62 | 202 |
|
63 | | -def buscar_cep(cep): |
64 | | - response = _GET('/cep/%s' % cep) |
65 | | - response.raise_for_status() |
66 | | - return Endereco(**response.json()) |
| 203 | +def cidade(uf, nome): |
| 204 | + """Busca a cidade no Postmon e retorna um objeto ``Cidade``. |
| 205 | +
|
| 206 | + Retorna ``None`` caso a cidade não exista ou caso ocorra algum erro de |
| 207 | + comunicação. |
| 208 | +
|
| 209 | + >>> import postmon |
| 210 | + >>> postmon.cidade('MG', 'Belo Horizonte') |
| 211 | + <Cidade 'Belo Horizonte'> |
| 212 | + """ |
| 213 | + return _make_object(Cidade, uf, nome) |
| 214 | + |
| 215 | + |
| 216 | +def estado(uf): |
| 217 | + """Busca o estado no Postmon e retorna um objeto ``Estado``. |
| 218 | +
|
| 219 | + Retorna ``None`` caso o estado não exista ou caso ocorra algum erro de |
| 220 | + comunicação. |
| 221 | +
|
| 222 | + >>> import postmon |
| 223 | + >>> postmon.estado('MG') |
| 224 | + <Estado 'MG'> |
| 225 | + """ |
| 226 | + return _make_object(Estado, uf) |
| 227 | + |
| 228 | + |
| 229 | +def endereco(cep): |
| 230 | + """Busca o CEP no Postmon e retorna um objeto ``Endereco``. |
| 231 | +
|
| 232 | + Retorna ``None`` caso o CEP não exista ou caso ocorra algum erro de |
| 233 | + comunicação. |
| 234 | +
|
| 235 | + >>> import postmon |
| 236 | + >>> postmon.endereco('01001-000') |
| 237 | + <Endereco '01001-000'> |
| 238 | + """ |
| 239 | + return _make_object(Endereco, cep) |
| 240 | + |
| 241 | + |
| 242 | +def _make_object(cls, *args): |
| 243 | + obj = cls(*args) |
| 244 | + return obj if obj.buscar() else None |
| 245 | + |
| 246 | + |
| 247 | +def _parse_area_km2(valor): |
| 248 | + """O campo ``area_km2`` é uma string com um número em formato pt-br, com |
| 249 | + casas decimais que representam m2. |
| 250 | +
|
| 251 | + Exemplos: "331,401", "248.222,801" |
| 252 | + """ |
| 253 | + if valor is None: |
| 254 | + return None |
| 255 | + elif isinstance(valor, Decimal): |
| 256 | + return valor |
| 257 | + try: |
| 258 | + int_, dec = valor.split(',', 1) |
| 259 | + except ValueError: |
| 260 | + # valor não tem separador decimal |
| 261 | + int_, dec = valor, '000' |
67 | 262 |
|
| 263 | + # remove os separadores de milhar |
| 264 | + int_ = int_.replace('.', '') |
68 | 265 |
|
69 | | -def _GET(endpoint): |
70 | | - url = '%s%s' % (BASE_URL, endpoint) |
71 | | - response = requests.get(url) |
72 | | - return response |
| 266 | + return Decimal('%s.%s' % (int_, dec)) |
0 commit comments