sigaa-tools 0.1.1__tar.gz

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 (80) hide show
  1. sigaa_tools-0.1.1/LICENSE +21 -0
  2. sigaa_tools-0.1.1/PKG-INFO +315 -0
  3. sigaa_tools-0.1.1/README.md +287 -0
  4. sigaa_tools-0.1.1/pyproject.toml +59 -0
  5. sigaa_tools-0.1.1/setup.cfg +4 -0
  6. sigaa_tools-0.1.1/sigaa/__init__.py +3 -0
  7. sigaa_tools-0.1.1/sigaa/auth.py +42 -0
  8. sigaa_tools-0.1.1/sigaa/cli.py +982 -0
  9. sigaa_tools-0.1.1/sigaa/client.py +379 -0
  10. sigaa_tools-0.1.1/sigaa/config.py +123 -0
  11. sigaa_tools-0.1.1/sigaa/curriculum.py +139 -0
  12. sigaa_tools-0.1.1/sigaa/documents.py +221 -0
  13. sigaa_tools-0.1.1/sigaa/exporters/__init__.py +1 -0
  14. sigaa_tools-0.1.1/sigaa/exporters/ics.py +119 -0
  15. sigaa_tools-0.1.1/sigaa/http.py +146 -0
  16. sigaa_tools-0.1.1/sigaa/mcp_server.py +826 -0
  17. sigaa_tools-0.1.1/sigaa/models.py +350 -0
  18. sigaa_tools-0.1.1/sigaa/parsers/__init__.py +1 -0
  19. sigaa_tools-0.1.1/sigaa/parsers/attendance.py +57 -0
  20. sigaa_tools-0.1.1/sigaa/parsers/curriculum.py +255 -0
  21. sigaa_tools-0.1.1/sigaa/parsers/grades.py +86 -0
  22. sigaa_tools-0.1.1/sigaa/parsers/materials.py +105 -0
  23. sigaa_tools-0.1.1/sigaa/parsers/matricula.py +89 -0
  24. sigaa_tools-0.1.1/sigaa/parsers/news.py +99 -0
  25. sigaa_tools-0.1.1/sigaa/parsers/participantes.py +88 -0
  26. sigaa_tools-0.1.1/sigaa/parsers/plano.py +45 -0
  27. sigaa_tools-0.1.1/sigaa/parsers/portal.py +214 -0
  28. sigaa_tools-0.1.1/sigaa/parsers/schedule.py +37 -0
  29. sigaa_tools-0.1.1/sigaa/parsers/sipac.py +476 -0
  30. sigaa_tools-0.1.1/sigaa/parsers/tarefa.py +87 -0
  31. sigaa_tools-0.1.1/sigaa/parsers/transcript.py +265 -0
  32. sigaa_tools-0.1.1/sigaa/services/__init__.py +1 -0
  33. sigaa_tools-0.1.1/sigaa/services/sync.py +212 -0
  34. sigaa_tools-0.1.1/sigaa/services/whatsnew.py +44 -0
  35. sigaa_tools-0.1.1/sigaa/setup_wizard.py +268 -0
  36. sigaa_tools-0.1.1/sigaa/sipac.py +313 -0
  37. sigaa_tools-0.1.1/sigaa/store/__init__.py +1 -0
  38. sigaa_tools-0.1.1/sigaa/store/db.py +154 -0
  39. sigaa_tools-0.1.1/sigaa/store/repository.py +448 -0
  40. sigaa_tools-0.1.1/sigaa_tools.egg-info/PKG-INFO +315 -0
  41. sigaa_tools-0.1.1/sigaa_tools.egg-info/SOURCES.txt +78 -0
  42. sigaa_tools-0.1.1/sigaa_tools.egg-info/dependency_links.txt +1 -0
  43. sigaa_tools-0.1.1/sigaa_tools.egg-info/entry_points.txt +3 -0
  44. sigaa_tools-0.1.1/sigaa_tools.egg-info/requires.txt +12 -0
  45. sigaa_tools-0.1.1/sigaa_tools.egg-info/top_level.txt +1 -0
  46. sigaa_tools-0.1.1/tests/test_academic_documents.py +330 -0
  47. sigaa_tools-0.1.1/tests/test_attendance.py +24 -0
  48. sigaa_tools-0.1.1/tests/test_attendance_store.py +64 -0
  49. sigaa_tools-0.1.1/tests/test_config.py +30 -0
  50. sigaa_tools-0.1.1/tests/test_curriculum_cli.py +137 -0
  51. sigaa_tools-0.1.1/tests/test_curriculum_client.py +119 -0
  52. sigaa_tools-0.1.1/tests/test_curriculum_parser.py +117 -0
  53. sigaa_tools-0.1.1/tests/test_curriculum_view.py +87 -0
  54. sigaa_tools-0.1.1/tests/test_grades_deadlines.py +55 -0
  55. sigaa_tools-0.1.1/tests/test_http_configuration.py +21 -0
  56. sigaa_tools-0.1.1/tests/test_materials.py +56 -0
  57. sigaa_tools-0.1.1/tests/test_matricula.py +37 -0
  58. sigaa_tools-0.1.1/tests/test_mcp_curriculum.py +124 -0
  59. sigaa_tools-0.1.1/tests/test_mcp_curriculum_protocol.py +64 -0
  60. sigaa_tools-0.1.1/tests/test_mcp_document_resources.py +121 -0
  61. sigaa_tools-0.1.1/tests/test_mcp_documents.py +135 -0
  62. sigaa_tools-0.1.1/tests/test_mcp_downloads.py +184 -0
  63. sigaa_tools-0.1.1/tests/test_mcp_mode.py +78 -0
  64. sigaa_tools-0.1.1/tests/test_mcp_protocol.py +83 -0
  65. sigaa_tools-0.1.1/tests/test_mcp_sipac.py +132 -0
  66. sigaa_tools-0.1.1/tests/test_parsers.py +134 -0
  67. sigaa_tools-0.1.1/tests/test_participantes.py +73 -0
  68. sigaa_tools-0.1.1/tests/test_plan_deadlines.py +124 -0
  69. sigaa_tools-0.1.1/tests/test_plano.py +29 -0
  70. sigaa_tools-0.1.1/tests/test_setup_wizard.py +206 -0
  71. sigaa_tools-0.1.1/tests/test_sipac_cli.py +132 -0
  72. sigaa_tools-0.1.1/tests/test_sipac_client.py +242 -0
  73. sigaa_tools-0.1.1/tests/test_sipac_parser.py +99 -0
  74. sigaa_tools-0.1.1/tests/test_sipac_search_client.py +109 -0
  75. sigaa_tools-0.1.1/tests/test_sipac_search_parser.py +115 -0
  76. sigaa_tools-0.1.1/tests/test_store.py +54 -0
  77. sigaa_tools-0.1.1/tests/test_transcript_parser.py +157 -0
  78. sigaa_tools-0.1.1/tests/test_turma_grades.py +94 -0
  79. sigaa_tools-0.1.1/tests/test_turma_postback_isolation.py +88 -0
  80. sigaa_tools-0.1.1/tests/test_whatsnew.py +61 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Puca Vaz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,315 @@
1
+ Metadata-Version: 2.4
2
+ Name: sigaa-tools
3
+ Version: 0.1.1
4
+ Summary: User-friendly SIGAA and public SIPAC UFPB client (CLI + MCP)
5
+ Author: Puca Vaz
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/PucaVaz/sigaa-tools
8
+ Project-URL: Repository, https://github.com/PucaVaz/sigaa-tools
9
+ Project-URL: Issues, https://github.com/PucaVaz/sigaa-tools/issues
10
+ Keywords: sigaa,sipac,ufpb,mcp,cli,academic
11
+ Classifier: Environment :: Console
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Requires-Python: >=3.11
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: httpx>=0.27
18
+ Requires-Dist: beautifulsoup4>=4.12
19
+ Requires-Dist: lxml>=5.0
20
+ Requires-Dist: keyring>=24
21
+ Requires-Dist: pypdf<7,>=5.0
22
+ Provides-Extra: mcp
23
+ Requires-Dist: mcp<2,>=1.27; extra == "mcp"
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=8.0; extra == "dev"
26
+ Requires-Dist: ruff>=0.6.0; extra == "dev"
27
+ Dynamic: license-file
28
+
29
+ # sigaa-tools
30
+
31
+ > [!IMPORTANT]
32
+ > This is an unofficial personal project and is not affiliated with, endorsed
33
+ > by, or supported by UFPB, SIGAA, or SIPAC. Use it responsibly, keep request rates low,
34
+ > and follow the rules that apply to your SIGAA account.
35
+ > See [Privacidade e segurança](docs/privacidade.mdx) for what is stored and what is never stored.
36
+
37
+ Friendly CLI and MCP server for **SIGAA UFPB** and **SIPAC** public process lookup. Fetch your classes, grades, deadlines, and curriculum all at once without opening the web portal.
38
+
39
+ ## Quickstart
40
+
41
+ **Step 1:** Install [uv](https://docs.astral.sh/uv/) (once only):
42
+ ```bash
43
+ brew install uv
44
+ # or: curl -LsSf https://astral.sh/uv/install.sh | sh
45
+ ```
46
+
47
+ **Step 2:** Install sigaa:
48
+ ```bash
49
+ uv tool install "sigaa-tools[mcp] @ git+https://github.com/PucaVaz/sigaa-tools"
50
+ ```
51
+
52
+ **Step 3:** Run the setup wizard:
53
+ ```bash
54
+ sigaa init
55
+ ```
56
+
57
+ That's it. The wizard prompts for your SIGAA credentials, stores them securely, syncs your data, and optionally sets up MCP for Claude Code or background polling.
58
+
59
+ ## What you get
60
+
61
+ - **CLI**: `sigaa classes`, `sigaa news`, `sigaa grades`, `sigaa deadlines`, etc. Fast, offline-first queries from a local SQLite database.
62
+ - **MCP server**: Wire into Claude Code (or any MCP client) to let AI agents fetch your SIGAA data on demand.
63
+ - **SIPAC lookup**: `sigaa sipac process 23074.056437/2026-26` — look up public UFPB administrative processes without a browser.
64
+
65
+ ## Common commands
66
+
67
+ ```bash
68
+ sigaa sync # fetch & persist new news, deadlines, grades
69
+ sigaa sync --bodies # also fetch full news article text
70
+ sigaa classes --schedule # enrolled classes with decoded weekly schedule
71
+ sigaa news --class DSCO00022 # news for one class
72
+ sigaa news --unread --mark-seen
73
+ sigaa grades --semester 2025.1 # grades by semester
74
+ sigaa deadlines # assessment/task due dates
75
+ sigaa ics --out sigaa.ics # export classes + deadlines as calendar
76
+ sigaa curriculum # live progress & required courses
77
+ sigaa cra --json # official CRA as JSON
78
+ sigaa watch --interval 900 # continuous sync in foreground
79
+ ```
80
+
81
+ Store-backed commands (`classes`, `news`, `grades`, `deadlines`) are fast and offline. Network commands (`sync`, `watch`, `curriculum`, `cra`, downloads) need internet.
82
+
83
+ ## SIPAC public process lookup
84
+
85
+ Use this feature to follow UFPB administrative processes without opening each SIPAC page manually. Look up one process by its complete number or find processes by an interested party name or identifier. Process details include the current status, subject, interested parties, public documents, routing movements, status changes, and attached files.
86
+
87
+ ```bash
88
+ sigaa sipac process 23074.056437/2026-26
89
+ sigaa sipac process 23074.056437/2026-26 --json
90
+ sigaa sipac search --name "Fulano de Tal"
91
+ sigaa sipac search --identifier "12345678901" --page 2 --json
92
+ ```
93
+
94
+ Example summary:
95
+
96
+ ```text
97
+ 23074.056437/2026-26 [ATIVO]
98
+ ANÁLISE DE PROPOSTA DE RESOLUÇÃO SOBRE DISTRIBUIÇÃO DE ENCARGOS DIDÁTICOS
99
+ Origin: CI - DIREÇÃO DE CENTRO (11.01.45.01)
100
+ Opened: 12/06/2026 17:26
101
+ Interested parties: 1 | Documents: 14 | Movements: 6
102
+ ```
103
+
104
+ Agents can call `sipac_get_public_process` with `{"number": "23074.056437/2026-26"}`. To search, call `sipac_search_public_processes` with either `name` or `identifier`. Both interfaces use the same `schema_version: 1` contracts as the CLI JSON output. Public SIPAC commands do not authenticate, bypass restricted documents, change processes, or persist results. See the [SIPAC process guide](docs/sipac-processes.mdx) for fields, use cases, privacy guidance, and error handling.
105
+
106
+ Headless fallback: `export SIGAA_USER=... SIGAA_PASS=...`.
107
+ Optional `SIGAA_DB=/path/to/sigaa.db` to override the store location.
108
+
109
+ ## MCP server (for code agents)
110
+
111
+ Run `sigaa init`; it can detect or create `.mcp.json` and add the `sigaa` MCP
112
+ server without manual absolute-path editing.
113
+
114
+ Tools: `sigaa_list_classes`, `sigaa_list_news`, `sigaa_get_news_body`,
115
+ `sigaa_get_schedule`, `sigaa_list_grades`, `sigaa_list_deadlines`,
116
+ `sigaa_get_curriculum`, `sigaa_get_cra`, `sigaa_export_ics`,
117
+ `sipac_get_public_process`, `sipac_search_public_processes`,
118
+ `sigaa_download_historico`,
119
+ `sigaa_download_declaracao_vinculo`, `sigaa_download_atestado_matricula`,
120
+ `sigaa_sync`. Store-backed reads are offline; sync, live lookups, and downloads
121
+ touch the network.
122
+
123
+ `sipac_get_public_process(number)` returns the public process metadata exposed
124
+ by UFPB: general data, interested parties, documents and public download links,
125
+ movements, status changes, and attached files. It shares schema version 1 with
126
+ `sigaa sipac process --json` and does not require credentials.
127
+
128
+ `sipac_search_public_processes(name?, identifier?, page=1)` finds public
129
+ processes by one interested-party field. Pass exactly one of `name` or
130
+ `identifier`. Each response contains at most the 15 results exposed by one
131
+ portal page. Results are not retained after the request finishes.
132
+ Identifiers such as CPF, registration number, and CNPJ are personal data. Query
133
+ them only for a legitimate purpose and avoid copying them into logs.
134
+
135
+ `sigaa_get_curriculum` is networked and uses the same normalized contract and
136
+ filters as `sigaa curriculum`: `status`, `required_only`, `period`,
137
+ `include_requirements`, and `include_cra`. Its default `current` view contains
138
+ enrolled components plus required pending ones. Pending optional components are
139
+ choices toward the remaining optional workload, not courses that must all be
140
+ completed. `sigaa_get_cra` is also networked and reads the official CRA from the
141
+ academic transcript; a new student may receive `source: "unavailable"` until
142
+ SIGAA reports one. Neither response exposes SIGAA's internal student id.
143
+
144
+ Every download tool accepts a safe filename (not an arbitrary path), never
145
+ overwrites, and writes under the app's private `downloads` directory. This
146
+ covers the three academic documents plus `sigaa_download_material` and
147
+ `sigaa_download_tarefa_anexo`; on those two the parameter is named `filename`
148
+ and no longer accepts a path. Filenames SIGAA supplies in `Content-Disposition`
149
+ are sanitized rather than trusted. Set
150
+ `SIGAA_DOWNLOAD_DIR` on the MCP server to choose another directory. A successful
151
+ call returns both structured metadata (filename, MIME type, and size) and
152
+ an opaque MCP `ResourceLink`. Clients that support resource links can present the
153
+ document as an attachment or download; opening it reads the saved file through
154
+ MCP without putting its bytes in the original tool response. The resource link is
155
+ valid for the current server session, while the local file remains on disk. HTML
156
+ certificates are exposed as download-only binary resources so an MCP client does
157
+ not execute the report's active SIGAA markup inline.
158
+
159
+ ### Tool surface: `SIGAA_MODE`
160
+
161
+ `SIGAA_MODE` picks which tools the MCP server exposes. It defaults to `local`,
162
+ which exposes all 24 tools — **nothing is ever removed from a local or
163
+ self-hosted install**.
164
+
165
+ `SIGAA_MODE=hosted` is for a shared, multi-tenant deployment. It withholds the
166
+ five tools that write files to the server's disk:
167
+
168
+ | Withheld in hosted | Why |
169
+ | --- | --- |
170
+ | `sigaa_download_material` | Unbounded file writes; a class can hold hundreds of MB |
171
+ | `sigaa_download_tarefa_anexo` | Same |
172
+ | `sigaa_download_historico` | Needs a per-tenant download directory, which does not exist yet |
173
+ | `sigaa_download_declaracao_vinculo` | Same |
174
+ | `sigaa_download_atestado_matricula` | Same |
175
+
176
+ Everything else stays, including `sigaa_sync` — without it a tenant's store is
177
+ empty and every store-backed tool returns nothing. Its writes are confined to
178
+ `SIGAA_DB`. No MCP tool mutates SIGAA state in any mode: enrollment
179
+ selection and confirmation are deliberately CLI-only.
180
+
181
+ The mode is applied at import, so it holds whether the server is started via
182
+ `sigaa-mcp`, `python -m sigaa.mcp_server`, or by importing `mcp` from
183
+ `sigaa.mcp_server` and mounting it. Withheld tools are removed from the tool
184
+ manager, so they are uncallable rather than merely hidden. An unrecognized
185
+ `SIGAA_MODE` raises at startup instead of falling back to `local`.
186
+
187
+ A hosted deployment must also set `SIGAA_DB` and `SIGAA_DOWNLOAD_DIR` per
188
+ tenant. Restoring the three academic-document tools in hosted mode depends on
189
+ that isolation existing first.
190
+
191
+ ## Scheduled polling
192
+
193
+ Run `sigaa init`; it can write the launchd plist on macOS or print the cron
194
+ entry for other operating systems.
195
+
196
+ ## Credentials
197
+
198
+ Keyring-first, env-second. Never commit credentials, cookies, downloaded live HTML, SQLite databases, exported PDFs, or `.env` files.
199
+
200
+ `sigaa init` stores your SIGAA password securely in your OS keyring and verifies the login. Public SIPAC queries do not need credentials.
201
+
202
+ For headless environments: `export SIGAA_USER=username SIGAA_PASS=password`. Optional: `SIGAA_DB=/path/to/sigaa.db` to override the store location.
203
+
204
+ ## Advanced manual configuration
205
+
206
+ Usually you should run `sigaa init`. The examples below are for manual setups,
207
+ debugging, or custom automation.
208
+
209
+ ### Manual MCP
210
+
211
+ Run with `sigaa-mcp` (stdio). Wire into Claude Code via `.mcp.json`:
212
+
213
+ ```json
214
+ {
215
+ "mcpServers": {
216
+ "sigaa": {
217
+ "command": "uvx",
218
+ "args": [
219
+ "--from",
220
+ "sigaa-tools[mcp] @ git+https://github.com/PucaVaz/sigaa-tools",
221
+ "sigaa-mcp"
222
+ ]
223
+ }
224
+ }
225
+ }
226
+ ```
227
+
228
+ This form carries no machine-specific path, so the file stays valid when
229
+ committed and shared with your team. `sigaa init` writes it for you. The server
230
+ reads the active account from your keyring; add
231
+ `"env": { "SIGAA_USER": "your_user" }` only if keyring is unavailable.
232
+
233
+ ### Manual scheduled polling
234
+
235
+ **macOS (launchd)** - sync every 30 min. Save as
236
+ `~/Library/LaunchAgents/ai.sigaa.sync.plist` then
237
+ `launchctl load` it:
238
+
239
+ ```xml
240
+ <?xml version="1.0" encoding="UTF-8"?>
241
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
242
+ "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
243
+ <plist version="1.0"><dict>
244
+ <key>Label</key><string>ai.sigaa.sync</string>
245
+ <key>ProgramArguments</key>
246
+ <array>
247
+ <string>/Users/you/.local/bin/sigaa</string>
248
+ <string>sync</string>
249
+ </array>
250
+ <key>EnvironmentVariables</key>
251
+ <dict><key>SIGAA_USER</key><string>your_user</string></dict>
252
+ <key>StartInterval</key><integer>1800</integer>
253
+ </dict></plist>
254
+ ```
255
+
256
+ `uv tool install` puts `sigaa` in `~/.local/bin`; launchd needs it spelled out in
257
+ full, since it does not expand `~`. Run `which sigaa` to confirm yours.
258
+
259
+ **Linux (cron)**: `*/30 * * * * SIGAA_USER=you $HOME/.local/bin/sigaa sync`
260
+ (password from keyring, or add `SIGAA_PASS`).
261
+
262
+ ## Architecture
263
+
264
+ ```
265
+ sigaa/
266
+ config.py endpoints, JSF constants, slot-time table, creds resolution
267
+ http.py session: cookie jar, ViewState, re-login + retry
268
+ auth.py login flow
269
+ client.py SigaaClient -> domain models
270
+ sipac.py unauthenticated public SIPAC process client + JSON contract
271
+ models.py Student, Turma, NewsItem, Schedule, CurriculumStatus
272
+ parsers/ portal, news, schedule, curriculum, transcript, SIPAC process
273
+ store/ SQLite db + repository (dedup, queries)
274
+ services/ sync (fetch -> diff -> persist)
275
+ cli.py command line
276
+ mcp_server.py agent tools
277
+ ```
278
+
279
+ Adding a feature (materials, attendance) = a parser + client method and a
280
+ CLI/MCP surface, plus store columns when it is persisted. HTML changes touch
281
+ only `parsers/`. Implemented so far: classes, news (+bodies), grades, deadlines,
282
+ curriculum progress, official CRA, academic documents, and ICS export.
283
+ Public SIPAC administrative-process consultation is also available live.
284
+
285
+ `exporters/` turns store data into interchange formats (currently `ics`).
286
+
287
+ ## Schedule decoding
288
+
289
+ `6M2345` → Fri (day 6) morning slots 2–5. Days 2=Mon..7=Sat, shift M/T/N.
290
+ Clock times in `config.SLOT_TIMES_UNCONFIRMED` are an **unconfirmed** default —
291
+ verify against a turma's "Plano de Curso" before using them for calendar export.
292
+
293
+ ## Tests
294
+
295
+ ```bash
296
+ pytest # offline: parsers run on fixtures, store on in-memory sqlite
297
+ ```
298
+
299
+ ## Contributing
300
+
301
+ Interested in hacking on sigaa-tools? See [CONTRIBUTING.md](CONTRIBUTING.md) for setup and release instructions.
302
+
303
+ ## Security
304
+
305
+ Single-user, local-first tool. `config.py` reads credentials from keyring first
306
+ and environment variables second. The SQLite db is local and may contain student
307
+ data copied from SIGAA. `.gitignore` excludes `*.db`, `.env`, and live HTML
308
+ dumps, but review generated files before sharing logs, issues, or screenshots.
309
+ The SIPAC process feature is read-only and public; it does not send stored
310
+ credentials, but its responses can contain names and identifiers already shown
311
+ by the public portal, so handle exported JSON responsibly.
312
+
313
+ ## License
314
+
315
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,287 @@
1
+ # sigaa-tools
2
+
3
+ > [!IMPORTANT]
4
+ > This is an unofficial personal project and is not affiliated with, endorsed
5
+ > by, or supported by UFPB, SIGAA, or SIPAC. Use it responsibly, keep request rates low,
6
+ > and follow the rules that apply to your SIGAA account.
7
+ > See [Privacidade e segurança](docs/privacidade.mdx) for what is stored and what is never stored.
8
+
9
+ Friendly CLI and MCP server for **SIGAA UFPB** and **SIPAC** public process lookup. Fetch your classes, grades, deadlines, and curriculum all at once without opening the web portal.
10
+
11
+ ## Quickstart
12
+
13
+ **Step 1:** Install [uv](https://docs.astral.sh/uv/) (once only):
14
+ ```bash
15
+ brew install uv
16
+ # or: curl -LsSf https://astral.sh/uv/install.sh | sh
17
+ ```
18
+
19
+ **Step 2:** Install sigaa:
20
+ ```bash
21
+ uv tool install "sigaa-tools[mcp] @ git+https://github.com/PucaVaz/sigaa-tools"
22
+ ```
23
+
24
+ **Step 3:** Run the setup wizard:
25
+ ```bash
26
+ sigaa init
27
+ ```
28
+
29
+ That's it. The wizard prompts for your SIGAA credentials, stores them securely, syncs your data, and optionally sets up MCP for Claude Code or background polling.
30
+
31
+ ## What you get
32
+
33
+ - **CLI**: `sigaa classes`, `sigaa news`, `sigaa grades`, `sigaa deadlines`, etc. Fast, offline-first queries from a local SQLite database.
34
+ - **MCP server**: Wire into Claude Code (or any MCP client) to let AI agents fetch your SIGAA data on demand.
35
+ - **SIPAC lookup**: `sigaa sipac process 23074.056437/2026-26` — look up public UFPB administrative processes without a browser.
36
+
37
+ ## Common commands
38
+
39
+ ```bash
40
+ sigaa sync # fetch & persist new news, deadlines, grades
41
+ sigaa sync --bodies # also fetch full news article text
42
+ sigaa classes --schedule # enrolled classes with decoded weekly schedule
43
+ sigaa news --class DSCO00022 # news for one class
44
+ sigaa news --unread --mark-seen
45
+ sigaa grades --semester 2025.1 # grades by semester
46
+ sigaa deadlines # assessment/task due dates
47
+ sigaa ics --out sigaa.ics # export classes + deadlines as calendar
48
+ sigaa curriculum # live progress & required courses
49
+ sigaa cra --json # official CRA as JSON
50
+ sigaa watch --interval 900 # continuous sync in foreground
51
+ ```
52
+
53
+ Store-backed commands (`classes`, `news`, `grades`, `deadlines`) are fast and offline. Network commands (`sync`, `watch`, `curriculum`, `cra`, downloads) need internet.
54
+
55
+ ## SIPAC public process lookup
56
+
57
+ Use this feature to follow UFPB administrative processes without opening each SIPAC page manually. Look up one process by its complete number or find processes by an interested party name or identifier. Process details include the current status, subject, interested parties, public documents, routing movements, status changes, and attached files.
58
+
59
+ ```bash
60
+ sigaa sipac process 23074.056437/2026-26
61
+ sigaa sipac process 23074.056437/2026-26 --json
62
+ sigaa sipac search --name "Fulano de Tal"
63
+ sigaa sipac search --identifier "12345678901" --page 2 --json
64
+ ```
65
+
66
+ Example summary:
67
+
68
+ ```text
69
+ 23074.056437/2026-26 [ATIVO]
70
+ ANÁLISE DE PROPOSTA DE RESOLUÇÃO SOBRE DISTRIBUIÇÃO DE ENCARGOS DIDÁTICOS
71
+ Origin: CI - DIREÇÃO DE CENTRO (11.01.45.01)
72
+ Opened: 12/06/2026 17:26
73
+ Interested parties: 1 | Documents: 14 | Movements: 6
74
+ ```
75
+
76
+ Agents can call `sipac_get_public_process` with `{"number": "23074.056437/2026-26"}`. To search, call `sipac_search_public_processes` with either `name` or `identifier`. Both interfaces use the same `schema_version: 1` contracts as the CLI JSON output. Public SIPAC commands do not authenticate, bypass restricted documents, change processes, or persist results. See the [SIPAC process guide](docs/sipac-processes.mdx) for fields, use cases, privacy guidance, and error handling.
77
+
78
+ Headless fallback: `export SIGAA_USER=... SIGAA_PASS=...`.
79
+ Optional `SIGAA_DB=/path/to/sigaa.db` to override the store location.
80
+
81
+ ## MCP server (for code agents)
82
+
83
+ Run `sigaa init`; it can detect or create `.mcp.json` and add the `sigaa` MCP
84
+ server without manual absolute-path editing.
85
+
86
+ Tools: `sigaa_list_classes`, `sigaa_list_news`, `sigaa_get_news_body`,
87
+ `sigaa_get_schedule`, `sigaa_list_grades`, `sigaa_list_deadlines`,
88
+ `sigaa_get_curriculum`, `sigaa_get_cra`, `sigaa_export_ics`,
89
+ `sipac_get_public_process`, `sipac_search_public_processes`,
90
+ `sigaa_download_historico`,
91
+ `sigaa_download_declaracao_vinculo`, `sigaa_download_atestado_matricula`,
92
+ `sigaa_sync`. Store-backed reads are offline; sync, live lookups, and downloads
93
+ touch the network.
94
+
95
+ `sipac_get_public_process(number)` returns the public process metadata exposed
96
+ by UFPB: general data, interested parties, documents and public download links,
97
+ movements, status changes, and attached files. It shares schema version 1 with
98
+ `sigaa sipac process --json` and does not require credentials.
99
+
100
+ `sipac_search_public_processes(name?, identifier?, page=1)` finds public
101
+ processes by one interested-party field. Pass exactly one of `name` or
102
+ `identifier`. Each response contains at most the 15 results exposed by one
103
+ portal page. Results are not retained after the request finishes.
104
+ Identifiers such as CPF, registration number, and CNPJ are personal data. Query
105
+ them only for a legitimate purpose and avoid copying them into logs.
106
+
107
+ `sigaa_get_curriculum` is networked and uses the same normalized contract and
108
+ filters as `sigaa curriculum`: `status`, `required_only`, `period`,
109
+ `include_requirements`, and `include_cra`. Its default `current` view contains
110
+ enrolled components plus required pending ones. Pending optional components are
111
+ choices toward the remaining optional workload, not courses that must all be
112
+ completed. `sigaa_get_cra` is also networked and reads the official CRA from the
113
+ academic transcript; a new student may receive `source: "unavailable"` until
114
+ SIGAA reports one. Neither response exposes SIGAA's internal student id.
115
+
116
+ Every download tool accepts a safe filename (not an arbitrary path), never
117
+ overwrites, and writes under the app's private `downloads` directory. This
118
+ covers the three academic documents plus `sigaa_download_material` and
119
+ `sigaa_download_tarefa_anexo`; on those two the parameter is named `filename`
120
+ and no longer accepts a path. Filenames SIGAA supplies in `Content-Disposition`
121
+ are sanitized rather than trusted. Set
122
+ `SIGAA_DOWNLOAD_DIR` on the MCP server to choose another directory. A successful
123
+ call returns both structured metadata (filename, MIME type, and size) and
124
+ an opaque MCP `ResourceLink`. Clients that support resource links can present the
125
+ document as an attachment or download; opening it reads the saved file through
126
+ MCP without putting its bytes in the original tool response. The resource link is
127
+ valid for the current server session, while the local file remains on disk. HTML
128
+ certificates are exposed as download-only binary resources so an MCP client does
129
+ not execute the report's active SIGAA markup inline.
130
+
131
+ ### Tool surface: `SIGAA_MODE`
132
+
133
+ `SIGAA_MODE` picks which tools the MCP server exposes. It defaults to `local`,
134
+ which exposes all 24 tools — **nothing is ever removed from a local or
135
+ self-hosted install**.
136
+
137
+ `SIGAA_MODE=hosted` is for a shared, multi-tenant deployment. It withholds the
138
+ five tools that write files to the server's disk:
139
+
140
+ | Withheld in hosted | Why |
141
+ | --- | --- |
142
+ | `sigaa_download_material` | Unbounded file writes; a class can hold hundreds of MB |
143
+ | `sigaa_download_tarefa_anexo` | Same |
144
+ | `sigaa_download_historico` | Needs a per-tenant download directory, which does not exist yet |
145
+ | `sigaa_download_declaracao_vinculo` | Same |
146
+ | `sigaa_download_atestado_matricula` | Same |
147
+
148
+ Everything else stays, including `sigaa_sync` — without it a tenant's store is
149
+ empty and every store-backed tool returns nothing. Its writes are confined to
150
+ `SIGAA_DB`. No MCP tool mutates SIGAA state in any mode: enrollment
151
+ selection and confirmation are deliberately CLI-only.
152
+
153
+ The mode is applied at import, so it holds whether the server is started via
154
+ `sigaa-mcp`, `python -m sigaa.mcp_server`, or by importing `mcp` from
155
+ `sigaa.mcp_server` and mounting it. Withheld tools are removed from the tool
156
+ manager, so they are uncallable rather than merely hidden. An unrecognized
157
+ `SIGAA_MODE` raises at startup instead of falling back to `local`.
158
+
159
+ A hosted deployment must also set `SIGAA_DB` and `SIGAA_DOWNLOAD_DIR` per
160
+ tenant. Restoring the three academic-document tools in hosted mode depends on
161
+ that isolation existing first.
162
+
163
+ ## Scheduled polling
164
+
165
+ Run `sigaa init`; it can write the launchd plist on macOS or print the cron
166
+ entry for other operating systems.
167
+
168
+ ## Credentials
169
+
170
+ Keyring-first, env-second. Never commit credentials, cookies, downloaded live HTML, SQLite databases, exported PDFs, or `.env` files.
171
+
172
+ `sigaa init` stores your SIGAA password securely in your OS keyring and verifies the login. Public SIPAC queries do not need credentials.
173
+
174
+ For headless environments: `export SIGAA_USER=username SIGAA_PASS=password`. Optional: `SIGAA_DB=/path/to/sigaa.db` to override the store location.
175
+
176
+ ## Advanced manual configuration
177
+
178
+ Usually you should run `sigaa init`. The examples below are for manual setups,
179
+ debugging, or custom automation.
180
+
181
+ ### Manual MCP
182
+
183
+ Run with `sigaa-mcp` (stdio). Wire into Claude Code via `.mcp.json`:
184
+
185
+ ```json
186
+ {
187
+ "mcpServers": {
188
+ "sigaa": {
189
+ "command": "uvx",
190
+ "args": [
191
+ "--from",
192
+ "sigaa-tools[mcp] @ git+https://github.com/PucaVaz/sigaa-tools",
193
+ "sigaa-mcp"
194
+ ]
195
+ }
196
+ }
197
+ }
198
+ ```
199
+
200
+ This form carries no machine-specific path, so the file stays valid when
201
+ committed and shared with your team. `sigaa init` writes it for you. The server
202
+ reads the active account from your keyring; add
203
+ `"env": { "SIGAA_USER": "your_user" }` only if keyring is unavailable.
204
+
205
+ ### Manual scheduled polling
206
+
207
+ **macOS (launchd)** - sync every 30 min. Save as
208
+ `~/Library/LaunchAgents/ai.sigaa.sync.plist` then
209
+ `launchctl load` it:
210
+
211
+ ```xml
212
+ <?xml version="1.0" encoding="UTF-8"?>
213
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
214
+ "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
215
+ <plist version="1.0"><dict>
216
+ <key>Label</key><string>ai.sigaa.sync</string>
217
+ <key>ProgramArguments</key>
218
+ <array>
219
+ <string>/Users/you/.local/bin/sigaa</string>
220
+ <string>sync</string>
221
+ </array>
222
+ <key>EnvironmentVariables</key>
223
+ <dict><key>SIGAA_USER</key><string>your_user</string></dict>
224
+ <key>StartInterval</key><integer>1800</integer>
225
+ </dict></plist>
226
+ ```
227
+
228
+ `uv tool install` puts `sigaa` in `~/.local/bin`; launchd needs it spelled out in
229
+ full, since it does not expand `~`. Run `which sigaa` to confirm yours.
230
+
231
+ **Linux (cron)**: `*/30 * * * * SIGAA_USER=you $HOME/.local/bin/sigaa sync`
232
+ (password from keyring, or add `SIGAA_PASS`).
233
+
234
+ ## Architecture
235
+
236
+ ```
237
+ sigaa/
238
+ config.py endpoints, JSF constants, slot-time table, creds resolution
239
+ http.py session: cookie jar, ViewState, re-login + retry
240
+ auth.py login flow
241
+ client.py SigaaClient -> domain models
242
+ sipac.py unauthenticated public SIPAC process client + JSON contract
243
+ models.py Student, Turma, NewsItem, Schedule, CurriculumStatus
244
+ parsers/ portal, news, schedule, curriculum, transcript, SIPAC process
245
+ store/ SQLite db + repository (dedup, queries)
246
+ services/ sync (fetch -> diff -> persist)
247
+ cli.py command line
248
+ mcp_server.py agent tools
249
+ ```
250
+
251
+ Adding a feature (materials, attendance) = a parser + client method and a
252
+ CLI/MCP surface, plus store columns when it is persisted. HTML changes touch
253
+ only `parsers/`. Implemented so far: classes, news (+bodies), grades, deadlines,
254
+ curriculum progress, official CRA, academic documents, and ICS export.
255
+ Public SIPAC administrative-process consultation is also available live.
256
+
257
+ `exporters/` turns store data into interchange formats (currently `ics`).
258
+
259
+ ## Schedule decoding
260
+
261
+ `6M2345` → Fri (day 6) morning slots 2–5. Days 2=Mon..7=Sat, shift M/T/N.
262
+ Clock times in `config.SLOT_TIMES_UNCONFIRMED` are an **unconfirmed** default —
263
+ verify against a turma's "Plano de Curso" before using them for calendar export.
264
+
265
+ ## Tests
266
+
267
+ ```bash
268
+ pytest # offline: parsers run on fixtures, store on in-memory sqlite
269
+ ```
270
+
271
+ ## Contributing
272
+
273
+ Interested in hacking on sigaa-tools? See [CONTRIBUTING.md](CONTRIBUTING.md) for setup and release instructions.
274
+
275
+ ## Security
276
+
277
+ Single-user, local-first tool. `config.py` reads credentials from keyring first
278
+ and environment variables second. The SQLite db is local and may contain student
279
+ data copied from SIGAA. `.gitignore` excludes `*.db`, `.env`, and live HTML
280
+ dumps, but review generated files before sharing logs, issues, or screenshots.
281
+ The SIPAC process feature is read-only and public; it does not send stored
282
+ credentials, but its responses can contain names and identifiers already shown
283
+ by the public portal, so handle exported JSON responsibly.
284
+
285
+ ## License
286
+
287
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,59 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "sigaa-tools"
7
+ dynamic = ["version"]
8
+ description = "User-friendly SIGAA and public SIPAC UFPB client (CLI + MCP)"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.11"
12
+ authors = [
13
+ { name = "Puca Vaz" },
14
+ ]
15
+ keywords = ["sigaa", "sipac", "ufpb", "mcp", "cli", "academic"]
16
+ classifiers = [
17
+ "Environment :: Console",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ ]
21
+ dependencies = [
22
+ "httpx>=0.27",
23
+ "beautifulsoup4>=4.12",
24
+ "lxml>=5.0",
25
+ "keyring>=24",
26
+ "pypdf>=5.0,<7",
27
+ ]
28
+
29
+ [project.urls]
30
+ Homepage = "https://github.com/PucaVaz/sigaa-tools"
31
+ Repository = "https://github.com/PucaVaz/sigaa-tools"
32
+ Issues = "https://github.com/PucaVaz/sigaa-tools/issues"
33
+
34
+ [project.optional-dependencies]
35
+ mcp = ["mcp>=1.27,<2"]
36
+ dev = ["pytest>=8.0", "ruff>=0.6.0"]
37
+
38
+ [project.scripts]
39
+ sigaa = "sigaa.cli:main"
40
+ sigaa-mcp = "sigaa.mcp_server:main"
41
+
42
+ [tool.setuptools.packages.find]
43
+ include = ["sigaa*"]
44
+
45
+ [tool.setuptools.dynamic]
46
+ version = {attr = "sigaa.__version__"}
47
+
48
+ [tool.pytest.ini_options]
49
+ testpaths = ["tests"]
50
+
51
+ [tool.ruff]
52
+ line-length = 100
53
+ target-version = "py311"
54
+
55
+ # Pinned explicitly rather than inherited. ruff's default selection widened in
56
+ # 0.16, which turned a green CI red on an unchanged tree; this is the set the
57
+ # codebase was actually written against.
58
+ [tool.ruff.lint]
59
+ select = ["E4", "E7", "E9", "F"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+