forked from MatCarvalho21/trabalho_a1_lp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathget_data.py
326 lines (266 loc) · 13.5 KB
/
get_data.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
"""Módulo que coleta os dados de Venda de Medicamentos Controlados e Antimicrobianos (Medicamentos Manipulados)
do site "dados.gov.br", dados disponíveis de Janeiro de 2014 até Novembro de 2021.
Para a plena execução deste módulo, é necessário ter certeza de que o domínio
https://dados.gov.br/dados/conjuntos-dados/venda-de-medicamentos-controlados-e-antimicrobianos---medicamentos-manipulados
se encontra em pleno funcionamente e não está fora do ar, além disso, não houve alteração na forma como os dados são armazenados.
"""
import sys, os
esse_caminho = os.path.dirname(os.path.abspath(__file__))
sys.path.append(esse_caminho)
import pandas as pd
import os
import doctest
def validacao_datas(data_inicial:str, data_final:str) -> True:
"""Recebe duas datas e as valida para o formato desejado.
Duas strings de datas, sendo que os primeiros 4 dígitos devem ser do ano e os últimos 2 do mês,
a primeira data é a data inicial e a segunda a data final, a data final deve ser maior ou igual a data inicial,
as duas datas devem estar entre Janeiro de 2014 (2014/01) e Novembro de 2021 (2021/11).
Parameters
----------
data_inicial : str
A primeira data a ser validada.
data_final : str
A segunda data a ser validada, esta data deve ser posterior a primeira.
Returns
-------
True
Caso nenhum erro seja encontrado, ou seja, as datas sejam válidas
Raises
------
TypeError
Tipo icorreto de datas.
NameError
Formato icorreto de datas.
ValueError
Problemas com os valores das datas, não estão entre Janeiro de 2014 e Novembro de 2021.
IndexError
A segunda data (data final) é menor do que a primeira data (data inicial).
Test
----------
>>> validacao_datas("2014/01", "2014/01")
True
>>> validacao_datas("2014/01", "2021/06")
True
>>> validacao_datas(2014/1, "2014/01")
Tipo das datas inserido está incorreto, tente inserir a data como uma string, ex: '2015/05'
>>> validacao_datas("2021", "2021/01")
Formato das datas inserido está incorreto, revise novamente e tente inserir como ANO/mês, ex: '2015/05'.
>>> validacao_datas("incorreto", "2021/01")
Problemas com a primeira data inserida: inco/to
Formato da data está incorreto ou ela não está entre Janeiro de 2014 e Novembro de 2021, tente inserir como ANO/mês, ex: '2015/05'.
>>> validacao_datas("2014/01", "2022/01")
Problemas com a segunda data inserida: 2022/01
Formato da data está incorreto ou ela não está entre Janeiro de 2014 e Novembro de 2021, tente inserir como ANO/mês, ex: '2015/05'.
"""
anos_validos = ["2014", "2015", "2016", "2017", "2018", "2019", "2020", "2021"]
meses_validos = ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"]
try:
# Checa se o tipo da data é válido.
if type(data_inicial) != str or type(data_final) != str:
raise TypeError
if len(data_inicial) < 6 or len(data_final) < 6:
raise NameError
ano_incial, mes_inicial = data_inicial[:4], data_inicial[-2:]
ano_final, mes_final = data_final[:4], data_final[-2:]
# Checa se o formato da data é válido.
if ano_incial not in anos_validos or mes_inicial not in meses_validos:
print("Problemas com a primeira data inserida:", f"{ano_incial}/{mes_inicial}")
raise ValueError
elif ano_final not in anos_validos or mes_final not in meses_validos:
print("Problemas com a segunda data inserida:", f"{ano_final}/{mes_final}")
raise ValueError
elif mes_inicial == "12" and ano_incial == "2021":
raise ValueError
elif mes_final == "12" and ano_final == "2021":
raise ValueError
# Checa se a data final é maior ou igual a inicial
elif anos_validos.index(ano_final) < anos_validos.index(ano_incial):
raise IndexError
elif ano_final == ano_incial and meses_validos.index(mes_final) < meses_validos.index(mes_inicial):
raise IndexError
except TypeError:
print("Tipo das datas inserido está incorreto, tente inserir a data como uma string, ex: '2015/05'")
except NameError:
print("Formato das datas inserido está incorreto, revise novamente e tente inserir como ANO/mês, ex: '2015/05'.")
except ValueError:
print("Formato da data está incorreto ou ela não está entre Janeiro de 2014 e Novembro de 2021, tente inserir como ANO/mês, ex: '2015/05'.")
except IndexError:
print("A segunda data deve ser maior ou igual a primeira, ex: ('2014/01', '2014/01') ou ('2014/01', '2016/02')")
except Exception as err:
print("Outro erro encontrado:", err)
else:
return True
def get_dates_between_dates(data_inicial:str, data_final:str) -> list:
"""Recebe duas datas e retorna uma lista com todas as datas entre essas duas datas.
A segunda data deve ser posterior a primeira, e do formato "AAAA/mm", além disso, as datas devem
estar entre Janeiro de 2014 e Novembro de 2021. Possui as mesmas exceções da função "validacao_datas".
Parameters
----------
data_inicial : str
A primeira data a ser validada.
data_final : str
A segunda data a ser validada, esta data deve ser posterior a primeira.
Returns
-------
lista_datas : list
Uma lista com todas as datas entre a data inicial e a data final, as datas
estão no formato de string "AAAAmm". Caso as datas não sejam válidas, retorna uma lista vazia.
Test
----------
>>> get_dates_between_dates("2014/01", "2014/01")
['201401']
>>> get_dates_between_dates("2021/08", "2021/11")
['202108', '202109', '202110', '202111']
>>> get_dates_between_dates("2014/01", "2030/06")
Problemas com a segunda data inserida: 2030/06
Formato da data está incorreto ou ela não está entre Janeiro de 2014 e Novembro de 2021, tente inserir como ANO/mês, ex: '2015/05'.
[]
>>> get_dates_between_dates("2015/01", "2014/06")
A segunda data deve ser maior ou igual a primeira, ex: ('2014/01', '2014/01') ou ('2014/01', '2016/02')
[]
"""
lista_datas = []
try:
# Valida a data antes de proseguir com o código
if validacao_datas(data_inicial, data_final) != True:
raise Exception
except:
return lista_datas
else:
ano_inicial = int(data_inicial[:4])
mes_inicial = int(data_inicial[-2:])
ano_final = int(data_final[:4])
mes_final = int(data_final[-2:])
# loop para adicionar todas as datas no formato "AAAAmm" à lista.
while ano_inicial <= ano_final:
data_atual = f"{ano_inicial}{mes_inicial:02}"
lista_datas.append(data_atual)
if ano_inicial == ano_final and mes_inicial == mes_final:
break
elif mes_inicial == 12:
ano_inicial += 1
mes_inicial = 1
else:
mes_inicial += 1
return lista_datas
def download_csv_by_dates(data_inicial:str, data_final:str = None, output_file:str = None) -> pd.DataFrame:
"""Função para baixar os dados da base de dados pelas datas selecionadas.
Como todos os dados da base de dados em csv podem ser baixados a partir dos links:
"https://dados.anvisa.gov.br/dados/SNGPC/Manipulados/EDA_Manipulados_{data}.csv", onde a data
se encontra no formato "AAAAmm" (ex: "201401", ou seja, Janeiro de 2014), essa função baixa esses
dados através do pandas e os retorna como um dataframe, podendo escolher entre apenas uma data ou todos os
dados entre duas datas, é possível também transformá-los em arquivos de saída definindo o
output_file, com o caminho e nome do arquivo desejado, o arquivo deve conter ".csv" no final,
a base de dados contém registros desde Janeiro de 2014 até Novembro de 2021, por isso,
é feita uma validação de datas através da função validacao_datas, e nem sempre o servidor possui resposta.
Parameters
----------
data_inicial : str
A data do primeiro registro buscado.
data_final : str, optional
A data do último registro buscado, by default None
output_file : str, optional
O caminho e nome do arquivo de saída, by default None
Returns
-------
pd.DataFrame
O dataframe dos registros da base de dados da data ou datas inseridas.
Raises
------
ValueError
O arquivo de saída não é uma string ou não acaba em .csv
Test
----------
>>> download_csv_by_dates(2021)
Tipo das datas inserido está incorreto, tente inserir a data como uma string, ex: '2015/05'
>>> download_csv_by_dates("2021/01", "2028/12")
Problemas com a segunda data inserida: 2028/12
Formato da data está incorreto ou ela não está entre Janeiro de 2014 e Novembro de 2021, tente inserir como ANO/mês, ex: '2015/05'.
>>> download_csv_by_dates("2021/01", output_file=3)
O arquivo de saída deve ser uma string e terminar em .csv, ex: 'caminho/meu_arquivo.csv'
>>> download_csv_by_dates("2021/01", output_file="caminho/test.txt")
O arquivo de saída deve ser uma string e terminar em .csv, ex: 'caminho/meu_arquivo.csv'
"""
try:
# Valida a data antes de prosseguir com o código.
if data_final != None:
if validacao_datas(data_inicial, data_final) != True:
raise BaseException
else:
if validacao_datas(data_inicial, data_inicial) != True:
raise BaseException
# Valida o arquivo de saída.
if output_file != None:
if type(output_file) != str:
raise ValueError
elif output_file[-4:] != ".csv":
raise ValueError
dataset = pd.read_csv(f"https://dados.anvisa.gov.br/dados/SNGPC/Manipulados/EDA_Manipulados_{data_inicial}.csv", delimiter=";", encoding="unicode_escape", low_memory=False)
if data_final != None:
dates = get_dates_between_dates(data_inicial, data_final)
# Concatena os arquivos de diferentes datas se necessário.
for index in range(1, len(dates)):
new_year_data = pd.read_csv(f"https://dados.anvisa.gov.br/dados/SNGPC/Manipulados/EDA_Manipulados_{dates[index]}.csv", delimiter=";", low_memory=False)
dataset = pd.concat([dataset, new_year_data])
dataset.to_csv(output_file, sep=";", index=False)
return dataset
except ValueError:
print("O arquivo de saída deve ser uma string e terminar em .csv, ex: 'caminho/meu_arquivo.csv'")
except Exception as err:
print("Houve um erro:", err, end=". ")
except BaseException:
return
def download_data_sep_by_months(data_incial:str, data_final:str, caminho:str) -> None:
"""Baixa arquivos da base de dados separadamente por meses
Devido as limitações do github para arquivos de tamanhos grandes, essa função baixa os arquivos
separadamente por mês e ano, os salvando da forma "Manipulados_AAAA_mm.csv", ex: Manipulados_2014_01.csv.
Os dados de medicamentos manipulados de Janeiro de 2014. as validações de datas são feitas seguindo a função
"validacao_datas". A função está também limitada ao pleno funcionamento do site
https://dados.gov.br/dados/conjuntos-dados/venda-de-medicamentos-controlados-e-antimicrobianos---medicamentos-manipulados
Parameters
----------
data_incial : str
A data do primeiro registro a ser baixado.
data_final : str
A data do último registro a ser baixado.
caminho : str
O caminho em que os arquivos serão baixados.
Test
----------
>>> download_data_sep_by_months("2014/01", "201401", 3)
Caminho deve ser uma string, tente iserir outro caminho.
>>> download_data_sep_by_months("2013/01", "2015/01", 3)
Problemas com a primeira data inserida: 2013/01
Formato da data está incorreto ou ela não está entre Janeiro de 2014 e Novembro de 2021, tente inserir como ANO/mês, ex: '2015/05'.
"""
# Pega as datas selecionadas e faz a validação.
datas_selecionadas = get_dates_between_dates(data_incial, data_final)
# Confirma se o caminho é do tipo válido e se as datas foram válidas.
try:
if datas_selecionadas == []:
raise ValueError
if type(caminho) != str:
raise TypeError
except TypeError:
print("Caminho deve ser uma string, tente iserir outro caminho.")
except ValueError:
return
else:
for cada_data in datas_selecionadas:
nome_arquivo = f"Manipulados_{cada_data[:4]}_{cada_data[-2:]}.csv"
try:
# Tenta baixar os arquivos e se eles não forem baixados levanda a exceção.
dados = download_csv_by_dates(cada_data, output_file=os.path.join(caminho, nome_arquivo))
if type(dados) == type(None):
raise Exception
print(nome_arquivo, "Adicionado com Sucesso!")
except Exception as err:
print(f"Falha ao baixar {nome_arquivo}. {err}")
if __name__ == "__main__":
# Baixando os dados para que eles fiquem salvos para futuras manipulações
"""
esse_caminho = os.path.dirname(os.path.abspath(__file__))
caminho_completo = os.path.join(esse_caminho, "..", "dados")
print(download_data_sep_by_months("2014/01", "2021/11", caminho_completo))
"""
doctest.testmod(verbose=True)