Skip to content

Commit ad7d4fa

Browse files
Add AVAILABLE_DATABASES registry for database discovery (#235)
* feat(databases): replace wildcard imports with explicit imports define `AVAILABLE_DATABASES` * test: add unit tests for registry * docs: add section for discovering available databases
1 parent 8d63ae1 commit ad7d4fa

4 files changed

Lines changed: 220 additions & 9 deletions

File tree

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,27 @@ In Docker, just add an extra parameter `-e PYSUS_CACHEPATH="/home/me/desired/pat
108108
docker run -p 8888:8888 -e PYSUS_CACHEPATH="/home/me/desired/path/.pysus" pysus:latest
109109
```
110110

111+
## Discovering Available Databases
112+
113+
Use `AVAILABLE_DATABASES` to programmatically discover all database classes:
114+
115+
```python
116+
>>> from pysus import AVAILABLE_DATABASES
117+
>>> for db_class in AVAILABLE_DATABASES:
118+
... db = db_class()
119+
... print(f"{db.name}: {db.metadata['long_name']}")
120+
...
121+
CIHA: Comunicação de Internação Hospitalar e Ambulatorial
122+
CNES: Cadastro Nacional de Estabelecimentos de Saúde
123+
IBGE-DataSUS: Populaçao Residente, Censos, Contagens Populacionais e Projeçoes Intercensitarias
124+
PNI: Sistema de Informações do Programa Nacional de Imunizações
125+
SIA: Sistema de Informações Ambulatoriais
126+
SIH: Sistema de Informações Hospitalares
127+
SIM: Sistema de Informação sobre Mortalidade
128+
SINAN: Doenças e Agravos de Notificação
129+
SINASC: Sistema de Informações sobre Nascidos Vivos
130+
```
131+
111132
## Examples
112133

113134
Reading SINAN files:

pysus/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from importlib import metadata as importlib_metadata
55

66
from pysus.ftp.databases import * # noqa
7+
from pysus.ftp.databases import AVAILABLE_DATABASES
78

89

910
def get_version() -> str:
@@ -15,3 +16,9 @@ def get_version() -> str:
1516

1617
version: str = get_version()
1718
__version__: str = version
19+
20+
__all__ = [
21+
"AVAILABLE_DATABASES",
22+
"version",
23+
"__version__",
24+
]

pysus/ftp/databases/__init__.py

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,34 @@
1-
from .ciha import * # noqa
2-
from .cnes import * # noqa
3-
from .ibge_datasus import * # noqa
4-
from .pni import * # noqa
5-
from .sia import * # noqa
6-
from .sih import * # noqa
7-
from .sim import * # noqa
8-
from .sinan import * # noqa
9-
from .sinasc import * # noqa
1+
from .ciha import CIHA
2+
from .cnes import CNES
3+
from .ibge_datasus import IBGEDATASUS
4+
from .pni import PNI
5+
from .sia import SIA
6+
from .sih import SIH
7+
from .sim import SIM
8+
from .sinan import SINAN
9+
from .sinasc import SINASC
10+
11+
AVAILABLE_DATABASES = [
12+
CIHA,
13+
CNES,
14+
IBGEDATASUS,
15+
PNI,
16+
SIA,
17+
SIH,
18+
SIM,
19+
SINAN,
20+
SINASC,
21+
]
22+
23+
__all__ = [
24+
"CIHA",
25+
"CNES",
26+
"IBGEDATASUS",
27+
"PNI",
28+
"SIA",
29+
"SIH",
30+
"SIM",
31+
"SINAN",
32+
"SINASC",
33+
"AVAILABLE_DATABASES",
34+
]
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import unittest
2+
3+
import pysus
4+
from pysus.ftp import Database
5+
6+
7+
class TestAvailableDatabases(unittest.TestCase):
8+
"""Test suite for AVAILABLE_DATABASES registry"""
9+
10+
def test_available_databases_exists(self):
11+
"""Verify AVAILABLE_DATABASES is accessible from pysus namespace"""
12+
self.assertTrue(hasattr(pysus, "AVAILABLE_DATABASES"))
13+
self.assertIsInstance(pysus.AVAILABLE_DATABASES, list)
14+
15+
def test_available_databases_not_empty(self):
16+
"""Verify AVAILABLE_DATABASES list contains entries"""
17+
self.assertGreater(len(pysus.AVAILABLE_DATABASES), 0)
18+
19+
def test_all_are_database_classes(self):
20+
"""Verify all entries inherit from Database base class"""
21+
for db_class in pysus.AVAILABLE_DATABASES:
22+
with self.subTest(db_class=db_class):
23+
self.assertTrue(
24+
issubclass(db_class, Database),
25+
f"{db_class.__name__} does not inherit from Database",
26+
)
27+
28+
def test_all_have_required_attributes(self):
29+
"""Verify all database classes have name, paths, metadata"""
30+
for db_class in pysus.AVAILABLE_DATABASES:
31+
with self.subTest(db_class=db_class):
32+
db_instance = db_class()
33+
self.assertTrue(
34+
hasattr(db_instance, "name"),
35+
f"{db_class.__name__} missing 'name' attribute",
36+
)
37+
self.assertTrue(
38+
hasattr(db_instance, "paths"),
39+
f"{db_class.__name__} missing 'paths' attribute",
40+
)
41+
self.assertTrue(
42+
hasattr(db_instance, "metadata"),
43+
f"{db_class.__name__} missing 'metadata' attribute",
44+
)
45+
46+
def test_all_have_valid_metadata(self):
47+
"""Verify metadata contains required fields"""
48+
required_fields = {"long_name", "source", "description"}
49+
for db_class in pysus.AVAILABLE_DATABASES:
50+
with self.subTest(db_class=db_class):
51+
db_instance = db_class()
52+
metadata_keys = set(db_instance.metadata.keys())
53+
self.assertTrue(
54+
required_fields.issubset(metadata_keys),
55+
f"{db_class.__name__} metadata missing required fields. "
56+
f"Expected: {required_fields}, Got: {metadata_keys}",
57+
)
58+
# verify values exist (can be strings or tuples)
59+
for field in required_fields:
60+
value = db_instance.metadata[field]
61+
if field == "source":
62+
# can be a string or tuple of strings
63+
self.assertTrue(
64+
isinstance(value, (str, tuple)),
65+
f"{db_class.__name__}.metadata['source'] "
66+
f"must be str or tuple",
67+
)
68+
if isinstance(value, tuple):
69+
self.assertTrue(
70+
all(isinstance(s, str) for s in value),
71+
f"{db_class.__name__}.metadata['source'] "
72+
f"tuple must contain only strings",
73+
)
74+
else:
75+
# long_name and description should be strings
76+
# Note: Some databases may have empty descriptions
77+
self.assertIsInstance(
78+
value,
79+
str,
80+
f"{db_class.__name__}.metadata['{field}'] "
81+
f"is not a string",
82+
)
83+
84+
def test_expected_databases_present(self):
85+
"""Verify all expected database classes are included"""
86+
expected_databases = {
87+
"CIHA",
88+
"CNES",
89+
"IBGEDATASUS",
90+
"PNI",
91+
"SIA",
92+
"SIH",
93+
"SIM",
94+
"SINAN",
95+
"SINASC",
96+
}
97+
actual_databases = {
98+
db_class.__name__ for db_class in pysus.AVAILABLE_DATABASES
99+
}
100+
self.assertEqual(
101+
expected_databases,
102+
actual_databases,
103+
f"Database list mismatch. "
104+
f"Missing: {expected_databases - actual_databases}, "
105+
f"Extra: {actual_databases - expected_databases}",
106+
)
107+
108+
def test_can_instantiate_all_databases(self):
109+
"""Verify all database classes can be instantiated without errors"""
110+
for db_class in pysus.AVAILABLE_DATABASES:
111+
with self.subTest(db_class=db_class):
112+
try:
113+
db_instance = db_class()
114+
self.assertIsInstance(db_instance, Database)
115+
except Exception as e:
116+
self.fail(
117+
f"Failed to instantiate {db_class.__name__}: {e}"
118+
)
119+
120+
def test_list_order_is_consistent(self):
121+
"""Document that the list order is alphabetical by class name"""
122+
class_names = [
123+
db_class.__name__ for db_class in pysus.AVAILABLE_DATABASES
124+
]
125+
sorted_names = sorted(class_names)
126+
self.assertEqual(
127+
class_names,
128+
sorted_names,
129+
"AVAILABLE_DATABASES should be in alphabetical order",
130+
)
131+
132+
def test_usage_example(self):
133+
"""Demonstrate iteration pattern for accessing metadata"""
134+
databases_info = []
135+
for db_class in pysus.AVAILABLE_DATABASES:
136+
db = db_class()
137+
databases_info.append(
138+
{
139+
"name": db.name,
140+
"long_name": db.metadata["long_name"],
141+
"description": db.metadata["description"],
142+
}
143+
)
144+
145+
self.assertEqual(len(databases_info), 9)
146+
147+
# vrify all entries have the expected structure
148+
for info in databases_info:
149+
self.assertIn("name", info)
150+
self.assertIn("long_name", info)
151+
self.assertIn("description", info)
152+
self.assertTrue(isinstance(info["name"], str))
153+
self.assertTrue(isinstance(info["long_name"], str))
154+
self.assertTrue(isinstance(info["description"], str))
155+
156+
157+
if __name__ == "__main__":
158+
unittest.main()

0 commit comments

Comments
 (0)