omnisus 0.2.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.
Files changed (282) hide show
  1. omnisus/__init__.py +643 -0
  2. omnisus/_loop.py +24 -0
  3. omnisus/_version.py +1 -0
  4. omnisus/cli/__init__.py +0 -0
  5. omnisus/cli/main.py +442 -0
  6. omnisus/data/__init__.py +0 -0
  7. omnisus/data/auxiliares-bootstrap.zip +0 -0
  8. omnisus/data/dicionarios/__init__.py +0 -0
  9. omnisus/data/dicionarios/aux_cid10.yaml +25 -0
  10. omnisus/data/dicionarios/aux_municipios.yaml +57 -0
  11. omnisus/data/dicionarios/aux_ocupacoes.yaml +20 -0
  12. omnisus/data/dicionarios/aux_paises.yaml +17 -0
  13. omnisus/data/dicionarios/aux_sigtap_procedimentos.yaml +52 -0
  14. omnisus/data/dicionarios/aux_uf.yaml +22 -0
  15. omnisus/data/dicionarios/cnes_dados_complementares.yaml +532 -0
  16. omnisus/data/dicionarios/cnes_equipamentos.yaml +204 -0
  17. omnisus/data/dicionarios/cnes_equipes.yaml +616 -0
  18. omnisus/data/dicionarios/cnes_estabelecimentos.yaml +191 -0
  19. omnisus/data/dicionarios/cnes_estabelecimentos_ensino.yaml +247 -0
  20. omnisus/data/dicionarios/cnes_estabelecimentos_filantropicos.yaml +249 -0
  21. omnisus/data/dicionarios/cnes_gestao_metas.yaml +232 -0
  22. omnisus/data/dicionarios/cnes_habilitacoes.yaml +234 -0
  23. omnisus/data/dicionarios/cnes_incentivos.yaml +232 -0
  24. omnisus/data/dicionarios/cnes_leitos.yaml +294 -0
  25. omnisus/data/dicionarios/cnes_regras_contratuais.yaml +196 -0
  26. omnisus/data/dicionarios/cnes_servicos_especializados.yaml +220 -0
  27. omnisus/data/dicionarios/ibge_populacao.yaml +32 -0
  28. omnisus/data/dicionarios/sia_apac_acompanhamento_bariatrica.yaml +489 -0
  29. omnisus/data/dicionarios/sia_apac_acompanhamento_multiprofissional.yaml +168 -0
  30. omnisus/data/dicionarios/sia_apac_cirurgia_bariatrica.yaml +506 -0
  31. omnisus/data/dicionarios/sia_apac_fistula_arteriovenosa.yaml +504 -0
  32. omnisus/data/dicionarios/sia_apac_laudos_diversos.yaml +413 -0
  33. omnisus/data/dicionarios/sia_apac_medicamentos.yaml +460 -0
  34. omnisus/data/dicionarios/sia_apac_nefrologia.yaml +610 -0
  35. omnisus/data/dicionarios/sia_apac_quimioterapia.yaml +499 -0
  36. omnisus/data/dicionarios/sia_apac_radioterapia.yaml +564 -0
  37. omnisus/data/dicionarios/sia_apac_tratamento_dialitico.yaml +471 -0
  38. omnisus/data/dicionarios/sia_atencao_domiciliar.yaml +407 -0
  39. omnisus/data/dicionarios/sia_bpa_individualizado.yaml +301 -0
  40. omnisus/data/dicionarios/sia_producao_ambulatorial.yaml +550 -0
  41. omnisus/data/dicionarios/sia_producao_ambulatorial_1994_2007.yaml +72 -0
  42. omnisus/data/dicionarios/sia_psicossocial.yaml +382 -0
  43. omnisus/data/dicionarios/sih_aih_reduzida.yaml +2544 -0
  44. omnisus/data/dicionarios/sih_aih_reduzida_1992_2007.yaml +462 -0
  45. omnisus/data/dicionarios/sih_aih_rejeitada.yaml +1671 -0
  46. omnisus/data/dicionarios/sih_aih_rejeitada_erro.yaml +659 -0
  47. omnisus/data/dicionarios/sih_servicos_profissionais.yaml +109 -0
  48. omnisus/data/dicionarios/sim_obitos.yaml +1956 -0
  49. omnisus/data/dicionarios/sim_obitos_cid9.yaml +236 -0
  50. omnisus/data/dicionarios/sim_obitos_externos.yaml +1754 -0
  51. omnisus/data/dicionarios/sim_obitos_fetais.yaml +603 -0
  52. omnisus/data/dicionarios/sim_obitos_infantis.yaml +1754 -0
  53. omnisus/data/dicionarios/sim_obitos_maternos.yaml +1754 -0
  54. omnisus/data/dicionarios/sinan_chagas.yaml +549 -0
  55. omnisus/data/dicionarios/sinan_hanseniase.yaml +495 -0
  56. omnisus/data/dicionarios/sinan_tuberculose.yaml +1173 -0
  57. omnisus/data/dicionarios/sinasc_1994_1995.yaml +76 -0
  58. omnisus/data/dicionarios/sinasc_nascidos_vivos.yaml +1111 -0
  59. omnisus/data/dicionarios/sources/cnv/cnes/CNV/Ativ_Ens.CNV +7 -0
  60. omnisus/data/dicionarios/sources/cnv/cnes/CNV/ESTABENS.CNV +2 -0
  61. omnisus/data/dicionarios/sources/cnv/cnes/CNV/ESTABFIL.CNV +2 -0
  62. omnisus/data/dicionarios/sources/cnv/cnes/CNV/Esp_leit.CNV +71 -0
  63. omnisus/data/dicionarios/sources/cnv/cnes/CNV/Flux_Cli.CNV +6 -0
  64. omnisus/data/dicionarios/sources/cnv/cnes/CNV/IND_SUS.CNV +3 -0
  65. omnisus/data/dicionarios/sources/cnv/cnes/CNV/NIVELDEP.CNV +3 -0
  66. omnisus/data/dicionarios/sources/cnv/cnes/CNV/Srv_Caract.CNV +7 -0
  67. omnisus/data/dicionarios/sources/cnv/cnes/CNV/TPGESTAO.CNV +6 -0
  68. omnisus/data/dicionarios/sources/cnv/cnes/CNV/TP_DESAT.CNV +5 -0
  69. omnisus/data/dicionarios/sources/cnv/cnes/CNV/TP_ESTAB.CNV +46 -0
  70. omnisus/data/dicionarios/sources/cnv/cnes/CNV/TurnosAt.CNV +9 -0
  71. omnisus/data/dicionarios/sources/cnv/cnes/CNV/Vinc_SUS.cnv +3 -0
  72. omnisus/data/dicionarios/sources/cnv/cnes/CNV/motdesat.cnv +12 -0
  73. omnisus/data/dicionarios/sources/cnv/cnes/CNV/tip1leit.cnv +8 -0
  74. omnisus/data/dicionarios/sources/cnv/cnes/CNV/tipoeqpC.cnv +65 -0
  75. omnisus/data/dicionarios/sources/cnv/cnes/CNV/tiposegm.cnv +4 -0
  76. omnisus/data/dicionarios/sources/cnv/cnes/DadosComplementares.def +443 -0
  77. omnisus/data/dicionarios/sources/cnv/cnes/Equipamento.def +238 -0
  78. omnisus/data/dicionarios/sources/cnv/cnes/Equipes.def +521 -0
  79. omnisus/data/dicionarios/sources/cnv/cnes/Estabel_Ensino.def +196 -0
  80. omnisus/data/dicionarios/sources/cnv/cnes/Estabel_Filantropico.def +192 -0
  81. omnisus/data/dicionarios/sources/cnv/cnes/Estabelecimento.def +487 -0
  82. omnisus/data/dicionarios/sources/cnv/cnes/Gestao_de_Metas.def +193 -0
  83. omnisus/data/dicionarios/sources/cnv/cnes/Habilitacao.def +201 -0
  84. omnisus/data/dicionarios/sources/cnv/cnes/Incentivos.def +202 -0
  85. omnisus/data/dicionarios/sources/cnv/cnes/Leitos_Especialidade.def +195 -0
  86. omnisus/data/dicionarios/sources/cnv/cnes/Regras_Contratuais.def +195 -0
  87. omnisus/data/dicionarios/sources/cnv/cnes/Servico_Especializado_200803_.def +200 -0
  88. omnisus/data/dicionarios/sources/cnv/sia/APAC_Cirurgia_Bariatica.DEF +455 -0
  89. omnisus/data/dicionarios/sources/cnv/sia/APAC_Confeccao_de_Fistula.DEF +365 -0
  90. omnisus/data/dicionarios/sources/cnv/sia/APAC_Laudos_Diversos.DEF +438 -0
  91. omnisus/data/dicionarios/sources/cnv/sia/APAC_Medicamentos.DEF +443 -0
  92. omnisus/data/dicionarios/sources/cnv/sia/APAC_Nefrologia.DEF +455 -0
  93. omnisus/data/dicionarios/sources/cnv/sia/APAC_Pos_Cirurgia_Bariatica.def +366 -0
  94. omnisus/data/dicionarios/sources/cnv/sia/APAC_Quimioterapia.DEF +484 -0
  95. omnisus/data/dicionarios/sources/cnv/sia/APAC_Radioterapia.DEF +478 -0
  96. omnisus/data/dicionarios/sources/cnv/sia/APAC_Tratamento_Dialitico.DEF +317 -0
  97. omnisus/data/dicionarios/sources/cnv/sia/Atencao_Domiciliar.def +408 -0
  98. omnisus/data/dicionarios/sources/cnv/sia/CNV/ACES_VASC.CNV +3 -0
  99. omnisus/data/dicionarios/sources/cnv/sia/CNV/ALTA.CNV +3 -0
  100. omnisus/data/dicionarios/sources/cnv/sia/CNV/CARAT_AT.CNV +12 -0
  101. omnisus/data/dicionarios/sources/cnv/sia/CNV/CNCDO.CNV +4 -0
  102. omnisus/data/dicionarios/sources/cnv/sia/CNV/COBERTURA.CNV +3 -0
  103. omnisus/data/dicionarios/sources/cnv/sia/CNV/COMPLEX.CNV +5 -0
  104. omnisus/data/dicionarios/sources/cnv/sia/CNV/CONT_TRAT.CNV +3 -0
  105. omnisus/data/dicionarios/sources/cnv/sia/CNV/ENCERRAM.CNV +3 -0
  106. omnisus/data/dicionarios/sources/cnv/sia/CNV/ESTADIO.CNV +7 -0
  107. omnisus/data/dicionarios/sources/cnv/sia/CNV/FINALID.CNV +8 -0
  108. omnisus/data/dicionarios/sources/cnv/sia/CNV/FL_IDADE.CNV +6 -0
  109. omnisus/data/dicionarios/sources/cnv/sia/CNV/GESTANTE.CNV +3 -0
  110. omnisus/data/dicionarios/sources/cnv/sia/CNV/HBSAG.CNV +3 -0
  111. omnisus/data/dicionarios/sources/cnv/sia/CNV/INDICA.cnv +4 -0
  112. omnisus/data/dicionarios/sources/cnv/sia/CNV/Invasao.CNV +4 -0
  113. omnisus/data/dicionarios/sources/cnv/sia/CNV/LINF_INV.CNV +4 -0
  114. omnisus/data/dicionarios/sources/cnv/sia/CNV/LOC_REALIZ.CNV +3 -0
  115. omnisus/data/dicionarios/sources/cnv/sia/CNV/MANT_IND.CNV +3 -0
  116. omnisus/data/dicionarios/sources/cnv/sia/CNV/MOTSAIPE.CNV +23 -0
  117. omnisus/data/dicionarios/sources/cnv/sia/CNV/OBITO.CNV +3 -0
  118. omnisus/data/dicionarios/sources/cnv/sia/CNV/PERMANEN.CNV +3 -0
  119. omnisus/data/dicionarios/sources/cnv/sia/CNV/RACA_COR.CNV +11 -0
  120. omnisus/data/dicionarios/sources/cnv/sia/CNV/REGRA_C.cnv +20 -0
  121. omnisus/data/dicionarios/sources/cnv/sia/CNV/SEXO.CNV +4 -0
  122. omnisus/data/dicionarios/sources/cnv/sia/CNV/TP_APAC.CNV +4 -0
  123. omnisus/data/dicionarios/sources/cnv/sia/CNV/TP_ATEND.CNV +7 -0
  124. omnisus/data/dicionarios/sources/cnv/sia/CNV/TP_ESTAB.CNV +43 -0
  125. omnisus/data/dicionarios/sources/cnv/sia/CNV/TP_GESTAO.CNV +8 -0
  126. omnisus/data/dicionarios/sources/cnv/sia/CNV/TRANSFER.CNV +3 -0
  127. omnisus/data/dicionarios/sources/cnv/sia/CNV/TRANSPL.CNV +3 -0
  128. omnisus/data/dicionarios/sources/cnv/sia/CNV/destino.cnv +9 -0
  129. omnisus/data/dicionarios/sources/cnv/sia/CNV/origem.cnv +7 -0
  130. omnisus/data/dicionarios/sources/cnv/sia/Producao_Ambulatorial.DEF +522 -0
  131. omnisus/data/dicionarios/sources/cnv/sia/RAAS_Psicossocial.def +337 -0
  132. omnisus/data/dicionarios/sources/cnv/sih/CNV/CARATEND.CNV +7 -0
  133. omnisus/data/dicionarios/sources/cnv/sih/CNV/COMPLEX2.CNV +5 -0
  134. omnisus/data/dicionarios/sources/cnv/sih/CNV/CONTRAC.CNV +14 -0
  135. omnisus/data/dicionarios/sources/cnv/sih/CNV/FINANC.CNV +8 -0
  136. omnisus/data/dicionarios/sources/cnv/sih/CNV/GESTAO.CNV +5 -0
  137. omnisus/data/dicionarios/sources/cnv/sih/CNV/IDENT.CNV +4 -0
  138. omnisus/data/dicionarios/sources/cnv/sih/CNV/INSTRU.CNV +6 -0
  139. omnisus/data/dicionarios/sources/cnv/sih/CNV/LEITOS.CNV +42 -0
  140. omnisus/data/dicionarios/sources/cnv/sih/CNV/MARCAUCI.cnv +5 -0
  141. omnisus/data/dicionarios/sources/cnv/sih/CNV/MARCAUTI.CNV +18 -0
  142. omnisus/data/dicionarios/sources/cnv/sih/CNV/MORTES.CNV +3 -0
  143. omnisus/data/dicionarios/sources/cnv/sih/CNV/NACION3D.CNV +333 -0
  144. omnisus/data/dicionarios/sources/cnv/sih/CNV/NATUREZA.CNV +19 -0
  145. omnisus/data/dicionarios/sources/cnv/sih/CNV/RACACOR.CNV +7 -0
  146. omnisus/data/dicionarios/sources/cnv/sih/CNV/REGCT.cnv +21 -0
  147. omnisus/data/dicionarios/sources/cnv/sih/CNV/SAIDAPERM.CNV +29 -0
  148. omnisus/data/dicionarios/sources/cnv/sih/CNV/SEXO.CNV +4 -0
  149. omnisus/data/dicionarios/sources/cnv/sih/CNV/SIMNAO.CNV +4 -0
  150. omnisus/data/dicionarios/sources/cnv/sih/CNV/TP_DIAGSEC.CNV +3 -0
  151. omnisus/data/dicionarios/sources/cnv/sih/CNV/TP_VAL.CNV +7 -0
  152. omnisus/data/dicionarios/sources/cnv/sih/CNV/VINCPREV.CNV +8 -0
  153. omnisus/data/dicionarios/sources/cnv/sih/CNV/motbloqueio.cnv +45 -0
  154. omnisus/data/dicionarios/sources/cnv/sih/CNV/sitbloqueio.cnv +5 -0
  155. omnisus/data/dicionarios/sources/cnv/sih/CNV/sitrejeicao.cnv +3 -0
  156. omnisus/data/dicionarios/sources/cnv/sih/DBF/MOTERRO.dbf +0 -0
  157. omnisus/data/dicionarios/sources/cnv/sih/Motivo_de_Erro.DEF +74 -0
  158. omnisus/data/dicionarios/sources/cnv/sih/RD2008.DEF +532 -0
  159. omnisus/data/dicionarios/sources/cnv/sih/RJ2008.DEF +489 -0
  160. omnisus/data/dicionarios/sources/cnv/sih/SP2008.DEF +310 -0
  161. omnisus/data/dicionarios/sources/cnv/sih_199201_199712/MORTES.CNV +3 -0
  162. omnisus/data/dicionarios/sources/cnv/sih_199201_199712/RD.DEF +547 -0
  163. omnisus/data/dicionarios/sources/cnv/sih_199201_199712/VINCPREV.CNV +8 -0
  164. omnisus/data/dicionarios/sources/cnv/sih_199801_200307/RD.DEF +550 -0
  165. omnisus/data/dicionarios/sources/cnv/sih_200308_200712/RD.DEF +549 -0
  166. omnisus/data/dicionarios/sources/cnv/sim/ACIDTRAB.CNV +5 -0
  167. omnisus/data/dicionarios/sources/cnv/sim/ASSMEDIC.CNV +5 -0
  168. omnisus/data/dicionarios/sources/cnv/sim/ATESTANT.CNV +8 -0
  169. omnisus/data/dicionarios/sources/cnv/sim/CBO2002.CNV +2430 -0
  170. omnisus/data/dicionarios/sources/cnv/sim/CODIFICA1.cnv +4 -0
  171. omnisus/data/dicionarios/sources/cnv/sim/DONOVA.CNV +4 -0
  172. omnisus/data/dicionarios/sources/cnv/sim/ESCAGR1.CNV +15 -0
  173. omnisus/data/dicionarios/sources/cnv/sim/ESTCIV.CNV +8 -0
  174. omnisus/data/dicionarios/sources/cnv/sim/FONTEINV.CNV +12 -0
  175. omnisus/data/dicionarios/sources/cnv/sim/FONTINFO.CNV +7 -0
  176. omnisus/data/dicionarios/sources/cnv/sim/GRAVIDEZ.CNV +6 -0
  177. omnisus/data/dicionarios/sources/cnv/sim/INSTRUC.CNV +8 -0
  178. omnisus/data/dicionarios/sources/cnv/sim/INVESTIG.CNV +5 -0
  179. omnisus/data/dicionarios/sources/cnv/sim/LOCOCOR.CNV +8 -0
  180. omnisus/data/dicionarios/sources/cnv/sim/OBITOFE1.CNV +5 -0
  181. omnisus/data/dicionarios/sources/cnv/sim/OBITOFE2.CNV +5 -0
  182. omnisus/data/dicionarios/sources/cnv/sim/OMUFER.CNV +9 -0
  183. omnisus/data/dicionarios/sources/cnv/sim/Obito_1979_95_CID-09.DEF +217 -0
  184. omnisus/data/dicionarios/sources/cnv/sim/Obito_1996_CID10.def +1096 -0
  185. omnisus/data/dicionarios/sources/cnv/sim/PARTO.CNV +5 -0
  186. omnisus/data/dicionarios/sources/cnv/sim/RACACOR.CNV +8 -0
  187. omnisus/data/dicionarios/sources/cnv/sim/SEMANAS.CNV +9 -0
  188. omnisus/data/dicionarios/sources/cnv/sim/SERIEFAL.CNV +12 -0
  189. omnisus/data/dicionarios/sources/cnv/sim/TIPOACID.CNV +8 -0
  190. omnisus/data/dicionarios/sources/cnv/sim/TIPOBITO.CNV +4 -0
  191. omnisus/data/dicionarios/sources/cnv/sim/TIPOVIOL.CNV +7 -0
  192. omnisus/data/dicionarios/sources/cnv/sim/VERSCB.CNV +23 -0
  193. omnisus/data/dicionarios/sources/cnv/sim/obitoPUE.cnv +7 -0
  194. omnisus/data/dicionarios/sources/cnv/sim/obitogra.cnv +5 -0
  195. omnisus/data/dicionarios/sources/cnv/sim/oparto.cnv +7 -0
  196. omnisus/data/dicionarios/sources/cnv/sinan/AvaliaN.cnv +8 -0
  197. omnisus/data/dicionarios/sources/cnv/sinan/ChagasNET.def +643 -0
  198. omnisus/data/dicionarios/sources/cnv/sinan/Escolarnet.cnv +13 -0
  199. omnisus/data/dicionarios/sources/cnv/sinan/HansNET.def +492 -0
  200. omnisus/data/dicionarios/sources/cnv/sinan/SAIDAhans.cnv +12 -0
  201. omnisus/data/dicionarios/sources/cnv/sinan/TuberculNET5_0.def +514 -0
  202. omnisus/data/dicionarios/sources/cnv/sinan/reacional.cnv +8 -0
  203. omnisus/data/dicionarios/sources/cnv/sinasc/ANOMAL.CNV +6 -0
  204. omnisus/data/dicionarios/sources/cnv/sinasc/CONSULT.CNV +7 -0
  205. omnisus/data/dicionarios/sources/cnv/sinasc/ESC2010.CNV +9 -0
  206. omnisus/data/dicionarios/sources/cnv/sinasc/ESCAGR1.CNV +15 -0
  207. omnisus/data/dicionarios/sources/cnv/sinasc/ESTCIV.CNV +8 -0
  208. omnisus/data/dicionarios/sources/cnv/sinasc/GRAVIDEZ.CNV +6 -0
  209. omnisus/data/dicionarios/sources/cnv/sinasc/INSTRUC.CNV +8 -0
  210. omnisus/data/dicionarios/sources/cnv/sinasc/KOTELCHUCK.CNV +7 -0
  211. omnisus/data/dicionarios/sources/cnv/sinasc/LOCOCOR.CNV +7 -0
  212. omnisus/data/dicionarios/sources/cnv/sinasc/NASCIDO.def +876 -0
  213. omnisus/data/dicionarios/sources/cnv/sinasc/ORIGEM.CNV +6 -0
  214. omnisus/data/dicionarios/sources/cnv/sinasc/PARAPRE.CNV +7 -0
  215. omnisus/data/dicionarios/sources/cnv/sinasc/PARTO.CNV +5 -0
  216. omnisus/data/dicionarios/sources/cnv/sinasc/RACA.CNV +8 -0
  217. omnisus/data/dicionarios/sources/cnv/sinasc/ROBSON.CNV +12 -0
  218. omnisus/data/dicionarios/sources/cnv/sinasc/SEMANAS.CNV +9 -0
  219. omnisus/data/dicionarios/sources/cnv/sinasc/SEXO.CNV +4 -0
  220. omnisus/data/dicionarios/sources/cnv/sinasc/STPARTO.CNV +7 -0
  221. omnisus/data/dicionarios/sources/cnv/sinasc/TPASSIST.CNV +7 -0
  222. omnisus/data/dicionarios/sources/cnv/sinasc/TPFUNC.CNV +7 -0
  223. omnisus/data/dicionarios/sources/cnv/sinasc/TPMETODO.CNV +7 -0
  224. omnisus/data/dicionarios/sources/cnv/sinasc/idanomal.cnv +6 -0
  225. omnisus/data/dicionarios/sources/cnv/vinculos.json +1743 -0
  226. omnisus/data/dicionarios/sources/registry.json +554 -0
  227. omnisus/data/dicionarios/versoes.json +152 -0
  228. omnisus/lake/__init__.py +14 -0
  229. omnisus/lake/_transactions.py +15 -0
  230. omnisus/lake/catalog.py +107 -0
  231. omnisus/lake/connection.py +137 -0
  232. omnisus/lake/locking.py +55 -0
  233. omnisus/lake/maintenance.py +37 -0
  234. omnisus/lake/operations.py +633 -0
  235. omnisus/lake/publication.py +449 -0
  236. omnisus/lake/schema.py +28 -0
  237. omnisus/lake/session.py +213 -0
  238. omnisus/lake/sql.py +17 -0
  239. omnisus/metadata.py +218 -0
  240. omnisus/products.py +91 -0
  241. omnisus/py.typed +0 -0
  242. omnisus/research.py +406 -0
  243. omnisus/sources/__init__.py +0 -0
  244. omnisus/sources/_base.py +179 -0
  245. omnisus/sources/cnes/__init__.py +0 -0
  246. omnisus/sources/cnes/importers/__init__.py +0 -0
  247. omnisus/sources/cnes/importers/master.py +212 -0
  248. omnisus/sources/datasus_ftp/__init__.py +0 -0
  249. omnisus/sources/datasus_ftp/_cache.py +144 -0
  250. omnisus/sources/datasus_ftp/_ftp.py +41 -0
  251. omnisus/sources/datasus_ftp/_runner.py +604 -0
  252. omnisus/sources/datasus_ftp/datasets.py +242 -0
  253. omnisus/sources/datasus_ftp/dbc.py +199 -0
  254. omnisus/sources/datasus_ftp/dbf_batches.py +172 -0
  255. omnisus/sources/datasus_ftp/dbf_contract.py +149 -0
  256. omnisus/sources/datasus_ftp/fetch.py +214 -0
  257. omnisus/sources/datasus_ftp/filenames.py +96 -0
  258. omnisus/sources/datasus_ftp/identity.py +75 -0
  259. omnisus/sources/datasus_ftp/inventory.py +438 -0
  260. omnisus/sources/datasus_ftp/native.py +40 -0
  261. omnisus/sources/datasus_ftp/parse.py +87 -0
  262. omnisus/sources/datasus_ftp/staging.py +242 -0
  263. omnisus/sources/ibge/__init__.py +0 -0
  264. omnisus/sources/ibge/fetch.py +140 -0
  265. omnisus/sources/ibge/importers/__init__.py +0 -0
  266. omnisus/sources/ibge/importers/pop.py +100 -0
  267. omnisus/sources/ibge/parse.py +88 -0
  268. omnisus/sources/ibge/products.py +60 -0
  269. omnisus/sources/medicamentos/__init__.py +5 -0
  270. omnisus/sources/medicamentos/horus.py +137 -0
  271. omnisus/sources/sigtap/__init__.py +172 -0
  272. omnisus/transforms/__init__.py +0 -0
  273. omnisus/transforms/age.py +226 -0
  274. omnisus/transforms/analytics.py +252 -0
  275. omnisus/transforms/cnv.py +235 -0
  276. omnisus/transforms/columns.py +113 -0
  277. omnisus/transforms/dictionaries.py +253 -0
  278. omnisus-0.2.0.dist-info/METADATA +169 -0
  279. omnisus-0.2.0.dist-info/RECORD +282 -0
  280. omnisus-0.2.0.dist-info/WHEEL +4 -0
  281. omnisus-0.2.0.dist-info/entry_points.txt +2 -0
  282. omnisus-0.2.0.dist-info/licenses/LICENSE +21 -0
omnisus/__init__.py ADDED
@@ -0,0 +1,643 @@
1
+ """omnisus — Brazilian public health database ingestion lib."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import warnings
6
+ from collections.abc import Callable, Iterable, Mapping, Sequence
7
+ from urllib.parse import urlparse
8
+
9
+ import polars as pl
10
+
11
+ from omnisus._loop import run_sync
12
+ from omnisus._version import __version__
13
+ from omnisus.lake import CatalogAttachError, Lake, LakeReader
14
+ from omnisus.lake.catalog import set_lake_dir
15
+ from omnisus.lake.publication import DeletionResult, ImportPolicy, scope_filter
16
+ from omnisus.lake.sql import qualified, quote_identifier
17
+ from omnisus.metadata import describe_dataset
18
+ from omnisus.products import Product, datasets, products
19
+ from omnisus.research import (
20
+ Citation,
21
+ citation_from_publications,
22
+ cite,
23
+ import_research,
24
+ latest_snapshot_id,
25
+ municipality_join_key,
26
+ municipality_join_key_sql,
27
+ reference_join_sql,
28
+ )
29
+ from omnisus.sources._base import (
30
+ ALL_UFS,
31
+ ImportAbortedError,
32
+ ImportReport,
33
+ ImportResult,
34
+ ScopeKey,
35
+ ScopeOutcome,
36
+ )
37
+ from omnisus.sources.datasus_ftp._runner import (
38
+ DEFAULT_BATCH_SIZE,
39
+ DEFAULT_CONCURRENCY,
40
+ )
41
+ from omnisus.sources.datasus_ftp._runner import run_scopes as _run_scopes_ftp
42
+ from omnisus.sources.datasus_ftp.datasets import Dataset, resolve
43
+ from omnisus.sources.datasus_ftp.fetch import (
44
+ DEFAULT_MAX_INFLIGHT_BYTES,
45
+ DEFAULT_MAX_PAYLOAD_BYTES,
46
+ )
47
+ from omnisus.sources.datasus_ftp.inventory import (
48
+ FtpEntry,
49
+ FtpPathNotFound,
50
+ FtpUnavailable,
51
+ available,
52
+ available_releases,
53
+ list_sources,
54
+ )
55
+ from omnisus.sources.datasus_ftp.inventory import crawl as _crawl
56
+ from omnisus.transforms.analytics import (
57
+ AnalyticalProjection,
58
+ DerivedColumn,
59
+ SourceContext,
60
+ UnavailableField,
61
+ analytical_projection,
62
+ )
63
+ from omnisus.transforms.columns import check_columns
64
+ from omnisus.transforms.dictionaries import display_row, label
65
+
66
+
67
+ def scopes_for(
68
+ dataset: str | Dataset,
69
+ *,
70
+ years: Iterable[int],
71
+ ufs: Sequence[str] | None = None,
72
+ months: Iterable[int] | None = None,
73
+ ) -> list[ScopeKey]:
74
+ """Every scope (UF x year [x month]) of a dataset, without asking the server.
75
+
76
+ Planning is composition: pass the result, or any other list of scopes, to
77
+ :func:`import_dataset`. Scopes DATASUS does not publish are later reported as
78
+ ``skipped``; :func:`available` plans only what the server lists.
79
+
80
+ Args:
81
+ dataset: Dataset name, e.g. ``"sim_obitos"``, or a :class:`Dataset`; see
82
+ :func:`datasets`.
83
+ years: Years, e.g. ``[2022, 2023]`` or ``range(2020, 2024)``.
84
+ ufs: UF abbreviations, e.g. ``["RR", "AC"]``. ``None`` (default) means all 27
85
+ (:data:`ALL_UFS`). National datasets (SINAN, SIM subsets) accept only ``None``.
86
+ months: Months 1-12 for monthly datasets (SIH, SIA, CNES); ``None`` (default)
87
+ means all twelve. Ignored for yearly UF datasets; national datasets accept
88
+ only ``None``.
89
+
90
+ Returns:
91
+ The scopes, ordered by year, then UF, then month.
92
+
93
+ Raises:
94
+ ValueError: ``dataset`` is unknown, or a national dataset got ``ufs`` or
95
+ ``months``.
96
+
97
+ Examples:
98
+ >>> import omnisus as sus
99
+ >>> sus.scopes_for("sih_aih_reduzida", years=[2024], ufs=["RR"], months=[1, 2])
100
+ [ScopeKey(uf='RR', ano=2024, mes=1), ScopeKey(uf='RR', ano=2024, mes=2)]
101
+ >>> sus.scopes_for("sinan_chagas", years=[2023])
102
+ [ScopeKey(uf=None, ano=2023, mes=None)]
103
+ """
104
+ d = resolve(dataset)
105
+ if d.geography == "national":
106
+ if ufs is not None:
107
+ raise ValueError(
108
+ "national datasets do not accept UF filters; filter records after import"
109
+ )
110
+ if months is not None:
111
+ raise ValueError("national yearly datasets do not accept month filters")
112
+ return [ScopeKey(uf=None, ano=year) for year in years]
113
+ uf_list = tuple(ufs) if ufs is not None else ALL_UFS
114
+ month_list = tuple(months) if months is not None else tuple(range(1, 13))
115
+ scopes: list[ScopeKey] = []
116
+ for year in years:
117
+ for uf in uf_list:
118
+ if d.monthly:
119
+ scopes.extend(ScopeKey(uf=uf, ano=year, mes=m) for m in month_list)
120
+ else:
121
+ scopes.append(ScopeKey(uf=uf, ano=year))
122
+ return scopes
123
+
124
+
125
+ def browse(path: str, *, depth: int = 1, refresh: bool = False) -> list[FtpEntry]:
126
+ """List any DATASUS FTP path, including datasets the package does not curate.
127
+
128
+ The open-world counterpart to :func:`available`: it reaches other SINAN agravos,
129
+ CIHA or PCE. Eager, so a huge tree is listed whole; call
130
+ ``omnisus.sources.datasus_ftp.inventory.crawl`` for a lazy walk.
131
+
132
+ Args:
133
+ path: Absolute FTP path, e.g. ``"/dissemin/publicos/SINAN/DADOS/FINAIS"``.
134
+ depth: How many directory levels to list; ``1`` (default) lists ``path`` only.
135
+ refresh: ``True`` ignores the local listing cache and asks the server again.
136
+
137
+ Returns:
138
+ One :class:`FtpEntry` per file or directory, with size and server time.
139
+
140
+ Raises:
141
+ FtpPathNotFound: the server has no such path.
142
+ FtpUnavailable: the server did not answer after retries.
143
+
144
+ Examples:
145
+ >>> import omnisus as sus
146
+ >>> entries = sus.browse("/dissemin/publicos/SINAN/DADOS/FINAIS") # doctest: +SKIP
147
+ >>> entries[0].name # doctest: +SKIP
148
+ 'ACBIBR06.dbc'
149
+ """
150
+ return list(_crawl(path, depth=depth, refresh=refresh))
151
+
152
+
153
+ _AFTER_IMPORT: dict[str, Callable[[Lake], object]] = {
154
+ "cnes_estabelecimentos": Lake.ensure_aux_cnes_view,
155
+ }
156
+ """Behaviour that follows an import, keyed by dataset. It lives here, not on the
157
+ registry row (ADR 0002), and runs whichever function started the import."""
158
+
159
+
160
+ def import_dataset(
161
+ dataset: str | Dataset,
162
+ *,
163
+ scopes: Sequence[ScopeKey],
164
+ target: str | None = None,
165
+ concurrency: int = DEFAULT_CONCURRENCY,
166
+ batch_size: int = DEFAULT_BATCH_SIZE,
167
+ policy: ImportPolicy = "append",
168
+ run_id: str | None = None,
169
+ max_payload_bytes: int = DEFAULT_MAX_PAYLOAD_BYTES,
170
+ max_inflight_bytes: int = DEFAULT_MAX_INFLIGHT_BYTES,
171
+ ) -> ImportReport:
172
+ """Import the given scopes of a DATASUS FTP dataset into the lake (operator door).
173
+
174
+ The import iterates ``scopes`` and never asks where they came from:
175
+ :func:`scopes_for` plans blindly and :func:`available` asks the server first.
176
+ Every run lists the server once and imports what it lists; callers never choose
177
+ a release or file name. Importing ``cnes_estabelecimentos`` also refreshes the
178
+ ``aux_cnes`` view. Works inside a notebook's running event loop.
179
+
180
+ Args:
181
+ dataset: Dataset name, e.g. ``"sia_bpa_individualizado"``, or a
182
+ :class:`Dataset`; see :func:`datasets`.
183
+ scopes: The scopes to import, e.g. from :func:`scopes_for` or :func:`available`.
184
+ target: DuckLake target such as ``"ducklake:/path/omnisus.ducklake"`` or
185
+ ``"ducklake:postgresql://…?storage=s3://…"``. ``None`` (default) is
186
+ ``data/raw/omnisus.ducklake`` under the working directory, or under
187
+ ``$OMNISUS_DATA_DIR``.
188
+ concurrency: Downloads in flight. DATASUS FTP is a shared public server; raise
189
+ it only with reason.
190
+ batch_size: Scopes committed in one DuckLake transaction, hence one snapshot.
191
+ policy: What to do with a scope already in the lake: ``"append"`` (default)
192
+ adds rows again; ``"skip_same"`` skips it when file and parser are
193
+ unchanged, without downloading when the listing shows the same path,
194
+ size and server time; ``"error_if_exists"`` fails that scope;
195
+ ``"replace"`` rewrites it. :func:`load` and :func:`import_research` default to ``"skip_same"``.
196
+ run_id: Your identifier for this run, chosen before it starts, to reconcile an
197
+ interrupted commit through ``Lake.publications(run_id=...)``. ``None``
198
+ generates one.
199
+ max_payload_bytes: Largest compressed download accepted per file (512 MiB).
200
+ max_inflight_bytes: Compressed bytes held at once before parsing (1 GiB).
201
+ Neither bounds total memory: a decompressed DBF is held whole.
202
+
203
+ Returns:
204
+ One :class:`ScopeOutcome` per scope: ``ok``, ``skipped`` (e.g. the server
205
+ does not list it) or ``failed``, with ``code`` and ``reason``. Inspect
206
+ ``report.failed``; later batches continue after a failed scope.
207
+
208
+ Raises:
209
+ ImportAbortedError: the lake transaction failed mid-run; its ``report`` and
210
+ ``unresolved`` say what was committed. Inspect before retrying.
211
+ ValueError: ``dataset`` is unknown or ``policy`` is not one of the four values.
212
+
213
+ Examples:
214
+ >>> import omnisus as sus
215
+ >>> scopes = sus.available("sim_obitos", years=[2023], ufs=["RR"]) # doctest: +SKIP
216
+ >>> report = sus.import_dataset("sim_obitos", scopes=scopes) # doctest: +SKIP
217
+ >>> [(str(o.scope), o.status) for o in report.outcomes] # doctest: +SKIP
218
+ [('RR_2023', 'ok')]
219
+ """
220
+ d = resolve(dataset)
221
+
222
+ async def run() -> ImportReport:
223
+ with Lake.local(target) as lake:
224
+ report = await _run_scopes_ftp(
225
+ d,
226
+ scopes=scopes,
227
+ lake=lake,
228
+ concurrency=concurrency,
229
+ batch_size=batch_size,
230
+ policy=policy,
231
+ run_id=run_id,
232
+ max_payload_bytes=max_payload_bytes,
233
+ max_inflight_bytes=max_inflight_bytes,
234
+ )
235
+ if after_import := _AFTER_IMPORT.get(d.name):
236
+ after_import(lake)
237
+ return report
238
+
239
+ return run_sync(run)
240
+
241
+
242
+ def _loadable(dataset: str | Dataset) -> Dataset:
243
+ """The FTP dataset ``load`` imports; another product's name points to its function."""
244
+ other = {p.name for p in products() if p.dataset is None}
245
+ if dataset in other:
246
+ raise ValueError(
247
+ f"load reads DATASUS FTP datasets; use sus.import_{dataset} for {dataset}"
248
+ )
249
+ return resolve(dataset)
250
+
251
+
252
+ def load(
253
+ dataset: str | Dataset,
254
+ *,
255
+ years: Iterable[int],
256
+ ufs: Sequence[str] | None = None,
257
+ months: Iterable[int] | None = None,
258
+ target: str | None = None,
259
+ policy: ImportPolicy = "skip_same",
260
+ ) -> pl.DataFrame:
261
+ """Download what DATASUS publishes for these scopes and return the rows.
262
+
263
+ The one-call path for a script or notebook cell. It imports into the lake (a
264
+ second call downloads nothing new) and returns the requested scopes' rows with
265
+ codes as DATASUS published them. When every file comes from a validated source
266
+ (see ``describe_dataset(dataset)["analytics"]["validated_sources"]``) it also adds
267
+ the harmonised categories: ``idade_anos_completos``, ``idade_status``,
268
+ ``sexo_categoria``, ``sexo_status`` and ``<date>_data`` columns. Otherwise it
269
+ leaves them out and warns once. Readable labels come from :func:`label`.
270
+
271
+ Args:
272
+ dataset: DATASUS FTP dataset name, e.g. ``"sim_obitos"``,
273
+ ``"sih_aih_reduzida"``; see :func:`datasets`. IBGE population, SIGTAP and
274
+ CNES names have their own functions (:func:`import_ibge_populacao`,
275
+ :func:`import_sigtap`, :func:`import_cnes_master`).
276
+ years: Years, e.g. ``[2023]`` or ``range(2020, 2024)``.
277
+ ufs: UF abbreviations, e.g. ``["RR"]``. Required for datasets published per
278
+ UF; pass :data:`ALL_UFS` for all of Brazil. National datasets (SINAN,
279
+ SIM subsets) accept only ``None``.
280
+ months: Months 1-12 of monthly datasets (SIH, SIA, CNES), e.g. ``[1, 2]``;
281
+ ``None`` (default) means all twelve.
282
+ target: DuckLake target; ``None`` (default) is ``data/raw/omnisus.ducklake``
283
+ under the working directory, or under ``$OMNISUS_DATA_DIR``.
284
+ policy: ``"skip_same"`` (default) reuses scopes already imported from the
285
+ same file; ``"replace"`` rewrites scopes imported from another file or
286
+ dictionary version; ``"append"`` and ``"error_if_exists"`` as in
287
+ :func:`import_dataset`.
288
+
289
+ Returns:
290
+ A polars DataFrame with one row per record of the requested scopes.
291
+
292
+ Raises:
293
+ ValueError: ``dataset`` is unknown or not an FTP dataset, ``ufs`` is missing
294
+ for a UF dataset, or given for a national one.
295
+ RuntimeError: a scope failed to import; the message names each one.
296
+ LookupError: DATASUS publishes none of the requested scopes.
297
+
298
+ Warns:
299
+ UserWarning: harmonised categories were left out, and why.
300
+
301
+ Examples:
302
+ >>> import omnisus as sus
303
+ >>> dados = sus.load("sim_obitos", years=[2023], ufs=["RR"]) # doctest: +SKIP
304
+ >>> dados.height # doctest: +SKIP
305
+ 3311
306
+ >>> dados = sus.label("sim_obitos", dados, columns=["sexo"]) # doctest: +SKIP
307
+ """
308
+ d = _loadable(dataset)
309
+ if ufs is None and d.geography != "national":
310
+ raise ValueError(
311
+ f"{d.name} is published per UF: pass ufs=['RR', ...], or ufs=sus.ALL_UFS "
312
+ "for all of Brazil"
313
+ )
314
+ scopes = scopes_for(d, years=years, ufs=ufs, months=months)
315
+ report = import_dataset(d, scopes=scopes, target=target, policy=policy)
316
+ if report.failed:
317
+ reasons = "; ".join(f"{o.scope}: {o.reason}" for o in report.failed)
318
+ raise RuntimeError(
319
+ f"{d.name} import failed ({reasons}). A scope already imported from "
320
+ "another file or parser version is rewritten with policy='replace'."
321
+ )
322
+ rows = _read_with_harmonised(d, scopes, target)
323
+ if rows.is_empty():
324
+ raise LookupError(f"{d.name}: DATASUS publishes none of these scopes: {scopes}")
325
+ return rows
326
+
327
+
328
+ def _read_with_harmonised(
329
+ d: Dataset, scopes: Sequence[ScopeKey], target: str | None
330
+ ) -> pl.DataFrame:
331
+ """The scopes' rows plus the harmonised categories their sources allow (ADR 0003).
332
+
333
+ Rows, schema and source identities come from one pinned snapshot, as
334
+ :func:`analytical_projection` requires. A category the rule leaves out is
335
+ named in one warning; a dataset without rules warns nothing.
336
+ """
337
+ with LakeReader(target) as latest:
338
+ if d.name not in latest.tables():
339
+ return pl.DataFrame()
340
+ snapshot = latest_snapshot_id(latest)
341
+ predicates = [scope_filter(scope) for scope in scopes]
342
+ where = " OR ".join(f"({sql})" for sql, _ in predicates)
343
+ args = [value for _, values in predicates for value in values]
344
+ with LakeReader(target, snapshot_id=snapshot) as reader:
345
+ con = reader.connect()
346
+ table = qualified(reader.alias, d.name)
347
+ schema = {
348
+ row[0]: row[1] for row in con.execute(f"DESCRIBE SELECT * FROM {table}").fetchall()
349
+ }
350
+ sources = [
351
+ SourceContext.from_publication(row)
352
+ for row in reader.publications()
353
+ if row["dataset"] == d.name and row["active"] and row["scope"] in scopes
354
+ ]
355
+ projection = analytical_projection(d.name, observed_schema=schema, scopes=sources)
356
+ derived = "".join(
357
+ f", {column.expression} AS {quote_identifier(column.name)}"
358
+ for column in projection.columns
359
+ )
360
+ rows = con.execute(f"SELECT *{derived} FROM {table} WHERE {where}", args).pl()
361
+ left_out = [u for u in projection.unavailable if u.reason != "unsupported_dataset"]
362
+ if left_out:
363
+ warnings.warn(
364
+ f"{d.name}: harmonised categories left out: "
365
+ + ", ".join(f"{u.field} ({u.reason})" for u in left_out)
366
+ + ". They exist only for validated sources (docs/decisions/0003); "
367
+ "labels from sus.label work for any scope.",
368
+ UserWarning,
369
+ stacklevel=3,
370
+ )
371
+ return rows
372
+
373
+
374
+ def import_ibge_populacao(
375
+ *,
376
+ years: Iterable[int],
377
+ census: bool,
378
+ target: str | None = None,
379
+ ) -> list[ImportResult]:
380
+ """Import IBGE population by municipality: a census or the year's latest estimate.
381
+
382
+ Each population edition is its own publication, identified by the returned
383
+ ``publication_id`` in ``ibge_population_manifest``; it never appears in
384
+ ``Lake.publications()``. Reconcile an interrupted run through that manifest.
385
+
386
+ Args:
387
+ years: Years, e.g. ``[2022]``. Censuses exist for 2010 and 2022; an estimate
388
+ is importable only as the aggregate's latest edition, and years without a
389
+ verified territorial universe are refused.
390
+ census: Required, with no default, so nobody gets an estimate thinking it is
391
+ the census. ``True`` imports the census; ``False`` the latest estimate.
392
+ target: DuckLake target; ``None`` (default) is ``data/raw/omnisus.ducklake``
393
+ under the working directory, or under ``$OMNISUS_DATA_DIR``.
394
+
395
+ Returns:
396
+ One :class:`ImportResult` per year, with ``rows`` and ``publication_id``.
397
+
398
+ Raises:
399
+ ValueError: the year has no such edition.
400
+ TypeError: ``census`` was not given.
401
+
402
+ Examples:
403
+ >>> import omnisus as sus
404
+ >>> (censo,) = sus.import_ibge_populacao(years=[2022], census=True) # doctest: +SKIP
405
+ >>> censo.rows # doctest: +SKIP
406
+ 5570
407
+ """
408
+ from omnisus.sources.ibge.importers.pop import import_pop_year
409
+
410
+ edition = "census" if census else "estimate"
411
+
412
+ async def run() -> list[ImportResult]:
413
+ results: list[ImportResult] = []
414
+ with Lake.local(target) as lake:
415
+ for y in years:
416
+ results.append(await import_pop_year(year=y, lake=lake, product=edition))
417
+ return results
418
+
419
+ return run_sync(run)
420
+
421
+
422
+ def import_sigtap(
423
+ *,
424
+ years: Iterable[int],
425
+ months: Iterable[int] | None = None,
426
+ target: str | None = None,
427
+ policy: ImportPolicy = "skip_same",
428
+ ) -> ImportReport:
429
+ """Import SIGTAP procedures, one competência per year x month.
430
+
431
+ Each competência is one national monthly publication from
432
+ ``ftp2.datasus.gov.br/public/sistemas/tup/downloads`` into
433
+ ``aux_sigtap_procedimentos``. Nothing joins at import: join on
434
+ ``co_procedimento`` and the record's competência yourself.
435
+
436
+ Args:
437
+ years: Years, e.g. ``[2024]``.
438
+ months: Months 1-12, e.g. ``[1, 2]``; ``None`` (default) means all twelve.
439
+ target: DuckLake target; ``None`` (default) is ``data/raw/omnisus.ducklake``
440
+ under the working directory, or under ``$OMNISUS_DATA_DIR``.
441
+ policy: ``"skip_same"`` (default) reports an unchanged zip as
442
+ ``skipped``/``unchanged``; ``"append"``, ``"error_if_exists"`` and
443
+ ``"replace"`` as in :func:`import_dataset`.
444
+
445
+ Returns:
446
+ One :class:`ScopeOutcome` per competência; one the server does not list is
447
+ ``skipped``/``not_listed``. :func:`cite` names each zip.
448
+
449
+ Examples:
450
+ >>> import omnisus as sus
451
+ >>> report = sus.import_sigtap(years=[2024], months=[1]) # doctest: +SKIP
452
+ >>> [(str(o.scope), o.status) for o in report.outcomes] # doctest: +SKIP
453
+ [('national_2024_01', 'ok')]
454
+ """
455
+ from omnisus.sources.sigtap import import_sigtap as _impl
456
+
457
+ month_list = tuple(months) if months is not None else tuple(range(1, 13))
458
+ wanted = [(year, month) for year in years for month in month_list]
459
+ with Lake.local(target) as lake:
460
+ return _impl(wanted, lake=lake, policy=policy)
461
+
462
+
463
+ def available_sigtap() -> list[tuple[int, int]]:
464
+ """Every SIGTAP competência the server lists now, oldest first.
465
+
466
+ Returns:
467
+ ``(year, month)`` pairs, e.g. ``[(2008, 1), (2008, 2), ...]``.
468
+
469
+ Raises:
470
+ FtpUnavailable: the server did not answer after retries.
471
+
472
+ Examples:
473
+ >>> import omnisus as sus
474
+ >>> sus.available_sigtap()[:2] # doctest: +SKIP
475
+ [(2008, 1), (2008, 2)]
476
+ """
477
+ from omnisus.sources.sigtap import available_sigtap as _impl
478
+
479
+ return _impl()
480
+
481
+
482
+ def import_cnes_master(
483
+ *,
484
+ codes: Sequence[str] | None = None,
485
+ target: str | None = None,
486
+ concurrency: int = 5,
487
+ only_missing: bool = True,
488
+ progress: Callable[[int, int], None] | None = None,
489
+ ) -> int:
490
+ """Fetch CNES establishment names from the public API into ``cnes_master``.
491
+
492
+ The CNES-ST DBF has no establishment names; this pulls them from
493
+ ``apidadosabertos.saude.gov.br`` and joins them into the ``aux_cnes`` view. It
494
+ is an idempotent upsert, not a publication: no ``run_id``, no ``policy``, no
495
+ manifest row. To finish an interrupted run, run it again.
496
+
497
+ Args:
498
+ codes: 7-digit CNES codes, e.g. ``["2789590"]``. ``None`` (default) takes
499
+ every code in ``cnes_estabelecimentos``.
500
+ target: DuckLake target; ``None`` (default) is ``data/raw/omnisus.ducklake``
501
+ under the working directory, or under ``$OMNISUS_DATA_DIR``.
502
+ concurrency: API requests in flight (default 5).
503
+ only_missing: ``True`` (default) skips codes already in ``cnes_master``, so
504
+ reruns are incremental; ``False`` fetches every code again.
505
+ progress: Optional ``progress(done, total)`` callback after each fetch.
506
+
507
+ Returns:
508
+ The number of records written in this run.
509
+
510
+ Raises:
511
+ ValueError: a code is not seven ASCII digits, or the API returned conflicting
512
+ records for one code.
513
+
514
+ Examples:
515
+ >>> import omnisus as sus
516
+ >>> sus.import_cnes_master(codes=["2789590"]) # doctest: +SKIP
517
+ 1
518
+ """
519
+ from omnisus.sources.cnes.importers.master import (
520
+ import_cnes_master as _impl,
521
+ )
522
+
523
+ return _impl(
524
+ codes=codes,
525
+ target=target,
526
+ concurrency=concurrency,
527
+ only_missing=only_missing,
528
+ progress=progress,
529
+ )
530
+
531
+
532
+ def outdated(
533
+ dataset: str | Dataset,
534
+ *,
535
+ lake: Lake | LakeReader,
536
+ refresh: bool = False,
537
+ ) -> list[ScopeKey]:
538
+ """Scopes whose imported files differ from what the server lists now.
539
+
540
+ Different means another path (a preliminary year that became final, or a moved
541
+ directory), or the same path with another size or server time (DATASUS
542
+ republished it). Publications without a recorded source are always reported.
543
+ Scopes the server no longer lists are a withdrawal, not returned here.
544
+ Read-only.
545
+
546
+ Args:
547
+ dataset: Dataset name, e.g. ``"sim_obitos"``, or a :class:`Dataset`.
548
+ lake: An open :class:`Lake` or :class:`LakeReader`.
549
+ refresh: ``True`` ignores the local listing cache and asks the server again.
550
+
551
+ Returns:
552
+ The scopes to re-import with :func:`import_dataset` and
553
+ ``policy="replace"``, ordered by year, UF and month.
554
+
555
+ Raises:
556
+ ValueError: ``dataset`` is unknown.
557
+ FtpUnavailable: the server did not answer after retries.
558
+
559
+ Examples:
560
+ >>> import omnisus as sus
561
+ >>> with sus.LakeReader() as lake: # doctest: +SKIP
562
+ ... stale = sus.outdated("sim_obitos", lake=lake)
563
+ >>> sus.import_dataset( # doctest: +SKIP
564
+ ... "sim_obitos", scopes=stale, policy="replace", run_id="refresh-2026-09"
565
+ ... )
566
+ """
567
+ d = resolve(dataset)
568
+ current = list_sources(d, refresh=refresh)
569
+ changed = {
570
+ row["scope"]
571
+ for row in lake.publications()
572
+ if row["dataset"] == d.name
573
+ and row["active"]
574
+ and row["scope"] in current
575
+ and _files_differ(row["sources"], current[row["scope"]].files)
576
+ }
577
+ return sorted(changed, key=lambda s: (s.ano, s.uf or "", s.mes or 0))
578
+
579
+
580
+ def _files_differ(recorded: Sequence[Mapping[str, object]], listed: Sequence[FtpEntry]) -> bool:
581
+ if [urlparse(str(r["source_uri"])).path for r in recorded] != [e.path for e in listed]:
582
+ return True
583
+ return any(
584
+ (r["source_bytes"] is not None and r["source_bytes"] != e.size_bytes)
585
+ or (
586
+ r["source_modified"] is not None
587
+ and r["source_modified"] != e.modified.strftime("%Y-%m-%dT%H:%M")
588
+ )
589
+ for r, e in zip(recorded, listed, strict=True)
590
+ )
591
+
592
+
593
+ __all__ = [
594
+ "ALL_UFS",
595
+ "AnalyticalProjection",
596
+ "CatalogAttachError",
597
+ "Citation",
598
+ "Dataset",
599
+ "DeletionResult",
600
+ "DerivedColumn",
601
+ "FtpEntry",
602
+ "FtpPathNotFound",
603
+ "FtpUnavailable",
604
+ "ImportAbortedError",
605
+ "ImportPolicy",
606
+ "ImportReport",
607
+ "ImportResult",
608
+ "Lake",
609
+ "LakeReader",
610
+ "Product",
611
+ "ScopeKey",
612
+ "ScopeOutcome",
613
+ "SourceContext",
614
+ "UnavailableField",
615
+ "__version__",
616
+ "analytical_projection",
617
+ "available",
618
+ "available_releases",
619
+ "available_sigtap",
620
+ "browse",
621
+ "check_columns",
622
+ "citation_from_publications",
623
+ "cite",
624
+ "datasets",
625
+ "describe_dataset",
626
+ "display_row",
627
+ "import_cnes_master",
628
+ "import_dataset",
629
+ "import_ibge_populacao",
630
+ "import_research",
631
+ "import_sigtap",
632
+ "label",
633
+ "latest_snapshot_id",
634
+ "load",
635
+ "municipality_join_key",
636
+ "municipality_join_key_sql",
637
+ "outdated",
638
+ "products",
639
+ "reference_join_sql",
640
+ "resolve",
641
+ "scopes_for",
642
+ "set_lake_dir",
643
+ ]
omnisus/_loop.py ADDED
@@ -0,0 +1,24 @@
1
+ """Run a coroutine from synchronous code, also inside a notebook's running event loop."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ from collections.abc import Callable, Coroutine
7
+ from concurrent.futures import ThreadPoolExecutor
8
+ from typing import Any
9
+
10
+
11
+ def run_sync[T](coroutine: Callable[[], Coroutine[Any, Any, T]]) -> T:
12
+ """Run ``coroutine()`` to completion, also from inside a running event loop.
13
+
14
+ A marimo or Jupyter cell already runs in an event loop, where
15
+ ``asyncio.run`` refuses to start another; there the coroutine gets its own
16
+ loop on a worker thread and the caller blocks until it finishes. Every
17
+ synchronous public function that awaits goes through here.
18
+ """
19
+ try:
20
+ asyncio.get_running_loop()
21
+ except RuntimeError:
22
+ return asyncio.run(coroutine())
23
+ with ThreadPoolExecutor(max_workers=1) as worker:
24
+ return worker.submit(lambda: asyncio.run(coroutine())).result()
omnisus/_version.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.2.0"
File without changes