vigia-eew 0.3.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.
- vigia_eew-0.3.1/.gitignore +32 -0
- vigia_eew-0.3.1/ARCHITECTURE.md +187 -0
- vigia_eew-0.3.1/LICENSE +674 -0
- vigia_eew-0.3.1/PKG-INFO +935 -0
- vigia_eew-0.3.1/README.md +223 -0
- vigia_eew-0.3.1/docs/API-SPEC.md +286 -0
- vigia_eew-0.3.1/docs/DATA-MODEL.md +282 -0
- vigia_eew-0.3.1/docs/IMPLEMENTATION-PLAN.md +297 -0
- vigia_eew-0.3.1/docs/PRD.md +195 -0
- vigia_eew-0.3.1/docs/TECHNICAL-DESIGN.md +511 -0
- vigia_eew-0.3.1/docs/plans/2026-07-05-country-filter-design.md +62 -0
- vigia_eew-0.3.1/pyproject.toml +91 -0
- vigia_eew-0.3.1/src/vigia_eew/__init__.py +16 -0
- vigia_eew-0.3.1/src/vigia_eew/agent_state.py +42 -0
- vigia_eew-0.3.1/src/vigia_eew/app.py +426 -0
- vigia_eew-0.3.1/src/vigia_eew/assets/countries.geojson +1 -0
- vigia_eew-0.3.1/src/vigia_eew/assets/critical.wav +0 -0
- vigia_eew-0.3.1/src/vigia_eew/assets/info.wav +0 -0
- vigia_eew-0.3.1/src/vigia_eew/assets/tray_icon.png +0 -0
- vigia_eew-0.3.1/src/vigia_eew/assets/warning.wav +0 -0
- vigia_eew-0.3.1/src/vigia_eew/autostart/__init__.py +82 -0
- vigia_eew-0.3.1/src/vigia_eew/autostart/linux_systemd.py +98 -0
- vigia_eew-0.3.1/src/vigia_eew/autostart/macos_launchagent.py +84 -0
- vigia_eew-0.3.1/src/vigia_eew/autostart/windows_task.py +82 -0
- vigia_eew-0.3.1/src/vigia_eew/backoff.py +49 -0
- vigia_eew-0.3.1/src/vigia_eew/cli.py +115 -0
- vigia_eew-0.3.1/src/vigia_eew/config.py +246 -0
- vigia_eew-0.3.1/src/vigia_eew/config.toml.example +71 -0
- vigia_eew-0.3.1/src/vigia_eew/geo.py +35 -0
- vigia_eew-0.3.1/src/vigia_eew/geocode.py +103 -0
- vigia_eew-0.3.1/src/vigia_eew/geoloc.py +68 -0
- vigia_eew-0.3.1/src/vigia_eew/i18n.py +70 -0
- vigia_eew-0.3.1/src/vigia_eew/ingest/__init__.py +33 -0
- vigia_eew-0.3.1/src/vigia_eew/ingest/rest_usgs.py +162 -0
- vigia_eew-0.3.1/src/vigia_eew/ingest/ws_emsc.py +118 -0
- vigia_eew-0.3.1/src/vigia_eew/logging_conf.py +69 -0
- vigia_eew-0.3.1/src/vigia_eew/models.py +140 -0
- vigia_eew-0.3.1/src/vigia_eew/notify/__init__.py +11 -0
- vigia_eew-0.3.1/src/vigia_eew/notify/alert_window.py +172 -0
- vigia_eew-0.3.1/src/vigia_eew/notify/controller.py +109 -0
- vigia_eew-0.3.1/src/vigia_eew/notify/presentation.py +86 -0
- vigia_eew-0.3.1/src/vigia_eew/notify/queue.py +130 -0
- vigia_eew-0.3.1/src/vigia_eew/notify/sound.py +129 -0
- vigia_eew-0.3.1/src/vigia_eew/notify/toast.py +80 -0
- vigia_eew-0.3.1/src/vigia_eew/pipeline/__init__.py +8 -0
- vigia_eew-0.3.1/src/vigia_eew/pipeline/dedup.py +72 -0
- vigia_eew-0.3.1/src/vigia_eew/pipeline/filter.py +55 -0
- vigia_eew-0.3.1/src/vigia_eew/pipeline/normalize.py +140 -0
- vigia_eew-0.3.1/src/vigia_eew/pipeline/processor.py +80 -0
- vigia_eew-0.3.1/src/vigia_eew/simulation.py +48 -0
- vigia_eew-0.3.1/src/vigia_eew/state.py +130 -0
- vigia_eew-0.3.1/src/vigia_eew/subprocess_env.py +40 -0
- vigia_eew-0.3.1/src/vigia_eew/supervisor.py +134 -0
- vigia_eew-0.3.1/src/vigia_eew/tray.py +130 -0
- vigia_eew-0.3.1/src/vigia_eew/tui.py +174 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
*.spec.bak
|
|
9
|
+
|
|
10
|
+
# Entornos
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
env/
|
|
14
|
+
|
|
15
|
+
# Herramientas
|
|
16
|
+
.pytest_cache/
|
|
17
|
+
.mypy_cache/
|
|
18
|
+
.ruff_cache/
|
|
19
|
+
.coverage
|
|
20
|
+
htmlcov/
|
|
21
|
+
|
|
22
|
+
# uv
|
|
23
|
+
uv.lock
|
|
24
|
+
|
|
25
|
+
# IDE
|
|
26
|
+
.idea/
|
|
27
|
+
.vscode/
|
|
28
|
+
|
|
29
|
+
# Runtime
|
|
30
|
+
*.log
|
|
31
|
+
state.json
|
|
32
|
+
config.toml
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# ARCHITECTURE — Vigía-eew
|
|
2
|
+
|
|
3
|
+
| Field | Value |
|
|
4
|
+
|---|---|
|
|
5
|
+
| Document | System architecture and diagrams |
|
|
6
|
+
| Version | 1.0 (draft for review) |
|
|
7
|
+
| Status | 🟡 Pending approval |
|
|
8
|
+
| Related | `docs/PRD.md`, `docs/API-SPEC.md`, `docs/TECHNICAL-DESIGN.md`, `docs/DATA-MODEL.md`, `docs/IMPLEMENTATION-PLAN.md` |
|
|
9
|
+
|
|
10
|
+
> Diagrams are in **Mermaid** (renderable text on GitHub and most Markdown viewers).
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. Overview
|
|
15
|
+
|
|
16
|
+
Vigía is a **single asyncio process per machine** with no single point of failure (RNF-02). It
|
|
17
|
+
receives earthquakes via **push** (WebSocket EMSC, primary channel) and reconciles them with a
|
|
18
|
+
**low-frequency backup** (USGS polling every 60 s). A *pipeline* normalizes, filters by zone, and
|
|
19
|
+
deduplicates them; new and relevant events trigger an **undismissable desktop alert** (overlay
|
|
20
|
+
window + toast + sound). Critical state is **persisted** to survive restarts.
|
|
21
|
+
|
|
22
|
+
## 2. Components
|
|
23
|
+
|
|
24
|
+
| Component | Role | RF |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| **WSIngestor (EMSC)** | WebSocket connection, 15 s keepalive, backoff reconnection, emits raw messages | RF-01..RF-04 |
|
|
27
|
+
| **RESTReconciler (USGS)** | 60 s polling with persisted cursor; safety net | RF-05, RF-06 |
|
|
28
|
+
| **Normalizer** | Raw→`SeismicEvent`; haversine; severity | RF-07, RF-08, RF-13 |
|
|
29
|
+
| **GeoFilter** | Discards events outside radius or below minimum magnitude | RF-12 |
|
|
30
|
+
| **Deduplicator** | Inter-source heuristic; persisted ids; handles `update` | RF-09..RF-11 |
|
|
31
|
+
| **Notifier (toast)** | Native informational toast (`desktop-notifier`) | RF-14 |
|
|
32
|
+
| **AlertWindow (overlay)** | Topmost Tkinter window, with focus, undismissable | RF-15..RF-19 |
|
|
33
|
+
| **AlertQueue + bridge** | Event queue; asyncio↔Tk bridge | RF-20 |
|
|
34
|
+
| **Sound** | Audio by severity | RF-17 |
|
|
35
|
+
| **StateStore** | Atomic JSON persistence (ids, cursor) | RF-06, RF-10 |
|
|
36
|
+
| **Settings** | Loads/validates `config.toml` (pydantic) | RF-24 |
|
|
37
|
+
| **Supervisor** | Orchestrates asyncio tasks; restarts on failure | RNF-03, RNF-04 |
|
|
38
|
+
| **Autostart** | systemd / LaunchAgent / scheduled task | RF-22, RF-23 |
|
|
39
|
+
| **CLI (`vigia-eew`)** | Startup, `--simulate`, autostart | RF-21, RF-26 |
|
|
40
|
+
|
|
41
|
+
## 3. Architecture diagram — data flow
|
|
42
|
+
|
|
43
|
+
```mermaid
|
|
44
|
+
flowchart LR
|
|
45
|
+
subgraph Fuentes["External sources"]
|
|
46
|
+
EMSC["EMSC WebSocket<br/>(primary push)"]
|
|
47
|
+
USGS["USGS FDSN REST<br/>(60 s backup)"]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
subgraph Ingesta["Ingestion layer (asyncio)"]
|
|
51
|
+
WS["WSIngestor<br/>keepalive + reconnection"]
|
|
52
|
+
REST["RESTReconciler<br/>persisted cursor"]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
Q(["raw_queue<br/>(asyncio.Queue)"])
|
|
56
|
+
|
|
57
|
+
subgraph Pipeline["Pipeline"]
|
|
58
|
+
NORM["Normalizer<br/>haversine + severity"]
|
|
59
|
+
FILT["GeoFilter<br/>radius + min magnitude"]
|
|
60
|
+
DEDUP["Deduplicator<br/>heuristic + ids"]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
subgraph Estado["Persistence"]
|
|
64
|
+
STATE[("StateStore<br/>state.json<br/>ids + cursor")]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
subgraph Notif["Notification"]
|
|
68
|
+
AQ(["AlertQueue<br/>+ Tk bridge"])
|
|
69
|
+
TOAST["Notifier (toast)"]
|
|
70
|
+
WIN["AlertWindow<br/>topmost · sound · ACKNOWLEDGED"]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
CFG["Settings<br/>config.toml"]
|
|
74
|
+
|
|
75
|
+
EMSC -->|create/update| WS
|
|
76
|
+
USGS -->|GeoJSON| REST
|
|
77
|
+
WS --> Q
|
|
78
|
+
REST --> Q
|
|
79
|
+
Q --> NORM --> FILT --> DEDUP
|
|
80
|
+
DEDUP -->|new + relevant| AQ
|
|
81
|
+
AQ --> TOAST
|
|
82
|
+
AQ --> WIN
|
|
83
|
+
DEDUP <-->|alerted ids| STATE
|
|
84
|
+
REST <-->|cursor| STATE
|
|
85
|
+
CFG -.config.-> NORM
|
|
86
|
+
CFG -.config.-> FILT
|
|
87
|
+
CFG -.config.-> DEDUP
|
|
88
|
+
CFG -.config.-> WIN
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## 4. Sequence diagram — from EMSC to the alert window
|
|
92
|
+
|
|
93
|
+
```mermaid
|
|
94
|
+
sequenceDiagram
|
|
95
|
+
autonumber
|
|
96
|
+
participant EMSC as EMSC WebSocket
|
|
97
|
+
participant WS as WSIngestor
|
|
98
|
+
participant PIPE as Pipeline (normalize/filter/dedup)
|
|
99
|
+
participant ST as StateStore
|
|
100
|
+
participant UI as AlertQueue + AlertWindow
|
|
101
|
+
participant USR as User
|
|
102
|
+
|
|
103
|
+
EMSC->>WS: message {action:"create", data: Feature}
|
|
104
|
+
WS->>PIPE: raw message (raw_queue)
|
|
105
|
+
PIPE->>PIPE: normalizes (haversine, severity)
|
|
106
|
+
PIPE->>PIPE: filters (radius, min magnitude)
|
|
107
|
+
PIPE->>ST: id already alerted? duplicate?
|
|
108
|
+
ST-->>PIPE: no (new event)
|
|
109
|
+
PIPE->>ST: registers alerted id
|
|
110
|
+
PIPE->>UI: enqueues SeismicEvent
|
|
111
|
+
UI->>UI: shows topmost window + takes focus + sound
|
|
112
|
+
UI-->>USR: MAGNITUDE, place, distance, depth, local time, source
|
|
113
|
+
Note over EMSC,UI: If an {action:"update"} arrives for the same unid,<br/>the displayed event is updated WITHOUT a new alert (RF-11)
|
|
114
|
+
USR->>UI: presses "ACKNOWLEDGED"
|
|
115
|
+
UI->>ST: registers acknowledgment (audit)
|
|
116
|
+
UI->>UI: closes window and shows the next one in the queue
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## 5. State diagram — WebSocket connection
|
|
120
|
+
|
|
121
|
+
```mermaid
|
|
122
|
+
stateDiagram-v2
|
|
123
|
+
[*] --> Connecting
|
|
124
|
+
Connecting --> Connected: handshake OK
|
|
125
|
+
Connecting --> Backoff: connection error
|
|
126
|
+
|
|
127
|
+
Connected --> Ping: every ~15 s
|
|
128
|
+
Ping --> Connected: pong received
|
|
129
|
+
Ping --> Down: ping_timeout (no pong)
|
|
130
|
+
|
|
131
|
+
Connected --> Receiving: incoming message
|
|
132
|
+
Receiving --> Connected: processed
|
|
133
|
+
|
|
134
|
+
Connected --> Down: socket close/EOF
|
|
135
|
+
Down --> Backoff: schedule retry
|
|
136
|
+
|
|
137
|
+
Backoff --> Connecting: exponential wait + jitter (max backoff_max_s)
|
|
138
|
+
|
|
139
|
+
Connected --> Closing: SIGINT/SIGTERM
|
|
140
|
+
Closing --> [*]
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## 6. What happens if… (resilience scenarios)
|
|
144
|
+
|
|
145
|
+
| Scenario | Expected behavior | Mechanism / RF |
|
|
146
|
+
|---|---|---|
|
|
147
|
+
| **The WS goes down** | Close/ping_timeout is detected → `Down` state → exponential `Backoff` with jitter → perpetual reconnection. The process **does not die**. | RF-03, RNF-03; §5 |
|
|
148
|
+
| **The WS silently stops receiving** | The **keepalive (15 s ping)** detects the loss via `ping_timeout` and forces reconnection. | RF-02 |
|
|
149
|
+
| **REST fails (429/5xx/timeout)** | `Retry-After` is honored (429); the cycle is skipped and retried after 60 s; the **cursor is kept**; no abort. | RF-05; Technical Design §8 |
|
|
150
|
+
| **An `update` arrives** | Same `unid` already seen → the displayed/queued event is **updated** (e.g. magnitude) **without** triggering a new alert. | RF-11, CU-3 |
|
|
151
|
+
| **Two sources report the same earthquake** | The heuristic (≤100 km, ≤90 s, ≤0.5 mag) recognizes it as a duplicate → **a single** alert. | RF-09, CU-4 |
|
|
152
|
+
| **The agent restarts with pending alerts** | `StateStore` remembers `alerted_ids` → already-acknowledged ones **are not re-alerted**; `usgs_cursor` avoids reprocessing history. | RF-06, RF-10, CU-10 |
|
|
153
|
+
| **Invalid JSON / unexpected schema** | pydantic validation discards the item and logs it; the flow continues. | RNF-03 |
|
|
154
|
+
| **Total network loss** | Both ingestion paths keep retrying; once the network returns, USGS **reconciles** what was missed during the outage. | RF-05, OBJ-3 |
|
|
155
|
+
| **OS "do not disturb"** | The toast may be silenced, but the **topmost, focused overlay window** guarantees the alert. | RF-15, RF-16, RNF-05 |
|
|
156
|
+
| **UI fails** | Isolated from the pipeline (decoupled bridge); ingestion keeps running; showing the alert is retried. | ADR-006 |
|
|
157
|
+
|
|
158
|
+
## 7. Deployment
|
|
159
|
+
|
|
160
|
+
Each machine runs its own agent (no SPOF). OS-specific autostart (systemd `--user`, LaunchAgent,
|
|
161
|
+
scheduled task) keeps the process alive after login.
|
|
162
|
+
|
|
163
|
+
```mermaid
|
|
164
|
+
flowchart TB
|
|
165
|
+
subgraph PCs["N independent machines (no single point of failure)"]
|
|
166
|
+
A["Vigía Agent<br/>(Linux · systemd --user)"]
|
|
167
|
+
B["Vigía Agent<br/>(Windows · scheduled task)"]
|
|
168
|
+
C["Vigía Agent<br/>(macOS · LaunchAgent)"]
|
|
169
|
+
end
|
|
170
|
+
EMSC["EMSC WS"] --> A & B & C
|
|
171
|
+
USGS["USGS FDSN"] --> A & B & C
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## 8. Future evolution — central relay (not v1)
|
|
175
|
+
|
|
176
|
+
ADR-008 documents the migration to a **FastAPI relay** that consumes EMSC/USGS once and does
|
|
177
|
+
*fan-out* over WebSocket to many Vigía clients, **reusing the internal contract** (`SeismicEvent`)
|
|
178
|
+
as the payload so as not to break the data model.
|
|
179
|
+
|
|
180
|
+
```mermaid
|
|
181
|
+
flowchart LR
|
|
182
|
+
EMSC["EMSC WS"] --> RELAY["FastAPI Relay<br/>(central dedup)"]
|
|
183
|
+
USGS["USGS FDSN"] --> RELAY
|
|
184
|
+
RELAY -->|fan-out WS<br/>SeismicEvent| C1["Vigía client 1"]
|
|
185
|
+
RELAY -->|fan-out WS| C2["Vigía client 2"]
|
|
186
|
+
RELAY -->|fan-out WS| C3["Vigía client N"]
|
|
187
|
+
```
|