Compare commits
6 Commits
27b32deba4
...
6e18324a43
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e18324a43 | ||
|
|
423909d391 | ||
|
|
d7d3c90152 | ||
|
|
6b9a228d9a | ||
|
|
a9d1e135cb | ||
|
|
e4052374db |
183
CLAUDE.md
@@ -17,51 +17,58 @@ spr/
|
|||||||
├── schema.json # Source of truth for models
|
├── schema.json # Source of truth for models
|
||||||
├── build.py # Build tool
|
├── build.py # Build tool
|
||||||
│
|
│
|
||||||
|
├── soleprint/ # Core framework (all systems inside)
|
||||||
|
│ ├── main.py # Hub entry point
|
||||||
|
│ ├── run.py # Bare-metal dev server
|
||||||
|
│ ├── index.html
|
||||||
|
│ ├── requirements.txt
|
||||||
|
│ ├── Dockerfile
|
||||||
|
│ ├── dataloader/
|
||||||
|
│ │
|
||||||
|
│ ├── artery/ # Connectors
|
||||||
|
│ │ ├── veins/ # Real API connectors (jira, slack, google)
|
||||||
|
│ │ ├── shunts/ # Fake connectors for testing
|
||||||
|
│ │ ├── pulses/ # Composed: Vein + Room + Depot
|
||||||
|
│ │ └── depots/
|
||||||
|
│ │
|
||||||
|
│ ├── atlas/ # Documentation
|
||||||
|
│ │ ├── books/ # Documentation (soleprint-only)
|
||||||
|
│ │ ├── templates/
|
||||||
|
│ │ ├── depots/
|
||||||
|
│ │ └── static/
|
||||||
|
│ │
|
||||||
|
│ └── station/ # Tools & execution
|
||||||
|
│ ├── tools/ # modelgen, datagen, tester, sbwrapper
|
||||||
|
│ ├── monitors/ # databrowse
|
||||||
|
│ └── desks/
|
||||||
|
│
|
||||||
├── cfg/ # Room configurations
|
├── cfg/ # Room configurations
|
||||||
│ ├── standalone/ # Base soleprint config
|
│ ├── standalone/ # Base soleprint config
|
||||||
│ │ ├── config.json # Framework branding/terminology
|
│ │ ├── config.json
|
||||||
│ │ └── data/ # Data files (veins.json, shunts.json, etc.)
|
│ │ ├── data/
|
||||||
│ └── amar/ # AMAR room config
|
│ │ └── soleprint/
|
||||||
│ ├── config.json # Can rebrand (e.g., "pawprint")
|
│ │
|
||||||
│ ├── data/ # Room-specific data files
|
│ └── amar/ # Amar room config
|
||||||
│ ├── .env.example
|
│ ├── config.json
|
||||||
│ ├── docker-compose.yml
|
│ ├── data/
|
||||||
│ ├── soleprint/ # Soleprint Docker config for this room
|
│ ├── artery/ # Amar-specific (merged into output)
|
||||||
│ ├── databrowse/depot/
|
│ │ └── shunts/amar/
|
||||||
│ ├── tester/tests/
|
│ ├── atlas/ # Amar-specific books
|
||||||
|
│ │ └── books/
|
||||||
|
│ ├── station/ # Amar-specific tools config
|
||||||
|
│ │ └── tools/datagen/
|
||||||
|
│ ├── link/ # Bridge to managed app
|
||||||
|
│ ├── soleprint/ # Soleprint docker config
|
||||||
|
│ ├── databrowse/
|
||||||
|
│ ├── tester/
|
||||||
│ ├── monitors/
|
│ ├── monitors/
|
||||||
│ └── models/
|
│ └── models/
|
||||||
│
|
│
|
||||||
├── ctrl/ # Build/run scripts
|
├── ctrl/ # Build/run scripts
|
||||||
│ ├── build.sh # ./build.sh [room]
|
|
||||||
│ ├── start.sh # ./start.sh [room] [-d]
|
|
||||||
│ ├── stop.sh # ./stop.sh [room]
|
|
||||||
│ └── logs.sh # ./logs.sh [room]
|
|
||||||
│
|
|
||||||
├── artery/ # Vital connections
|
|
||||||
│ ├── veins/ # Stateless API connectors (jira, slack, google)
|
|
||||||
│ ├── shunts/ # Fake connectors for testing
|
|
||||||
│ ├── pulses/ # Composed: Vein + Room + Depot
|
|
||||||
│ └── plexus/ # Full apps: backend + frontend + DB
|
|
||||||
│
|
|
||||||
├── atlas/ # Documentation system
|
|
||||||
│ └── books/ # Soleprint docs (external via depots)
|
|
||||||
│
|
|
||||||
├── station/ # Tools & execution
|
|
||||||
│ ├── tools/ # modelgen, datagen, tester, sbwrapper
|
|
||||||
│ └── monitors/ # databrowse
|
|
||||||
│
|
|
||||||
├── soleprint/ # Core entry points (versioned)
|
|
||||||
│ ├── main.py
|
|
||||||
│ ├── run.py
|
|
||||||
│ ├── index.html
|
|
||||||
│ ├── requirements.txt
|
|
||||||
│ ├── Dockerfile
|
|
||||||
│ └── dataloader/
|
|
||||||
│
|
│
|
||||||
└── gen/ # Built instances (gitignored)
|
└── gen/ # Built instances (gitignored)
|
||||||
├── standalone/ # python build.py dev
|
├── standalone/
|
||||||
└── amar/ # python build.py dev --cfg amar
|
└── amar/
|
||||||
```
|
```
|
||||||
|
|
||||||
## The Four Systems
|
## The Four Systems
|
||||||
@@ -87,63 +94,47 @@ Vein ──────► Pulse ──────► Plexus
|
|||||||
Shunt ─── Fake connector for testing
|
Shunt ─── Fake connector for testing
|
||||||
```
|
```
|
||||||
|
|
||||||
| Type | State | Frontend | Deploy |
|
|
||||||
|------|-------|----------|--------|
|
|
||||||
| Vein | None (or OAuth) | Optional test UI | With soleprint |
|
|
||||||
| Shunt | Configurable responses | Config UI | With soleprint |
|
|
||||||
| Pulse | Vein + config | Uses vein's | With soleprint |
|
|
||||||
| Plexus | Full app state | Required | Self-contained |
|
|
||||||
|
|
||||||
## Room Configuration
|
## Room Configuration
|
||||||
|
|
||||||
Each room in `cfg/` has:
|
Each room in `cfg/` has:
|
||||||
- `config.json` - Framework branding/terminology (can rebrand soleprint)
|
- `config.json` - Framework branding/terminology
|
||||||
- `data/` - Data files (veins.json, shunts.json, depots.json, etc.)
|
- `data/` - Data files (veins.json, shunts.json, etc.)
|
||||||
- Room-specific: databrowse depot, tester tests, monitors, models
|
|
||||||
|
|
||||||
Managed rooms (like amar) also have:
|
Room-specific system configs (merged into output):
|
||||||
- `docker-compose.yml` - Room's own services
|
- `artery/` - Room-specific shunts, pulses
|
||||||
- `soleprint/` - Soleprint Docker config for this room
|
- `atlas/` - Room-specific books
|
||||||
- `.env.example` - Environment template
|
- `station/` - Room-specific tool configs (datagen, tester tests, etc.)
|
||||||
|
|
||||||
## Build & Run
|
## Build & Run
|
||||||
|
|
||||||
### Commands
|
|
||||||
```bash
|
```bash
|
||||||
# Build
|
# Build
|
||||||
python build.py dev # -> gen/standalone/
|
python build.py # -> gen/standalone/
|
||||||
python build.py dev --cfg amar # -> gen/amar/
|
python build.py --cfg amar # -> gen/amar/
|
||||||
python build.py dev --all # -> both
|
python build.py --all # -> all rooms
|
||||||
|
|
||||||
|
# Run bare-metal
|
||||||
|
cd gen/standalone && python run.py
|
||||||
|
|
||||||
# Using ctrl scripts
|
# Using ctrl scripts
|
||||||
./ctrl/build.sh # Build standalone
|
./ctrl/build.sh [room]
|
||||||
./ctrl/build.sh amar # Build amar
|
./ctrl/start.sh [room] [-d]
|
||||||
./ctrl/build.sh --all # Build all
|
./ctrl/stop.sh [room]
|
||||||
|
|
||||||
./ctrl/start.sh # Start standalone
|
|
||||||
./ctrl/start.sh amar # Start amar
|
|
||||||
./ctrl/start.sh amar -d # Detached
|
|
||||||
./ctrl/stop.sh amar # Stop
|
|
||||||
./ctrl/logs.sh amar # View logs
|
|
||||||
|
|
||||||
# Bare-metal dev
|
|
||||||
cd gen/standalone && .venv/bin/python run.py
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Adding a New Managed Room
|
## Adding a New Room
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Create room config
|
mkdir -p cfg/newroom/data
|
||||||
mkdir -p cfg/clientx/data
|
cp cfg/standalone/config.json cfg/newroom/
|
||||||
|
cp -r cfg/standalone/data/* cfg/newroom/data/
|
||||||
|
|
||||||
# 2. Copy base config
|
# Add room-specific configs as needed:
|
||||||
cp cfg/standalone/config.json cfg/clientx/
|
# cfg/newroom/artery/shunts/...
|
||||||
cp -r cfg/standalone/data/* cfg/clientx/data/
|
# cfg/newroom/atlas/books/...
|
||||||
|
# cfg/newroom/station/tools/...
|
||||||
|
|
||||||
# 3. Customize as needed (shunts, depots, branding)
|
python build.py --cfg newroom
|
||||||
|
|
||||||
# 4. Build and run
|
|
||||||
python build.py dev --cfg clientx
|
|
||||||
./ctrl/start.sh clientx
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Ports
|
## Ports
|
||||||
@@ -151,45 +142,25 @@ python build.py dev --cfg clientx
|
|||||||
| Service | Port |
|
| Service | Port |
|
||||||
|---------|------|
|
|---------|------|
|
||||||
| Soleprint | 12000 |
|
| Soleprint | 12000 |
|
||||||
| Amar Backend | 8000 |
|
|
||||||
| Amar Frontend | 3000 |
|
|
||||||
|
|
||||||
## Tools
|
## Tools
|
||||||
|
|
||||||
| Tool | Location | Purpose |
|
| Tool | Purpose |
|
||||||
|------|----------|---------|
|
|------|---------|
|
||||||
| modelgen | station/tools/modelgen | Model generation |
|
| modelgen | Generate models from config |
|
||||||
| datagen | station/tools/datagen | Test data generation |
|
| datagen | Generate test data (uses faker) |
|
||||||
| tester | station/tools/tester | BDD/playwright test runner |
|
| tester | BDD/playwright test runner |
|
||||||
| sbwrapper | station/tools/sbwrapper | Sidebar wrapper UI |
|
| graphgen | Generate navigable model graphs |
|
||||||
| databrowse | station/monitors/databrowse | SQL data browser |
|
| databrowse | SQL data browser |
|
||||||
|
|
||||||
## Integration with ppl/ (Infrastructure)
|
|
||||||
|
|
||||||
```
|
|
||||||
wdir/
|
|
||||||
├── spr/ # This repo (soleprint)
|
|
||||||
├── ppl/ # Pipelines & infrastructure
|
|
||||||
│ ├── pipelines/spr-standalone/ # CI/CD for standalone
|
|
||||||
│ ├── pipelines/spr-managed/ # Manual deploy for rooms
|
|
||||||
│ └── gateway/ # Nginx configs
|
|
||||||
└── ama/ # Amar source code
|
|
||||||
```
|
|
||||||
|
|
||||||
### Pipeline (standalone only)
|
|
||||||
- git push -> woodpecker -> build gen/standalone/ -> docker push -> deploy
|
|
||||||
- Managed rooms deploy manually (no pipeline for client code)
|
|
||||||
|
|
||||||
## External Paths
|
## External Paths
|
||||||
|
|
||||||
| What | Path |
|
| What | Path |
|
||||||
|------|------|
|
|------|------|
|
||||||
| Amar Backend | /home/mariano/wdir/ama/amar_django_back |
|
|
||||||
| Amar Frontend | /home/mariano/wdir/ama/amar_frontend |
|
|
||||||
| Pipelines | /home/mariano/wdir/ppl |
|
| Pipelines | /home/mariano/wdir/ppl |
|
||||||
|
|
||||||
## Files Ignored
|
## Files Ignored
|
||||||
|
|
||||||
- `gen/` - Regenerate with `python build.py dev`
|
- `gen/` - Regenerate with `python build.py`
|
||||||
- `fails/`, `def/` - Drafts
|
- `fails/`, `def/` - Drafts
|
||||||
- `__pycache__/`, `.venv/`
|
- `__pycache__/`, `.venv/`
|
||||||
|
|||||||
155
README.md
@@ -1,150 +1,51 @@
|
|||||||
# Soleprint
|
# Soleprint
|
||||||
|
|
||||||
> Cada paso deja huella / Each step leaves a mark
|
|
||||||
|
|
||||||
Development workflow platform. Run, test, and document everything in one place.
|
Development workflow platform. Run, test, and document everything in one place.
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build standalone
|
# Build and run standalone
|
||||||
python build.py dev
|
python build.py
|
||||||
cd gen/standalone && .venv/bin/python run.py
|
cd gen/standalone && docker compose up
|
||||||
|
|
||||||
# Build with room config
|
|
||||||
python build.py dev --cfg amar
|
|
||||||
cd gen/amar && .venv/bin/python run.py
|
|
||||||
|
|
||||||
# Visit http://localhost:12000
|
# Visit http://localhost:12000
|
||||||
|
|
||||||
|
# Build and run with room config
|
||||||
|
python build.py --cfg <room>
|
||||||
|
cd gen/<room>/soleprint && docker compose up
|
||||||
```
|
```
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build
|
# Build
|
||||||
python build.py dev # → gen/standalone/
|
python build.py # -> gen/standalone/
|
||||||
python build.py dev --cfg amar # → gen/amar/
|
python build.py --cfg <room> # -> gen/<room>/
|
||||||
python build.py dev --all # → both
|
python build.py --all # -> all rooms
|
||||||
|
|
||||||
# ctrl scripts
|
# Using ctrl scripts
|
||||||
./ctrl/build.sh # Build standalone
|
./ctrl/build.sh # Build standalone
|
||||||
./ctrl/build.sh amar # Build amar
|
./ctrl/build.sh <room> # Build room
|
||||||
./ctrl/build.sh --all # Build all
|
./ctrl/start.sh # Start standalone
|
||||||
|
./ctrl/start.sh <room> # Start room
|
||||||
|
./ctrl/stop.sh <room> # Stop
|
||||||
|
./ctrl/logs.sh <room> # View logs
|
||||||
|
|
||||||
./ctrl/start.sh # Start standalone (Docker)
|
# Bare-metal dev (without Docker)
|
||||||
./ctrl/start.sh amar # Start amar
|
cd gen/standalone && python run.py
|
||||||
./ctrl/start.sh -d # Detached
|
|
||||||
./ctrl/stop.sh # Stop
|
|
||||||
./ctrl/logs.sh # View logs
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Adding a New Managed Room
|
## Adding a New Room
|
||||||
|
|
||||||
1. Create room config directory:
|
|
||||||
```bash
|
```bash
|
||||||
mkdir -p cfg/clientx
|
# 1. Create room config
|
||||||
|
mkdir -p cfg/myroom/data
|
||||||
|
cp cfg/standalone/config.json cfg/myroom/
|
||||||
|
cp -r cfg/standalone/data/* cfg/myroom/data/
|
||||||
|
|
||||||
|
# 2. Build and run
|
||||||
|
python build.py --cfg myroom
|
||||||
|
cd gen/myroom/soleprint && docker compose up
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Add required files:
|
Or use the generation UI: `python -m http.server 8080` in `spr/` and visit `/generate.html`.
|
||||||
```
|
|
||||||
cfg/clientx/
|
|
||||||
├── .env.example # Environment template
|
|
||||||
├── docker-compose.yml # Room services (optional)
|
|
||||||
├── databrowse/depot/ # Database schemas (optional)
|
|
||||||
├── tester/tests/ # Room-specific tests (optional)
|
|
||||||
├── monitors/ # Room-specific monitors (optional)
|
|
||||||
├── models/ # Room-specific models (optional)
|
|
||||||
└── data/ # Room-specific data files (optional)
|
|
||||||
├── depots.json # Doc depots for Atlas
|
|
||||||
└── shunts.json # Mock connectors
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Build and run:
|
|
||||||
```bash
|
|
||||||
python build.py dev --cfg clientx
|
|
||||||
./ctrl/start.sh clientx
|
|
||||||
```
|
|
||||||
|
|
||||||
## Systems
|
|
||||||
|
|
||||||
| | System | What it does |
|
|
||||||
|---|--------|--------------|
|
|
||||||
| 👣 | **Soleprint** | Core coordinator, routing, landing page |
|
|
||||||
| 💉 | **Artery** | Connectors to external services (Jira, Slack, Google) |
|
|
||||||
| 🗺️ | **Atlas** | Actionable documentation (BDD, Gherkin, specs) |
|
|
||||||
| 🎛️ | **Station** | Tools (modelgen, tester) and monitors (databrowse) |
|
|
||||||
|
|
||||||
## Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
spr/
|
|
||||||
├── build.py # Build tool
|
|
||||||
├── cfg/ # Room configurations
|
|
||||||
│ ├── soleprint.config.json
|
|
||||||
│ └── amar/ # AMAR room config
|
|
||||||
├── ctrl/ # Docker scripts
|
|
||||||
│
|
|
||||||
├── artery/ # Connectors
|
|
||||||
│ ├── veins/ # Jira, Slack, Google
|
|
||||||
│ ├── shunts/ # Fake connectors for testing
|
|
||||||
│ └── plexus/ # Full apps (backend + frontend)
|
|
||||||
│
|
|
||||||
├── atlas/ # Documentation
|
|
||||||
│ └── books/ # Soleprint docs only
|
|
||||||
│
|
|
||||||
├── station/ # Tools & monitors
|
|
||||||
│ ├── tools/ # modelgen, tester, datagen
|
|
||||||
│ └── monitors/ # databrowse
|
|
||||||
│
|
|
||||||
├── soleprint/ # Core (versioned)
|
|
||||||
│
|
|
||||||
├── gen/ # Built instances (gitignored)
|
|
||||||
│ ├── standalone/ # Base soleprint
|
|
||||||
│ └── amar/ # With amar config
|
|
||||||
│
|
|
||||||
└── mainroom/ # Orchestration with managed room
|
|
||||||
├── amar -> cfg/amar
|
|
||||||
├── soleprint/ # Soleprint Docker config
|
|
||||||
└── ctrl/ # start, stop, deploy scripts
|
|
||||||
```
|
|
||||||
|
|
||||||
## Artery Hierarchy
|
|
||||||
|
|
||||||
```
|
|
||||||
Vein ──► Pulse ──► Plexus
|
|
||||||
│ │ │
|
|
||||||
│ │ └── Full app (backend + frontend + DB)
|
|
||||||
│ └── Composed (Vein + Room + Depot)
|
|
||||||
└── Stateless API connector
|
|
||||||
|
|
||||||
Shunt ── Fake connector for testing
|
|
||||||
```
|
|
||||||
|
|
||||||
## Ports
|
|
||||||
|
|
||||||
| Service | Port |
|
|
||||||
|---------|------|
|
|
||||||
| Soleprint | 12000 |
|
|
||||||
| Amar Backend | 8000 |
|
|
||||||
| Amar Frontend | 3000 |
|
|
||||||
|
|
||||||
## Tools
|
|
||||||
|
|
||||||
| Tool | Purpose |
|
|
||||||
|------|---------|
|
|
||||||
| modelgen | Generate models from config |
|
|
||||||
| tester | BDD/playwright test runner |
|
|
||||||
| datagen | Test data generation |
|
|
||||||
| databrowse | SQL data browser |
|
|
||||||
|
|
||||||
## Veins
|
|
||||||
|
|
||||||
| Vein | Auth |
|
|
||||||
|------|------|
|
|
||||||
| jira | Token |
|
|
||||||
| slack | Token |
|
|
||||||
| google | OAuth2 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Built for small teams who need to move fast without breaking things.*
|
|
||||||
|
|||||||
@@ -1,184 +0,0 @@
|
|||||||
digraph BackendArchitecture {
|
|
||||||
// Graph settings
|
|
||||||
rankdir=TB
|
|
||||||
compound=true
|
|
||||||
splines=ortho
|
|
||||||
node [shape=box, style="rounded,filled", fontname="Helvetica", fontsize=11]
|
|
||||||
edge [fontname="Helvetica", fontsize=9]
|
|
||||||
|
|
||||||
// Color scheme
|
|
||||||
// Django Core: #092E20 (dark green)
|
|
||||||
// Mascotas: #4A90D9 (blue)
|
|
||||||
// Productos: #50C878 (green)
|
|
||||||
// Solicitudes: #FF6B6B (coral)
|
|
||||||
// Common: #9B59B6 (purple)
|
|
||||||
// External: #F39C12 (orange)
|
|
||||||
// Payments: #E74C3C (red)
|
|
||||||
|
|
||||||
label="AMAR Mascotas - Backend Architecture (Django)\n\n"
|
|
||||||
labelloc="t"
|
|
||||||
fontsize=16
|
|
||||||
fontname="Helvetica-Bold"
|
|
||||||
|
|
||||||
// Django Core Cluster
|
|
||||||
subgraph cluster_django_core {
|
|
||||||
label="Django Core"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#E8F5E9"
|
|
||||||
color="#2E7D32"
|
|
||||||
|
|
||||||
auth_user [label="auth.User\n(Django Auth)", fillcolor="#C8E6C9"]
|
|
||||||
django_admin [label="Django Admin\nInterface", fillcolor="#C8E6C9"]
|
|
||||||
drf [label="Django REST\nFramework", fillcolor="#C8E6C9"]
|
|
||||||
jwt_auth [label="JWT Authentication\n(SimpleJWT)", fillcolor="#C8E6C9"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mascotas App Cluster
|
|
||||||
subgraph cluster_mascotas {
|
|
||||||
label="mascotas (Pets & Veterinarians)"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#E3F2FD"
|
|
||||||
color="#1565C0"
|
|
||||||
|
|
||||||
petowner [label="PetOwner\n(Cliente/Tutor)", fillcolor="#BBDEFB"]
|
|
||||||
pet [label="Pet\n(Mascota)", fillcolor="#BBDEFB"]
|
|
||||||
veterinarian [label="Veterinarian\n(Profesional)", fillcolor="#BBDEFB"]
|
|
||||||
vetvisit [label="VetVisit\n(Consulta)", fillcolor="#BBDEFB"]
|
|
||||||
vetvisitreport [label="VetVisitReport\n(Informe Clinico)", fillcolor="#BBDEFB"]
|
|
||||||
availability [label="Availability /\nUnavailability", fillcolor="#BBDEFB"]
|
|
||||||
pet_health [label="PetVaccine /\nPetStudy", fillcolor="#BBDEFB"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Productos App Cluster
|
|
||||||
subgraph cluster_productos {
|
|
||||||
label="productos (Services & Pricing)"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#E8F5E9"
|
|
||||||
color="#2E7D32"
|
|
||||||
|
|
||||||
service [label="Service\n(Servicio)", fillcolor="#C8E6C9"]
|
|
||||||
category [label="Category / Group\n(Categorias)", fillcolor="#C8E6C9"]
|
|
||||||
prices [label="Prices\n(Precios)", fillcolor="#C8E6C9"]
|
|
||||||
discounts [label="Discounts\n(Descuentos)", fillcolor="#C8E6C9"]
|
|
||||||
cart [label="Cart / CartItem\n(Carrito)", fillcolor="#C8E6C9"]
|
|
||||||
combo [label="ServiceCombo\n(Paquetes)", fillcolor="#C8E6C9"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Solicitudes App Cluster
|
|
||||||
subgraph cluster_solicitudes {
|
|
||||||
label="solicitudes (Service Requests)"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#FFEBEE"
|
|
||||||
color="#C62828"
|
|
||||||
|
|
||||||
servicerequest [label="ServiceRequest\n(Solicitud)", fillcolor="#FFCDD2"]
|
|
||||||
statehistory [label="StateHistory\n(Historial)", fillcolor="#FFCDD2"]
|
|
||||||
vetasked [label="VeterinarianAsked\n(Vet Consultado)", fillcolor="#FFCDD2"]
|
|
||||||
reminders [label="Reminders\n(Recordatorios)", fillcolor="#FFCDD2"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Common App Cluster
|
|
||||||
subgraph cluster_common {
|
|
||||||
label="common (Shared Models)"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#F3E5F5"
|
|
||||||
color="#7B1FA2"
|
|
||||||
|
|
||||||
campaign [label="Campaign\n(Marketing)", fillcolor="#E1BEE7"]
|
|
||||||
tag [label="Tag\n(Etiquetas)", fillcolor="#E1BEE7"]
|
|
||||||
specialty [label="Specialty\n(Especialidades)", fillcolor="#E1BEE7"]
|
|
||||||
medication [label="Medication\n(Medicamentos)", fillcolor="#E1BEE7"]
|
|
||||||
breed [label="PetBreed / Vaccine\n/ Study", fillcolor="#E1BEE7"]
|
|
||||||
neighborhood [label="Neighborhood /\nProvince / Locality", fillcolor="#E1BEE7"]
|
|
||||||
turnfee [label="IndividualTurnFee\nGroup", fillcolor="#E1BEE7"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Payments App Cluster
|
|
||||||
subgraph cluster_payments {
|
|
||||||
label="payments (Payment Processing)"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#FCE4EC"
|
|
||||||
color="#AD1457"
|
|
||||||
|
|
||||||
mercadopago [label="MercadoPago\nAccount", fillcolor="#F8BBD9"]
|
|
||||||
mpnotification [label="MP Notification\n(Webhooks)", fillcolor="#F8BBD9"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// External Integrations Cluster
|
|
||||||
subgraph cluster_external {
|
|
||||||
label="External Integrations"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#FFF3E0"
|
|
||||||
color="#E65100"
|
|
||||||
|
|
||||||
google_cal [label="Google Calendar\n(Agenda)", fillcolor="#FFE0B2"]
|
|
||||||
google_sheets [label="Google Sheets\n(Exports)", fillcolor="#FFE0B2"]
|
|
||||||
mercately [label="Mercately\n(WhatsApp)", fillcolor="#FFE0B2"]
|
|
||||||
afip [label="AFIP\n(Facturacion)", fillcolor="#FFE0B2"]
|
|
||||||
celery [label="Celery\n(Async Tasks)", fillcolor="#FFE0B2"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// AFIP Integration
|
|
||||||
subgraph cluster_afip {
|
|
||||||
label="django_afip (Invoicing)"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#FFFDE7"
|
|
||||||
color="#F9A825"
|
|
||||||
|
|
||||||
receipt [label="Receipt\n(Comprobante)", fillcolor="#FFF9C4"]
|
|
||||||
taxpayer [label="TaxPayer\n(Contribuyente)", fillcolor="#FFF9C4"]
|
|
||||||
pos [label="PointOfSales\n(Punto de Venta)", fillcolor="#FFF9C4"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Relationships - Core
|
|
||||||
auth_user -> petowner [label="1:1 optional", color="#666"]
|
|
||||||
auth_user -> veterinarian [label="1:1", color="#666"]
|
|
||||||
drf -> jwt_auth [style=dashed, color="#999"]
|
|
||||||
|
|
||||||
// Relationships - Mascotas
|
|
||||||
petowner -> pet [label="1:N", color="#1565C0"]
|
|
||||||
petowner -> servicerequest [label="1:N", color="#1565C0"]
|
|
||||||
pet -> vetvisit [label="N:M", color="#1565C0"]
|
|
||||||
pet -> pet_health [label="1:N", color="#1565C0"]
|
|
||||||
veterinarian -> vetvisit [label="1:N", color="#1565C0"]
|
|
||||||
veterinarian -> availability [label="1:N", color="#1565C0"]
|
|
||||||
vetvisit -> vetvisitreport [label="1:N", color="#1565C0"]
|
|
||||||
vetvisit -> servicerequest [label="1:1", style=dashed, color="#1565C0"]
|
|
||||||
|
|
||||||
// Relationships - Productos
|
|
||||||
service -> category [label="N:1", color="#2E7D32"]
|
|
||||||
service -> prices [label="1:N", color="#2E7D32"]
|
|
||||||
service -> discounts [label="1:N", color="#2E7D32"]
|
|
||||||
cart -> petowner [label="N:1", color="#2E7D32"]
|
|
||||||
cart -> veterinarian [label="N:1 optional", color="#2E7D32", style=dashed]
|
|
||||||
service -> cart [label="via CartItem", color="#2E7D32"]
|
|
||||||
combo -> service [label="contains", color="#2E7D32"]
|
|
||||||
|
|
||||||
// Relationships - Solicitudes
|
|
||||||
servicerequest -> cart [label="1:1", color="#C62828"]
|
|
||||||
servicerequest -> statehistory [label="1:N", color="#C62828"]
|
|
||||||
servicerequest -> vetasked [label="1:N", color="#C62828"]
|
|
||||||
vetasked -> reminders [label="1:N", color="#C62828"]
|
|
||||||
veterinarian -> vetasked [label="N:1", color="#C62828"]
|
|
||||||
|
|
||||||
// Relationships - Common
|
|
||||||
petowner -> neighborhood [label="N:1", color="#7B1FA2"]
|
|
||||||
veterinarian -> specialty [label="N:M", color="#7B1FA2"]
|
|
||||||
veterinarian -> neighborhood [label="N:M coverage", color="#7B1FA2"]
|
|
||||||
servicerequest -> campaign [label="N:1 optional", color="#7B1FA2", style=dashed]
|
|
||||||
servicerequest -> tag [label="N:M", color="#7B1FA2"]
|
|
||||||
vetvisitreport -> medication [label="references", color="#7B1FA2", style=dashed]
|
|
||||||
veterinarian -> turnfee [label="N:M", color="#7B1FA2"]
|
|
||||||
|
|
||||||
// Relationships - Payments & External
|
|
||||||
servicerequest -> mercadopago [label="payment", color="#AD1457"]
|
|
||||||
mpnotification -> servicerequest [label="confirms", color="#AD1457"]
|
|
||||||
vetvisit -> google_cal [label="sync", color="#E65100", style=dashed]
|
|
||||||
servicerequest -> mercately [label="notify", color="#E65100", style=dashed]
|
|
||||||
reminders -> celery [label="scheduled", color="#E65100", style=dashed]
|
|
||||||
|
|
||||||
// AFIP relationships
|
|
||||||
vetvisit -> receipt [label="1:1 optional", color="#F9A825", style=dashed]
|
|
||||||
receipt -> taxpayer [label="N:1", color="#F9A825"]
|
|
||||||
receipt -> pos [label="N:1", color="#F9A825"]
|
|
||||||
}
|
|
||||||
@@ -1,585 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
|
||||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<!-- Generated by graphviz version 14.0.5 (0)
|
|
||||||
-->
|
|
||||||
<!-- Title: BackendArchitecture Pages: 1 -->
|
|
||||||
<svg width="1559pt" height="935pt"
|
|
||||||
viewBox="0.00 0.00 1559.00 935.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
||||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 931.4)">
|
|
||||||
<title>BackendArchitecture</title>
|
|
||||||
<polygon fill="white" stroke="none" points="-4,4 -4,-931.4 1555,-931.4 1555,4 -4,4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="775.5" y="-908.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">AMAR Mascotas - Backend Architecture (Django)</text>
|
|
||||||
<g id="clust1" class="cluster">
|
|
||||||
<title>cluster_django_core</title>
|
|
||||||
<path fill="#e8f5e9" stroke="#2e7d32" d="M169,-496.4C169,-496.4 449,-496.4 449,-496.4 455,-496.4 461,-502.4 461,-508.4 461,-508.4 461,-726 461,-726 461,-732 455,-738 449,-738 449,-738 169,-738 169,-738 163,-738 157,-732 157,-726 157,-726 157,-508.4 157,-508.4 157,-502.4 163,-496.4 169,-496.4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="309" y="-718.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Django Core</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust2" class="cluster">
|
|
||||||
<title>cluster_mascotas</title>
|
|
||||||
<path fill="#e3f2fd" stroke="#1565c0" d="M481,-143.2C481,-143.2 733,-143.2 733,-143.2 739,-143.2 745,-149.2 745,-155.2 745,-155.2 745,-590.8 745,-590.8 745,-596.8 739,-602.8 733,-602.8 733,-602.8 481,-602.8 481,-602.8 475,-602.8 469,-596.8 469,-590.8 469,-590.8 469,-155.2 469,-155.2 469,-149.2 475,-143.2 481,-143.2"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="607" y="-583.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">mascotas (Pets & Veterinarians)</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust3" class="cluster">
|
|
||||||
<title>cluster_productos</title>
|
|
||||||
<path fill="#e8f5e9" stroke="#2e7d32" d="M765,-496.4C765,-496.4 1141,-496.4 1141,-496.4 1147,-496.4 1153,-502.4 1153,-508.4 1153,-508.4 1153,-861.2 1153,-861.2 1153,-867.2 1147,-873.2 1141,-873.2 1141,-873.2 765,-873.2 765,-873.2 759,-873.2 753,-867.2 753,-861.2 753,-861.2 753,-508.4 753,-508.4 753,-502.4 759,-496.4 765,-496.4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="953" y="-854" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">productos (Services & Pricing)</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust4" class="cluster">
|
|
||||||
<title>cluster_solicitudes</title>
|
|
||||||
<path fill="#ffebee" stroke="#c62828" d="M1084,-143.2C1084,-143.2 1290,-143.2 1290,-143.2 1296,-143.2 1302,-149.2 1302,-155.2 1302,-155.2 1302,-455.6 1302,-455.6 1302,-461.6 1296,-467.6 1290,-467.6 1290,-467.6 1084,-467.6 1084,-467.6 1078,-467.6 1072,-461.6 1072,-455.6 1072,-455.6 1072,-155.2 1072,-155.2 1072,-149.2 1078,-143.2 1084,-143.2"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1187" y="-448.4" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">solicitudes (Service Requests)</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust5" class="cluster">
|
|
||||||
<title>cluster_common</title>
|
|
||||||
<path fill="#f3e5f5" stroke="#7b1fa2" d="M20,-8C20,-8 774,-8 774,-8 780,-8 786,-14 786,-20 786,-20 786,-102.4 786,-102.4 786,-108.4 780,-114.4 774,-114.4 774,-114.4 20,-114.4 20,-114.4 14,-114.4 8,-108.4 8,-102.4 8,-102.4 8,-20 8,-20 8,-14 14,-8 20,-8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="397" y="-95.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">common (Shared Models)</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust6" class="cluster">
|
|
||||||
<title>cluster_payments</title>
|
|
||||||
<path fill="#fce4ec" stroke="#ad1457" d="M1173,-496.4C1173,-496.4 1395,-496.4 1395,-496.4 1401,-496.4 1407,-502.4 1407,-508.4 1407,-508.4 1407,-590.8 1407,-590.8 1407,-596.8 1401,-602.8 1395,-602.8 1395,-602.8 1173,-602.8 1173,-602.8 1167,-602.8 1161,-596.8 1161,-590.8 1161,-590.8 1161,-508.4 1161,-508.4 1161,-502.4 1167,-496.4 1173,-496.4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1284" y="-583.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">payments (Payment Processing)</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust7" class="cluster">
|
|
||||||
<title>cluster_external</title>
|
|
||||||
<path fill="#fff3e0" stroke="#e65100" d="M1039,-8C1039,-8 1531,-8 1531,-8 1537,-8 1543,-14 1543,-20 1543,-20 1543,-102.4 1543,-102.4 1543,-108.4 1537,-114.4 1531,-114.4 1531,-114.4 1039,-114.4 1039,-114.4 1033,-114.4 1027,-108.4 1027,-102.4 1027,-102.4 1027,-20 1027,-20 1027,-14 1033,-8 1039,-8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1285" y="-95.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">External Integrations</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust8" class="cluster">
|
|
||||||
<title>cluster_afip</title>
|
|
||||||
<path fill="#fffde7" stroke="#f9a825" d="M806,-8C806,-8 1007,-8 1007,-8 1013,-8 1019,-14 1019,-20 1019,-20 1019,-237.6 1019,-237.6 1019,-243.6 1013,-249.6 1007,-249.6 1007,-249.6 806,-249.6 806,-249.6 800,-249.6 794,-243.6 794,-237.6 794,-237.6 794,-20 794,-20 794,-14 800,-8 806,-8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="906.5" y="-230.4" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">django_afip (Invoicing)</text>
|
|
||||||
</g>
|
|
||||||
<!-- auth_user -->
|
|
||||||
<g id="node1" class="node">
|
|
||||||
<title>auth_user</title>
|
|
||||||
<path fill="#c8e6c9" stroke="black" d="M440.93,-675.6C440.93,-675.6 381.07,-675.6 381.07,-675.6 375.07,-675.6 369.07,-669.6 369.07,-663.6 369.07,-663.6 369.07,-651.6 369.07,-651.6 369.07,-645.6 375.07,-639.6 381.07,-639.6 381.07,-639.6 440.93,-639.6 440.93,-639.6 446.93,-639.6 452.93,-645.6 452.93,-651.6 452.93,-651.6 452.93,-663.6 452.93,-663.6 452.93,-669.6 446.93,-675.6 440.93,-675.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="411" y="-660.9" font-family="Helvetica,sans-Serif" font-size="11.00">auth.User</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="411" y="-647.7" font-family="Helvetica,sans-Serif" font-size="11.00">(Django Auth)</text>
|
|
||||||
</g>
|
|
||||||
<!-- petowner -->
|
|
||||||
<g id="node5" class="node">
|
|
||||||
<title>petowner</title>
|
|
||||||
<path fill="#bbdefb" stroke="black" d="M725.15,-540.4C725.15,-540.4 662.85,-540.4 662.85,-540.4 656.85,-540.4 650.85,-534.4 650.85,-528.4 650.85,-528.4 650.85,-516.4 650.85,-516.4 650.85,-510.4 656.85,-504.4 662.85,-504.4 662.85,-504.4 725.15,-504.4 725.15,-504.4 731.15,-504.4 737.15,-510.4 737.15,-516.4 737.15,-516.4 737.15,-528.4 737.15,-528.4 737.15,-534.4 731.15,-540.4 725.15,-540.4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="694" y="-525.7" font-family="Helvetica,sans-Serif" font-size="11.00">PetOwner</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="694" y="-512.5" font-family="Helvetica,sans-Serif" font-size="11.00">(Cliente/Tutor)</text>
|
|
||||||
</g>
|
|
||||||
<!-- auth_user->petowner -->
|
|
||||||
<g id="edge1" class="edge">
|
|
||||||
<title>auth_user->petowner</title>
|
|
||||||
<path fill="none" stroke="#666666" d="M424.94,-639.31C424.94,-602.3 424.94,-522 424.94,-522 424.94,-522 638.96,-522 638.96,-522"/>
|
|
||||||
<polygon fill="#666666" stroke="#666666" points="638.96,-525.5 648.96,-522 638.96,-518.5 638.96,-525.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="645.27" y="-613.5" font-family="Helvetica,sans-Serif" font-size="9.00">1:1 optional</text>
|
|
||||||
</g>
|
|
||||||
<!-- veterinarian -->
|
|
||||||
<g id="node7" class="node">
|
|
||||||
<title>veterinarian</title>
|
|
||||||
<path fill="#bbdefb" stroke="black" d="M544.18,-405.2C544.18,-405.2 489.82,-405.2 489.82,-405.2 483.82,-405.2 477.82,-399.2 477.82,-393.2 477.82,-393.2 477.82,-381.2 477.82,-381.2 477.82,-375.2 483.82,-369.2 489.82,-369.2 489.82,-369.2 544.18,-369.2 544.18,-369.2 550.18,-369.2 556.18,-375.2 556.18,-381.2 556.18,-381.2 556.18,-393.2 556.18,-393.2 556.18,-399.2 550.18,-405.2 544.18,-405.2"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="517" y="-390.5" font-family="Helvetica,sans-Serif" font-size="11.00">Veterinarian</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="517" y="-377.3" font-family="Helvetica,sans-Serif" font-size="11.00">(Profesional)</text>
|
|
||||||
</g>
|
|
||||||
<!-- auth_user->veterinarian -->
|
|
||||||
<g id="edge2" class="edge">
|
|
||||||
<title>auth_user->veterinarian</title>
|
|
||||||
<path fill="none" stroke="#666666" d="M453.39,-664C477.9,-664 503.94,-664 503.94,-664 503.94,-664 503.94,-416.88 503.94,-416.88"/>
|
|
||||||
<polygon fill="#666666" stroke="#666666" points="507.44,-416.88 503.94,-406.88 500.44,-416.88 507.44,-416.88"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="470.26" y="-519.7" font-family="Helvetica,sans-Serif" font-size="9.00">1:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- django_admin -->
|
|
||||||
<g id="node2" class="node">
|
|
||||||
<title>django_admin</title>
|
|
||||||
<path fill="#c8e6c9" stroke="black" d="M339.55,-675.6C339.55,-675.6 278.45,-675.6 278.45,-675.6 272.45,-675.6 266.45,-669.6 266.45,-663.6 266.45,-663.6 266.45,-651.6 266.45,-651.6 266.45,-645.6 272.45,-639.6 278.45,-639.6 278.45,-639.6 339.55,-639.6 339.55,-639.6 345.55,-639.6 351.55,-645.6 351.55,-651.6 351.55,-651.6 351.55,-663.6 351.55,-663.6 351.55,-669.6 345.55,-675.6 339.55,-675.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="309" y="-660.9" font-family="Helvetica,sans-Serif" font-size="11.00">Django Admin</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="309" y="-647.7" font-family="Helvetica,sans-Serif" font-size="11.00">Interface</text>
|
|
||||||
</g>
|
|
||||||
<!-- drf -->
|
|
||||||
<g id="node3" class="node">
|
|
||||||
<title>drf</title>
|
|
||||||
<path fill="#c8e6c9" stroke="black" d="M236.63,-675.6C236.63,-675.6 177.37,-675.6 177.37,-675.6 171.37,-675.6 165.37,-669.6 165.37,-663.6 165.37,-663.6 165.37,-651.6 165.37,-651.6 165.37,-645.6 171.37,-639.6 177.37,-639.6 177.37,-639.6 236.63,-639.6 236.63,-639.6 242.63,-639.6 248.63,-645.6 248.63,-651.6 248.63,-651.6 248.63,-663.6 248.63,-663.6 248.63,-669.6 242.63,-675.6 236.63,-675.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="207" y="-660.9" font-family="Helvetica,sans-Serif" font-size="11.00">Django REST</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="207" y="-647.7" font-family="Helvetica,sans-Serif" font-size="11.00">Framework</text>
|
|
||||||
</g>
|
|
||||||
<!-- jwt_auth -->
|
|
||||||
<g id="node4" class="node">
|
|
||||||
<title>jwt_auth</title>
|
|
||||||
<path fill="#c8e6c9" stroke="black" d="M264.69,-540.4C264.69,-540.4 177.31,-540.4 177.31,-540.4 171.31,-540.4 165.31,-534.4 165.31,-528.4 165.31,-528.4 165.31,-516.4 165.31,-516.4 165.31,-510.4 171.31,-504.4 177.31,-504.4 177.31,-504.4 264.69,-504.4 264.69,-504.4 270.69,-504.4 276.69,-510.4 276.69,-516.4 276.69,-516.4 276.69,-528.4 276.69,-528.4 276.69,-534.4 270.69,-540.4 264.69,-540.4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="221" y="-525.7" font-family="Helvetica,sans-Serif" font-size="11.00">JWT Authentication</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="221" y="-512.5" font-family="Helvetica,sans-Serif" font-size="11.00">(SimpleJWT)</text>
|
|
||||||
</g>
|
|
||||||
<!-- drf->jwt_auth -->
|
|
||||||
<g id="edge3" class="edge">
|
|
||||||
<title>drf->jwt_auth</title>
|
|
||||||
<path fill="none" stroke="#999999" stroke-dasharray="5,2" d="M207,-639.37C207,-639.37 207,-552.29 207,-552.29"/>
|
|
||||||
<polygon fill="#999999" stroke="#999999" points="210.5,-552.29 207,-542.29 203.5,-552.29 210.5,-552.29"/>
|
|
||||||
</g>
|
|
||||||
<!-- pet -->
|
|
||||||
<g id="node6" class="node">
|
|
||||||
<title>pet</title>
|
|
||||||
<path fill="#bbdefb" stroke="black" d="M714.45,-405.2C714.45,-405.2 673.55,-405.2 673.55,-405.2 667.55,-405.2 661.55,-399.2 661.55,-393.2 661.55,-393.2 661.55,-381.2 661.55,-381.2 661.55,-375.2 667.55,-369.2 673.55,-369.2 673.55,-369.2 714.45,-369.2 714.45,-369.2 720.45,-369.2 726.45,-375.2 726.45,-381.2 726.45,-381.2 726.45,-393.2 726.45,-393.2 726.45,-399.2 720.45,-405.2 714.45,-405.2"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="694" y="-390.5" font-family="Helvetica,sans-Serif" font-size="11.00">Pet</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="694" y="-377.3" font-family="Helvetica,sans-Serif" font-size="11.00">(Mascota)</text>
|
|
||||||
</g>
|
|
||||||
<!-- petowner->pet -->
|
|
||||||
<g id="edge4" class="edge">
|
|
||||||
<title>petowner->pet</title>
|
|
||||||
<path fill="none" stroke="#1565c0" d="M694,-504.17C694,-504.17 694,-417.09 694,-417.09"/>
|
|
||||||
<polygon fill="#1565c0" stroke="#1565c0" points="697.5,-417.09 694,-407.09 690.5,-417.09 697.5,-417.09"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="701" y="-478.3" font-family="Helvetica,sans-Serif" font-size="9.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- servicerequest -->
|
|
||||||
<g id="node18" class="node">
|
|
||||||
<title>servicerequest</title>
|
|
||||||
<path fill="#ffcdd2" stroke="black" d="M1163.82,-405.2C1163.82,-405.2 1094.18,-405.2 1094.18,-405.2 1088.18,-405.2 1082.18,-399.2 1082.18,-393.2 1082.18,-393.2 1082.18,-381.2 1082.18,-381.2 1082.18,-375.2 1088.18,-369.2 1094.18,-369.2 1094.18,-369.2 1163.82,-369.2 1163.82,-369.2 1169.82,-369.2 1175.82,-375.2 1175.82,-381.2 1175.82,-381.2 1175.82,-393.2 1175.82,-393.2 1175.82,-399.2 1169.82,-405.2 1163.82,-405.2"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1129" y="-390.5" font-family="Helvetica,sans-Serif" font-size="11.00">ServiceRequest</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1129" y="-377.3" font-family="Helvetica,sans-Serif" font-size="11.00">(Solicitud)</text>
|
|
||||||
</g>
|
|
||||||
<!-- petowner->servicerequest -->
|
|
||||||
<g id="edge5" class="edge">
|
|
||||||
<title>petowner->servicerequest</title>
|
|
||||||
<path fill="none" stroke="#1565c0" d="M729.12,-504.02C729.12,-468.96 729.12,-396 729.12,-396 729.12,-396 1070.44,-396 1070.44,-396"/>
|
|
||||||
<polygon fill="#1565c0" stroke="#1565c0" points="1070.44,-399.5 1080.44,-396 1070.44,-392.5 1070.44,-399.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1005" y="-478.3" font-family="Helvetica,sans-Serif" font-size="9.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- neighborhood -->
|
|
||||||
<g id="node27" class="node">
|
|
||||||
<title>neighborhood</title>
|
|
||||||
<path fill="#e1bee7" stroke="black" d="M109.63,-52C109.63,-52 28.37,-52 28.37,-52 22.37,-52 16.37,-46 16.37,-40 16.37,-40 16.37,-28 16.37,-28 16.37,-22 22.37,-16 28.37,-16 28.37,-16 109.63,-16 109.63,-16 115.63,-16 121.63,-22 121.63,-28 121.63,-28 121.63,-40 121.63,-40 121.63,-46 115.63,-52 109.63,-52"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="69" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">Neighborhood /</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="69" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">Province / Locality</text>
|
|
||||||
</g>
|
|
||||||
<!-- petowner->neighborhood -->
|
|
||||||
<g id="edge24" class="edge">
|
|
||||||
<title>petowner->neighborhood</title>
|
|
||||||
<path fill="none" stroke="#7b1fa2" d="M655.77,-504.11C655.77,-441.12 655.77,-237 655.77,-237 655.77,-237 86.54,-237 86.54,-237 86.54,-237 86.54,-63.89 86.54,-63.89"/>
|
|
||||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="90.04,-63.89 86.54,-53.89 83.04,-63.89 90.04,-63.89"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="86" y="-301.7" font-family="Helvetica,sans-Serif" font-size="9.00">N:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisit -->
|
|
||||||
<g id="node8" class="node">
|
|
||||||
<title>vetvisit</title>
|
|
||||||
<path fill="#bbdefb" stroke="black" d="M725.37,-322.4C725.37,-322.4 682.63,-322.4 682.63,-322.4 676.63,-322.4 670.63,-316.4 670.63,-310.4 670.63,-310.4 670.63,-298.4 670.63,-298.4 670.63,-292.4 676.63,-286.4 682.63,-286.4 682.63,-286.4 725.37,-286.4 725.37,-286.4 731.37,-286.4 737.37,-292.4 737.37,-298.4 737.37,-298.4 737.37,-310.4 737.37,-310.4 737.37,-316.4 731.37,-322.4 725.37,-322.4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="704" y="-307.7" font-family="Helvetica,sans-Serif" font-size="11.00">VetVisit</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="704" y="-294.5" font-family="Helvetica,sans-Serif" font-size="11.00">(Consulta)</text>
|
|
||||||
</g>
|
|
||||||
<!-- pet->vetvisit -->
|
|
||||||
<g id="edge6" class="edge">
|
|
||||||
<title>pet->vetvisit</title>
|
|
||||||
<path fill="none" stroke="#1565c0" d="M698.54,-368.82C698.54,-368.82 698.54,-334.24 698.54,-334.24"/>
|
|
||||||
<polygon fill="#1565c0" stroke="#1565c0" points="702.04,-334.24 698.54,-324.24 695.04,-334.24 702.04,-334.24"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="710.25" y="-343.1" font-family="Helvetica,sans-Serif" font-size="9.00">N:M</text>
|
|
||||||
</g>
|
|
||||||
<!-- pet_health -->
|
|
||||||
<g id="node11" class="node">
|
|
||||||
<title>pet_health</title>
|
|
||||||
<path fill="#bbdefb" stroke="black" d="M640.88,-322.4C640.88,-322.4 587.12,-322.4 587.12,-322.4 581.12,-322.4 575.12,-316.4 575.12,-310.4 575.12,-310.4 575.12,-298.4 575.12,-298.4 575.12,-292.4 581.12,-286.4 587.12,-286.4 587.12,-286.4 640.88,-286.4 640.88,-286.4 646.88,-286.4 652.88,-292.4 652.88,-298.4 652.88,-298.4 652.88,-310.4 652.88,-310.4 652.88,-316.4 646.88,-322.4 640.88,-322.4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="614" y="-307.7" font-family="Helvetica,sans-Serif" font-size="11.00">PetVaccine /</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="614" y="-294.5" font-family="Helvetica,sans-Serif" font-size="11.00">PetStudy</text>
|
|
||||||
</g>
|
|
||||||
<!-- pet->pet_health -->
|
|
||||||
<g id="edge7" class="edge">
|
|
||||||
<title>pet->pet_health</title>
|
|
||||||
<path fill="none" stroke="#1565c0" d="M666.09,-368.72C666.09,-342.59 666.09,-298 666.09,-298 666.09,-298 664.73,-298 664.73,-298"/>
|
|
||||||
<polygon fill="#1565c0" stroke="#1565c0" points="664.73,-294.5 654.73,-298 664.73,-301.5 664.73,-294.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="623" y="-343.1" font-family="Helvetica,sans-Serif" font-size="9.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- veterinarian->vetvisit -->
|
|
||||||
<g id="edge8" class="edge">
|
|
||||||
<title>veterinarian->vetvisit</title>
|
|
||||||
<path fill="none" stroke="#1565c0" d="M556.46,-387C598.38,-387 658.66,-387 658.66,-387 658.66,-387 658.66,-310 658.66,-310 658.66,-310 659.85,-310 659.85,-310"/>
|
|
||||||
<polygon fill="#1565c0" stroke="#1565c0" points="659.06,-313.5 669.06,-310 659.06,-306.5 659.06,-313.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="668" y="-343.1" font-family="Helvetica,sans-Serif" font-size="9.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- availability -->
|
|
||||||
<g id="node10" class="node">
|
|
||||||
<title>availability</title>
|
|
||||||
<path fill="#bbdefb" stroke="black" d="M545.4,-322.4C545.4,-322.4 488.6,-322.4 488.6,-322.4 482.6,-322.4 476.6,-316.4 476.6,-310.4 476.6,-310.4 476.6,-298.4 476.6,-298.4 476.6,-292.4 482.6,-286.4 488.6,-286.4 488.6,-286.4 545.4,-286.4 545.4,-286.4 551.4,-286.4 557.4,-292.4 557.4,-298.4 557.4,-298.4 557.4,-310.4 557.4,-310.4 557.4,-316.4 551.4,-322.4 545.4,-322.4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="517" y="-307.7" font-family="Helvetica,sans-Serif" font-size="11.00">Availability /</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="517" y="-294.5" font-family="Helvetica,sans-Serif" font-size="11.00">Unavailability</text>
|
|
||||||
</g>
|
|
||||||
<!-- veterinarian->availability -->
|
|
||||||
<g id="edge9" class="edge">
|
|
||||||
<title>veterinarian->availability</title>
|
|
||||||
<path fill="none" stroke="#1565c0" d="M503.94,-368.82C503.94,-368.82 503.94,-334.24 503.94,-334.24"/>
|
|
||||||
<polygon fill="#1565c0" stroke="#1565c0" points="507.44,-334.24 503.94,-324.24 500.44,-334.24 507.44,-334.24"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="524" y="-343.1" font-family="Helvetica,sans-Serif" font-size="9.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetasked -->
|
|
||||||
<g id="node20" class="node">
|
|
||||||
<title>vetasked</title>
|
|
||||||
<path fill="#ffcdd2" stroke="black" d="M1173.94,-322.4C1173.94,-322.4 1092.06,-322.4 1092.06,-322.4 1086.06,-322.4 1080.06,-316.4 1080.06,-310.4 1080.06,-310.4 1080.06,-298.4 1080.06,-298.4 1080.06,-292.4 1086.06,-286.4 1092.06,-286.4 1092.06,-286.4 1173.94,-286.4 1173.94,-286.4 1179.94,-286.4 1185.94,-292.4 1185.94,-298.4 1185.94,-298.4 1185.94,-310.4 1185.94,-310.4 1185.94,-316.4 1179.94,-322.4 1173.94,-322.4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1133" y="-307.7" font-family="Helvetica,sans-Serif" font-size="11.00">VeterinarianAsked</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1133" y="-294.5" font-family="Helvetica,sans-Serif" font-size="11.00">(Vet Consultado)</text>
|
|
||||||
</g>
|
|
||||||
<!-- veterinarian->vetasked -->
|
|
||||||
<g id="edge23" class="edge">
|
|
||||||
<title>veterinarian->vetasked</title>
|
|
||||||
<path fill="none" stroke="#c62828" d="M530.06,-368.88C530.06,-354.89 530.06,-338 530.06,-338 530.06,-338 1129,-338 1129,-338 1129,-338 1129,-334.29 1129,-334.29"/>
|
|
||||||
<polygon fill="#c62828" stroke="#c62828" points="1132.5,-334.29 1129,-324.29 1125.5,-334.29 1132.5,-334.29"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="959" y="-343.1" font-family="Helvetica,sans-Serif" font-size="9.00">N:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- specialty -->
|
|
||||||
<g id="node24" class="node">
|
|
||||||
<title>specialty</title>
|
|
||||||
<path fill="#e1bee7" stroke="black" d="M224.66,-52C224.66,-52 151.34,-52 151.34,-52 145.34,-52 139.34,-46 139.34,-40 139.34,-40 139.34,-28 139.34,-28 139.34,-22 145.34,-16 151.34,-16 151.34,-16 224.66,-16 224.66,-16 230.66,-16 236.66,-22 236.66,-28 236.66,-28 236.66,-40 236.66,-40 236.66,-46 230.66,-52 224.66,-52"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="188" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">Specialty</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="188" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">(Especialidades)</text>
|
|
||||||
</g>
|
|
||||||
<!-- veterinarian->specialty -->
|
|
||||||
<g id="edge25" class="edge">
|
|
||||||
<title>veterinarian->specialty</title>
|
|
||||||
<path fill="none" stroke="#7b1fa2" d="M477.62,-387C392.9,-387 200.99,-387 200.99,-387 200.99,-387 200.99,-64 200.99,-64"/>
|
|
||||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="204.49,-64 200.99,-54 197.49,-64 204.49,-64"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="289.25" y="-260.3" font-family="Helvetica,sans-Serif" font-size="9.00">N:M</text>
|
|
||||||
</g>
|
|
||||||
<!-- veterinarian->neighborhood -->
|
|
||||||
<g id="edge26" class="edge">
|
|
||||||
<title>veterinarian->neighborhood</title>
|
|
||||||
<path fill="none" stroke="#7b1fa2" d="M477.71,-396C365.47,-396 51.46,-396 51.46,-396 51.46,-396 51.46,-63.96 51.46,-63.96"/>
|
|
||||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="54.96,-63.96 51.46,-53.96 47.96,-63.96 54.96,-63.96"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="158.01" y="-260.3" font-family="Helvetica,sans-Serif" font-size="9.00">N:M coverage</text>
|
|
||||||
</g>
|
|
||||||
<!-- turnfee -->
|
|
||||||
<g id="node28" class="node">
|
|
||||||
<title>turnfee</title>
|
|
||||||
<path fill="#e1bee7" stroke="black" d="M347.02,-52C347.02,-52 266.98,-52 266.98,-52 260.98,-52 254.98,-46 254.98,-40 254.98,-40 254.98,-28 254.98,-28 254.98,-22 260.98,-16 266.98,-16 266.98,-16 347.02,-16 347.02,-16 353.02,-16 359.02,-22 359.02,-28 359.02,-28 359.02,-40 359.02,-40 359.02,-46 353.02,-52 347.02,-52"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="307" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">IndividualTurnFee</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="307" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">Group</text>
|
|
||||||
</g>
|
|
||||||
<!-- veterinarian->turnfee -->
|
|
||||||
<g id="edge30" class="edge">
|
|
||||||
<title>veterinarian->turnfee</title>
|
|
||||||
<path fill="none" stroke="#7b1fa2" d="M477.44,-378C429.56,-378 355.29,-378 355.29,-378 355.29,-378 355.29,-64.01 355.29,-64.01"/>
|
|
||||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="358.79,-64.01 355.29,-54.01 351.79,-64.01 358.79,-64.01"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="364.25" y="-260.3" font-family="Helvetica,sans-Serif" font-size="9.00">N:M</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisitreport -->
|
|
||||||
<g id="node9" class="node">
|
|
||||||
<title>vetvisitreport</title>
|
|
||||||
<path fill="#bbdefb" stroke="black" d="M694.34,-187.2C694.34,-187.2 621.66,-187.2 621.66,-187.2 615.66,-187.2 609.66,-181.2 609.66,-175.2 609.66,-175.2 609.66,-163.2 609.66,-163.2 609.66,-157.2 615.66,-151.2 621.66,-151.2 621.66,-151.2 694.34,-151.2 694.34,-151.2 700.34,-151.2 706.34,-157.2 706.34,-163.2 706.34,-163.2 706.34,-175.2 706.34,-175.2 706.34,-181.2 700.34,-187.2 694.34,-187.2"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="658" y="-172.5" font-family="Helvetica,sans-Serif" font-size="11.00">VetVisitReport</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="658" y="-159.3" font-family="Helvetica,sans-Serif" font-size="11.00">(Informe Clinico)</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisit->vetvisitreport -->
|
|
||||||
<g id="edge10" class="edge">
|
|
||||||
<title>vetvisit->vetvisitreport</title>
|
|
||||||
<path fill="none" stroke="#1565c0" d="M688.49,-286.17C688.49,-286.17 688.49,-199.09 688.49,-199.09"/>
|
|
||||||
<polygon fill="#1565c0" stroke="#1565c0" points="691.99,-199.09 688.49,-189.09 684.99,-199.09 691.99,-199.09"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="671" y="-260.3" font-family="Helvetica,sans-Serif" font-size="9.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisit->servicerequest -->
|
|
||||||
<g id="edge11" class="edge">
|
|
||||||
<title>vetvisit->servicerequest</title>
|
|
||||||
<path fill="none" stroke="#1565c0" stroke-dasharray="5,2" d="M731.8,-322.73C731.8,-347.07 731.8,-387 731.8,-387 731.8,-387 1070.36,-387 1070.36,-387"/>
|
|
||||||
<polygon fill="#1565c0" stroke="#1565c0" points="1070.36,-390.5 1080.36,-387 1070.36,-383.5 1070.36,-390.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="749.26" y="-343.1" font-family="Helvetica,sans-Serif" font-size="9.00">1:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- google_cal -->
|
|
||||||
<g id="node31" class="node">
|
|
||||||
<title>google_cal</title>
|
|
||||||
<path fill="#ffe0b2" stroke="black" d="M1122.58,-52C1122.58,-52 1047.42,-52 1047.42,-52 1041.42,-52 1035.42,-46 1035.42,-40 1035.42,-40 1035.42,-28 1035.42,-28 1035.42,-22 1041.42,-16 1047.42,-16 1047.42,-16 1122.58,-16 1122.58,-16 1128.58,-16 1134.58,-22 1134.58,-28 1134.58,-28 1134.58,-40 1134.58,-40 1134.58,-46 1128.58,-52 1122.58,-52"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1085" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">Google Calendar</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1085" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">(Agenda)</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisit->google_cal -->
|
|
||||||
<g id="edge33" class="edge">
|
|
||||||
<title>vetvisit->google_cal</title>
|
|
||||||
<path fill="none" stroke="#e65100" stroke-dasharray="5,2" d="M737.59,-304C824.11,-304 1047.82,-304 1047.82,-304 1047.82,-304 1047.82,-63.65 1047.82,-63.65"/>
|
|
||||||
<polygon fill="#e65100" stroke="#e65100" points="1051.32,-63.65 1047.82,-53.65 1044.32,-63.65 1051.32,-63.65"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1054.25" y="-166.5" font-family="Helvetica,sans-Serif" font-size="9.00">sync</text>
|
|
||||||
</g>
|
|
||||||
<!-- receipt -->
|
|
||||||
<g id="node36" class="node">
|
|
||||||
<title>receipt</title>
|
|
||||||
<path fill="#fff9c4" stroke="black" d="M880.99,-187.2C880.99,-187.2 815.01,-187.2 815.01,-187.2 809.01,-187.2 803.01,-181.2 803.01,-175.2 803.01,-175.2 803.01,-163.2 803.01,-163.2 803.01,-157.2 809.01,-151.2 815.01,-151.2 815.01,-151.2 880.99,-151.2 880.99,-151.2 886.99,-151.2 892.99,-157.2 892.99,-163.2 892.99,-163.2 892.99,-175.2 892.99,-175.2 892.99,-181.2 886.99,-187.2 880.99,-187.2"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="848" y="-172.5" font-family="Helvetica,sans-Serif" font-size="11.00">Receipt</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="848" y="-159.3" font-family="Helvetica,sans-Serif" font-size="11.00">(Comprobante)</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisit->receipt -->
|
|
||||||
<g id="edge36" class="edge">
|
|
||||||
<title>vetvisit->receipt</title>
|
|
||||||
<path fill="none" stroke="#f9a825" stroke-dasharray="5,2" d="M722.89,-286.14C722.89,-249.18 722.89,-169 722.89,-169 722.89,-169 791.32,-169 791.32,-169"/>
|
|
||||||
<polygon fill="#f9a825" stroke="#f9a825" points="791.32,-172.5 801.32,-169 791.32,-165.5 791.32,-172.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="727.27" y="-260.3" font-family="Helvetica,sans-Serif" font-size="9.00">1:1 optional</text>
|
|
||||||
</g>
|
|
||||||
<!-- medication -->
|
|
||||||
<g id="node25" class="node">
|
|
||||||
<title>medication</title>
|
|
||||||
<path fill="#e1bee7" stroke="black" d="M678.43,-52C678.43,-52 607.57,-52 607.57,-52 601.57,-52 595.57,-46 595.57,-40 595.57,-40 595.57,-28 595.57,-28 595.57,-22 601.57,-16 607.57,-16 607.57,-16 678.43,-16 678.43,-16 684.43,-16 690.43,-22 690.43,-28 690.43,-28 690.43,-40 690.43,-40 690.43,-46 684.43,-52 678.43,-52"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="643" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">Medication</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="643" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">(Medicamentos)</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisitreport->medication -->
|
|
||||||
<g id="edge29" class="edge">
|
|
||||||
<title>vetvisitreport->medication</title>
|
|
||||||
<path fill="none" stroke="#7b1fa2" stroke-dasharray="5,2" d="M650.04,-150.97C650.04,-150.97 650.04,-63.89 650.04,-63.89"/>
|
|
||||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="653.54,-63.89 650.04,-53.89 646.54,-63.89 653.54,-63.89"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="667.26" y="-125.1" font-family="Helvetica,sans-Serif" font-size="9.00">references</text>
|
|
||||||
</g>
|
|
||||||
<!-- service -->
|
|
||||||
<g id="node12" class="node">
|
|
||||||
<title>service</title>
|
|
||||||
<path fill="#c8e6c9" stroke="black" d="M972.22,-675.6C972.22,-675.6 933.78,-675.6 933.78,-675.6 927.78,-675.6 921.78,-669.6 921.78,-663.6 921.78,-663.6 921.78,-651.6 921.78,-651.6 921.78,-645.6 927.78,-639.6 933.78,-639.6 933.78,-639.6 972.22,-639.6 972.22,-639.6 978.22,-639.6 984.22,-645.6 984.22,-651.6 984.22,-651.6 984.22,-663.6 984.22,-663.6 984.22,-669.6 978.22,-675.6 972.22,-675.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="953" y="-660.9" font-family="Helvetica,sans-Serif" font-size="11.00">Service</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="953" y="-647.7" font-family="Helvetica,sans-Serif" font-size="11.00">(Servicio)</text>
|
|
||||||
</g>
|
|
||||||
<!-- category -->
|
|
||||||
<g id="node13" class="node">
|
|
||||||
<title>category</title>
|
|
||||||
<path fill="#c8e6c9" stroke="black" d="M849.19,-540.4C849.19,-540.4 772.81,-540.4 772.81,-540.4 766.81,-540.4 760.81,-534.4 760.81,-528.4 760.81,-528.4 760.81,-516.4 760.81,-516.4 760.81,-510.4 766.81,-504.4 772.81,-504.4 772.81,-504.4 849.19,-504.4 849.19,-504.4 855.19,-504.4 861.19,-510.4 861.19,-516.4 861.19,-516.4 861.19,-528.4 861.19,-528.4 861.19,-534.4 855.19,-540.4 849.19,-540.4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="811" y="-525.7" font-family="Helvetica,sans-Serif" font-size="11.00">Category / Group</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="811" y="-512.5" font-family="Helvetica,sans-Serif" font-size="11.00">(Categorias)</text>
|
|
||||||
</g>
|
|
||||||
<!-- service->category -->
|
|
||||||
<g id="edge12" class="edge">
|
|
||||||
<title>service->category</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M921.49,-652C879.65,-652 811,-652 811,-652 811,-652 811,-552.21 811,-552.21"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="814.5,-552.21 811,-542.21 807.5,-552.21 814.5,-552.21"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="835" y="-613.5" font-family="Helvetica,sans-Serif" font-size="9.00">N:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- prices -->
|
|
||||||
<g id="node14" class="node">
|
|
||||||
<title>prices</title>
|
|
||||||
<path fill="#c8e6c9" stroke="black" d="M927,-540.4C927,-540.4 891,-540.4 891,-540.4 885,-540.4 879,-534.4 879,-528.4 879,-528.4 879,-516.4 879,-516.4 879,-510.4 885,-504.4 891,-504.4 891,-504.4 927,-504.4 927,-504.4 933,-504.4 939,-510.4 939,-516.4 939,-516.4 939,-528.4 939,-528.4 939,-534.4 933,-540.4 927,-540.4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="909" y="-525.7" font-family="Helvetica,sans-Serif" font-size="11.00">Prices</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="909" y="-512.5" font-family="Helvetica,sans-Serif" font-size="11.00">(Precios)</text>
|
|
||||||
</g>
|
|
||||||
<!-- service->prices -->
|
|
||||||
<g id="edge13" class="edge">
|
|
||||||
<title>service->prices</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M930.39,-639.37C930.39,-639.37 930.39,-552.29 930.39,-552.29"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="933.89,-552.29 930.39,-542.29 926.89,-552.29 933.89,-552.29"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="929" y="-613.5" font-family="Helvetica,sans-Serif" font-size="9.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- discounts -->
|
|
||||||
<g id="node15" class="node">
|
|
||||||
<title>discounts</title>
|
|
||||||
<path fill="#c8e6c9" stroke="black" d="M1026.71,-540.4C1026.71,-540.4 969.29,-540.4 969.29,-540.4 963.29,-540.4 957.29,-534.4 957.29,-528.4 957.29,-528.4 957.29,-516.4 957.29,-516.4 957.29,-510.4 963.29,-504.4 969.29,-504.4 969.29,-504.4 1026.71,-504.4 1026.71,-504.4 1032.71,-504.4 1038.71,-510.4 1038.71,-516.4 1038.71,-516.4 1038.71,-528.4 1038.71,-528.4 1038.71,-534.4 1032.71,-540.4 1026.71,-540.4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="998" y="-525.7" font-family="Helvetica,sans-Serif" font-size="11.00">Discounts</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="998" y="-512.5" font-family="Helvetica,sans-Serif" font-size="11.00">(Descuentos)</text>
|
|
||||||
</g>
|
|
||||||
<!-- service->discounts -->
|
|
||||||
<g id="edge14" class="edge">
|
|
||||||
<title>service->discounts</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M970.76,-639.37C970.76,-639.37 970.76,-552.29 970.76,-552.29"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="974.26,-552.29 970.76,-542.29 967.26,-552.29 974.26,-552.29"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="992" y="-613.5" font-family="Helvetica,sans-Serif" font-size="9.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- cart -->
|
|
||||||
<g id="node16" class="node">
|
|
||||||
<title>cart</title>
|
|
||||||
<path fill="#c8e6c9" stroke="black" d="M1133.06,-540.4C1133.06,-540.4 1068.94,-540.4 1068.94,-540.4 1062.94,-540.4 1056.94,-534.4 1056.94,-528.4 1056.94,-528.4 1056.94,-516.4 1056.94,-516.4 1056.94,-510.4 1062.94,-504.4 1068.94,-504.4 1068.94,-504.4 1133.06,-504.4 1133.06,-504.4 1139.06,-504.4 1145.06,-510.4 1145.06,-516.4 1145.06,-516.4 1145.06,-528.4 1145.06,-528.4 1145.06,-534.4 1139.06,-540.4 1133.06,-540.4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1101" y="-525.7" font-family="Helvetica,sans-Serif" font-size="11.00">Cart / CartItem</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1101" y="-512.5" font-family="Helvetica,sans-Serif" font-size="11.00">(Carrito)</text>
|
|
||||||
</g>
|
|
||||||
<!-- service->cart -->
|
|
||||||
<g id="edge17" class="edge">
|
|
||||||
<title>service->cart</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M984.39,-658C1027.83,-658 1101,-658 1101,-658 1101,-658 1101,-552.2 1101,-552.2"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1104.5,-552.2 1101,-542.2 1097.5,-552.2 1104.5,-552.2"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1089.26" y="-613.5" font-family="Helvetica,sans-Serif" font-size="9.00">via CartItem</text>
|
|
||||||
</g>
|
|
||||||
<!-- cart->petowner -->
|
|
||||||
<g id="edge15" class="edge">
|
|
||||||
<title>cart->petowner</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M1064.64,-504.13C1064.64,-489.36 1064.64,-471 1064.64,-471 1064.64,-471 734.47,-471 734.47,-471 734.47,-471 734.47,-492.62 734.47,-492.62"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="730.97,-492.62 734.47,-502.62 737.97,-492.62 730.97,-492.62"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="871" y="-613.5" font-family="Helvetica,sans-Serif" font-size="9.00">N:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- cart->veterinarian -->
|
|
||||||
<g id="edge16" class="edge">
|
|
||||||
<title>cart->veterinarian</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" stroke-dasharray="5,2" d="M1072.35,-504.09C1072.35,-479.26 1072.35,-438 1072.35,-438 1072.35,-438 530.06,-438 530.06,-438 530.06,-438 530.06,-417.05 530.06,-417.05"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="533.56,-417.05 530.06,-407.05 526.56,-417.05 533.56,-417.05"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="886.02" y="-478.3" font-family="Helvetica,sans-Serif" font-size="9.00">N:1 optional</text>
|
|
||||||
</g>
|
|
||||||
<!-- combo -->
|
|
||||||
<g id="node17" class="node">
|
|
||||||
<title>combo</title>
|
|
||||||
<path fill="#c8e6c9" stroke="black" d="M985.07,-810.8C985.07,-810.8 920.93,-810.8 920.93,-810.8 914.93,-810.8 908.93,-804.8 908.93,-798.8 908.93,-798.8 908.93,-786.8 908.93,-786.8 908.93,-780.8 914.93,-774.8 920.93,-774.8 920.93,-774.8 985.07,-774.8 985.07,-774.8 991.07,-774.8 997.07,-780.8 997.07,-786.8 997.07,-786.8 997.07,-798.8 997.07,-798.8 997.07,-804.8 991.07,-810.8 985.07,-810.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="953" y="-796.1" font-family="Helvetica,sans-Serif" font-size="11.00">ServiceCombo</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="953" y="-782.9" font-family="Helvetica,sans-Serif" font-size="11.00">(Paquetes)</text>
|
|
||||||
</g>
|
|
||||||
<!-- combo->service -->
|
|
||||||
<g id="edge18" class="edge">
|
|
||||||
<title>combo->service</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M953,-774.57C953,-774.57 953,-687.49 953,-687.49"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="956.5,-687.49 953,-677.49 949.5,-687.49 956.5,-687.49"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="969.76" y="-748.7" font-family="Helvetica,sans-Serif" font-size="9.00">contains</text>
|
|
||||||
</g>
|
|
||||||
<!-- servicerequest->cart -->
|
|
||||||
<g id="edge19" class="edge">
|
|
||||||
<title>servicerequest->cart</title>
|
|
||||||
<path fill="none" stroke="#c62828" d="M1113.62,-405.43C1113.62,-405.43 1113.62,-492.51 1113.62,-492.51"/>
|
|
||||||
<polygon fill="#c62828" stroke="#c62828" points="1110.12,-492.51 1113.62,-502.51 1117.12,-492.51 1110.12,-492.51"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1121.26" y="-478.3" font-family="Helvetica,sans-Serif" font-size="9.00">1:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- statehistory -->
|
|
||||||
<g id="node19" class="node">
|
|
||||||
<title>statehistory</title>
|
|
||||||
<path fill="#ffcdd2" stroke="black" d="M1267.95,-322.4C1267.95,-322.4 1216.05,-322.4 1216.05,-322.4 1210.05,-322.4 1204.05,-316.4 1204.05,-310.4 1204.05,-310.4 1204.05,-298.4 1204.05,-298.4 1204.05,-292.4 1210.05,-286.4 1216.05,-286.4 1216.05,-286.4 1267.95,-286.4 1267.95,-286.4 1273.95,-286.4 1279.95,-292.4 1279.95,-298.4 1279.95,-298.4 1279.95,-310.4 1279.95,-310.4 1279.95,-316.4 1273.95,-322.4 1267.95,-322.4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1242" y="-307.7" font-family="Helvetica,sans-Serif" font-size="11.00">StateHistory</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1242" y="-294.5" font-family="Helvetica,sans-Serif" font-size="11.00">(Historial)</text>
|
|
||||||
</g>
|
|
||||||
<!-- servicerequest->statehistory -->
|
|
||||||
<g id="edge20" class="edge">
|
|
||||||
<title>servicerequest->statehistory</title>
|
|
||||||
<path fill="none" stroke="#c62828" d="M1176.2,-378C1201.95,-378 1228.64,-378 1228.64,-378 1228.64,-378 1228.64,-334.11 1228.64,-334.11"/>
|
|
||||||
<polygon fill="#c62828" stroke="#c62828" points="1232.14,-334.11 1228.64,-324.11 1225.14,-334.11 1232.14,-334.11"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1212" y="-343.1" font-family="Helvetica,sans-Serif" font-size="9.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- servicerequest->vetasked -->
|
|
||||||
<g id="edge21" class="edge">
|
|
||||||
<title>servicerequest->vetasked</title>
|
|
||||||
<path fill="none" stroke="#c62828" d="M1152.41,-368.82C1152.41,-368.82 1152.41,-334.24 1152.41,-334.24"/>
|
|
||||||
<polygon fill="#c62828" stroke="#c62828" points="1155.91,-334.24 1152.41,-324.24 1148.91,-334.24 1155.91,-334.24"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1137" y="-343.1" font-family="Helvetica,sans-Serif" font-size="9.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- campaign -->
|
|
||||||
<g id="node22" class="node">
|
|
||||||
<title>campaign</title>
|
|
||||||
<path fill="#e1bee7" stroke="black" d="M436.81,-52C436.81,-52 389.19,-52 389.19,-52 383.19,-52 377.19,-46 377.19,-40 377.19,-40 377.19,-28 377.19,-28 377.19,-22 383.19,-16 389.19,-16 389.19,-16 436.81,-16 436.81,-16 442.81,-16 448.81,-22 448.81,-28 448.81,-28 448.81,-40 448.81,-40 448.81,-46 442.81,-52 436.81,-52"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="413" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">Campaign</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="413" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">(Marketing)</text>
|
|
||||||
</g>
|
|
||||||
<!-- servicerequest->campaign -->
|
|
||||||
<g id="edge27" class="edge">
|
|
||||||
<title>servicerequest->campaign</title>
|
|
||||||
<path fill="none" stroke="#7b1fa2" stroke-dasharray="5,2" d="M1105.59,-369.05C1105.59,-361.29 1105.59,-354 1105.59,-354 1105.59,-354 401.06,-354 401.06,-354 401.06,-354 401.06,-63.63 401.06,-63.63"/>
|
|
||||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="404.56,-63.63 401.06,-53.63 397.56,-63.64 404.56,-63.63"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="437.02" y="-260.3" font-family="Helvetica,sans-Serif" font-size="9.00">N:1 optional</text>
|
|
||||||
</g>
|
|
||||||
<!-- tag -->
|
|
||||||
<g id="node23" class="node">
|
|
||||||
<title>tag</title>
|
|
||||||
<path fill="#e1bee7" stroke="black" d="M765.59,-52C765.59,-52 720.41,-52 720.41,-52 714.41,-52 708.41,-46 708.41,-40 708.41,-40 708.41,-28 708.41,-28 708.41,-22 714.41,-16 720.41,-16 720.41,-16 765.59,-16 765.59,-16 771.59,-16 777.59,-22 777.59,-28 777.59,-28 777.59,-40 777.59,-40 777.59,-46 771.59,-52 765.59,-52"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="743" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">Tag</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="743" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">(Etiquetas)</text>
|
|
||||||
</g>
|
|
||||||
<!-- servicerequest->tag -->
|
|
||||||
<g id="edge28" class="edge">
|
|
||||||
<title>servicerequest->tag</title>
|
|
||||||
<path fill="none" stroke="#7b1fa2" d="M1081.96,-378C984.44,-378 769.2,-378 769.2,-378 769.2,-378 769.2,-64.01 769.2,-64.01"/>
|
|
||||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="772.7,-64.01 769.2,-54.01 765.7,-64.01 772.7,-64.01"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="783.25" y="-260.3" font-family="Helvetica,sans-Serif" font-size="9.00">N:M</text>
|
|
||||||
</g>
|
|
||||||
<!-- mercadopago -->
|
|
||||||
<g id="node29" class="node">
|
|
||||||
<title>mercadopago</title>
|
|
||||||
<path fill="#f8bbd9" stroke="black" d="M1241.24,-540.4C1241.24,-540.4 1180.76,-540.4 1180.76,-540.4 1174.76,-540.4 1168.76,-534.4 1168.76,-528.4 1168.76,-528.4 1168.76,-516.4 1168.76,-516.4 1168.76,-510.4 1174.76,-504.4 1180.76,-504.4 1180.76,-504.4 1241.24,-504.4 1241.24,-504.4 1247.24,-504.4 1253.24,-510.4 1253.24,-516.4 1253.24,-516.4 1253.24,-528.4 1253.24,-528.4 1253.24,-534.4 1247.24,-540.4 1241.24,-540.4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1211" y="-525.7" font-family="Helvetica,sans-Serif" font-size="11.00">MercadoPago</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1211" y="-512.5" font-family="Helvetica,sans-Serif" font-size="11.00">Account</text>
|
|
||||||
</g>
|
|
||||||
<!-- servicerequest->mercadopago -->
|
|
||||||
<g id="edge31" class="edge">
|
|
||||||
<title>servicerequest->mercadopago</title>
|
|
||||||
<path fill="none" stroke="#ad1457" d="M1172.29,-405.43C1172.29,-405.43 1172.29,-492.51 1172.29,-492.51"/>
|
|
||||||
<polygon fill="#ad1457" stroke="#ad1457" points="1168.79,-492.51 1172.29,-502.51 1175.79,-492.51 1168.79,-492.51"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1205.26" y="-478.3" font-family="Helvetica,sans-Serif" font-size="9.00">payment</text>
|
|
||||||
</g>
|
|
||||||
<!-- mercately -->
|
|
||||||
<g id="node33" class="node">
|
|
||||||
<title>mercately</title>
|
|
||||||
<path fill="#ffe0b2" stroke="black" d="M1523.04,-52C1523.04,-52 1472.96,-52 1472.96,-52 1466.96,-52 1460.96,-46 1460.96,-40 1460.96,-40 1460.96,-28 1460.96,-28 1460.96,-22 1466.96,-16 1472.96,-16 1472.96,-16 1523.04,-16 1523.04,-16 1529.04,-16 1535.04,-22 1535.04,-28 1535.04,-28 1535.04,-40 1535.04,-40 1535.04,-46 1529.04,-52 1523.04,-52"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1498" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">Mercately</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1498" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">(WhatsApp)</text>
|
|
||||||
</g>
|
|
||||||
<!-- servicerequest->mercately -->
|
|
||||||
<g id="edge34" class="edge">
|
|
||||||
<title>servicerequest->mercately</title>
|
|
||||||
<path fill="none" stroke="#e65100" stroke-dasharray="5,2" d="M1176.11,-387C1275.59,-387 1498,-387 1498,-387 1498,-387 1498,-64 1498,-64"/>
|
|
||||||
<polygon fill="#e65100" stroke="#e65100" points="1501.5,-64 1498,-54 1494.5,-64 1501.5,-64"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1350.76" y="-260.3" font-family="Helvetica,sans-Serif" font-size="9.00">notify</text>
|
|
||||||
</g>
|
|
||||||
<!-- reminders -->
|
|
||||||
<g id="node21" class="node">
|
|
||||||
<title>reminders</title>
|
|
||||||
<path fill="#ffcdd2" stroke="black" d="M1249.9,-187.2C1249.9,-187.2 1182.1,-187.2 1182.1,-187.2 1176.1,-187.2 1170.1,-181.2 1170.1,-175.2 1170.1,-175.2 1170.1,-163.2 1170.1,-163.2 1170.1,-157.2 1176.1,-151.2 1182.1,-151.2 1182.1,-151.2 1249.9,-151.2 1249.9,-151.2 1255.9,-151.2 1261.9,-157.2 1261.9,-163.2 1261.9,-163.2 1261.9,-175.2 1261.9,-175.2 1261.9,-181.2 1255.9,-187.2 1249.9,-187.2"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1216" y="-172.5" font-family="Helvetica,sans-Serif" font-size="11.00">Reminders</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1216" y="-159.3" font-family="Helvetica,sans-Serif" font-size="11.00">(Recordatorios)</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetasked->reminders -->
|
|
||||||
<g id="edge22" class="edge">
|
|
||||||
<title>vetasked->reminders</title>
|
|
||||||
<path fill="none" stroke="#c62828" d="M1178.02,-286.17C1178.02,-286.17 1178.02,-199.09 1178.02,-199.09"/>
|
|
||||||
<polygon fill="#c62828" stroke="#c62828" points="1181.52,-199.09 1178.02,-189.09 1174.52,-199.09 1181.52,-199.09"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1181" y="-260.3" font-family="Helvetica,sans-Serif" font-size="9.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- celery -->
|
|
||||||
<g id="node35" class="node">
|
|
||||||
<title>celery</title>
|
|
||||||
<path fill="#ffe0b2" stroke="black" d="M1332.84,-52C1332.84,-52 1271.16,-52 1271.16,-52 1265.16,-52 1259.16,-46 1259.16,-40 1259.16,-40 1259.16,-28 1259.16,-28 1259.16,-22 1265.16,-16 1271.16,-16 1271.16,-16 1332.84,-16 1332.84,-16 1338.84,-16 1344.84,-22 1344.84,-28 1344.84,-28 1344.84,-40 1344.84,-40 1344.84,-46 1338.84,-52 1332.84,-52"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1302" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">Celery</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1302" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">(Async Tasks)</text>
|
|
||||||
</g>
|
|
||||||
<!-- reminders->celery -->
|
|
||||||
<g id="edge35" class="edge">
|
|
||||||
<title>reminders->celery</title>
|
|
||||||
<path fill="none" stroke="#e65100" stroke-dasharray="5,2" d="M1250.27,-150.97C1250.27,-114.06 1250.27,-34 1250.27,-34 1250.27,-34 1251.13,-34 1251.13,-34"/>
|
|
||||||
<polygon fill="#e65100" stroke="#e65100" points="1247.3,-37.5 1257.3,-34 1247.3,-30.5 1247.3,-37.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1270.52" y="-125.1" font-family="Helvetica,sans-Serif" font-size="9.00">scheduled</text>
|
|
||||||
</g>
|
|
||||||
<!-- breed -->
|
|
||||||
<g id="node26" class="node">
|
|
||||||
<title>breed</title>
|
|
||||||
<path fill="#e1bee7" stroke="black" d="M565.08,-52C565.08,-52 478.92,-52 478.92,-52 472.92,-52 466.92,-46 466.92,-40 466.92,-40 466.92,-28 466.92,-28 466.92,-22 472.92,-16 478.92,-16 478.92,-16 565.08,-16 565.08,-16 571.08,-16 577.08,-22 577.08,-28 577.08,-28 577.08,-40 577.08,-40 577.08,-46 571.08,-52 565.08,-52"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="522" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">PetBreed / Vaccine</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="522" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">/ Study</text>
|
|
||||||
</g>
|
|
||||||
<!-- mpnotification -->
|
|
||||||
<g id="node30" class="node">
|
|
||||||
<title>mpnotification</title>
|
|
||||||
<path fill="#f8bbd9" stroke="black" d="M1348.99,-540.4C1348.99,-540.4 1283.01,-540.4 1283.01,-540.4 1277.01,-540.4 1271.01,-534.4 1271.01,-528.4 1271.01,-528.4 1271.01,-516.4 1271.01,-516.4 1271.01,-510.4 1277.01,-504.4 1283.01,-504.4 1283.01,-504.4 1348.99,-504.4 1348.99,-504.4 1354.99,-504.4 1360.99,-510.4 1360.99,-516.4 1360.99,-516.4 1360.99,-528.4 1360.99,-528.4 1360.99,-534.4 1354.99,-540.4 1348.99,-540.4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1316" y="-525.7" font-family="Helvetica,sans-Serif" font-size="11.00">MP Notification</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1316" y="-512.5" font-family="Helvetica,sans-Serif" font-size="11.00">(Webhooks)</text>
|
|
||||||
</g>
|
|
||||||
<!-- mpnotification->servicerequest -->
|
|
||||||
<g id="edge32" class="edge">
|
|
||||||
<title>mpnotification->servicerequest</title>
|
|
||||||
<path fill="none" stroke="#ad1457" d="M1275.48,-504.02C1275.48,-468.96 1275.48,-396 1275.48,-396 1275.48,-396 1187.67,-396 1187.67,-396"/>
|
|
||||||
<polygon fill="#ad1457" stroke="#ad1457" points="1187.67,-392.5 1177.67,-396 1187.67,-399.5 1187.67,-392.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1305" y="-478.3" font-family="Helvetica,sans-Serif" font-size="9.00">confirms</text>
|
|
||||||
</g>
|
|
||||||
<!-- google_sheets -->
|
|
||||||
<g id="node32" class="node">
|
|
||||||
<title>google_sheets</title>
|
|
||||||
<path fill="#ffe0b2" stroke="black" d="M1229.39,-52C1229.39,-52 1164.61,-52 1164.61,-52 1158.61,-52 1152.61,-46 1152.61,-40 1152.61,-40 1152.61,-28 1152.61,-28 1152.61,-22 1158.61,-16 1164.61,-16 1164.61,-16 1229.39,-16 1229.39,-16 1235.39,-16 1241.39,-22 1241.39,-28 1241.39,-28 1241.39,-40 1241.39,-40 1241.39,-46 1235.39,-52 1229.39,-52"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1197" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">Google Sheets</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1197" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">(Exports)</text>
|
|
||||||
</g>
|
|
||||||
<!-- afip -->
|
|
||||||
<g id="node34" class="node">
|
|
||||||
<title>afip</title>
|
|
||||||
<path fill="#ffe0b2" stroke="black" d="M1431.4,-52C1431.4,-52 1374.6,-52 1374.6,-52 1368.6,-52 1362.6,-46 1362.6,-40 1362.6,-40 1362.6,-28 1362.6,-28 1362.6,-22 1368.6,-16 1374.6,-16 1374.6,-16 1431.4,-16 1431.4,-16 1437.4,-16 1443.4,-22 1443.4,-28 1443.4,-28 1443.4,-40 1443.4,-40 1443.4,-46 1437.4,-52 1431.4,-52"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1403" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">AFIP</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1403" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">(Facturacion)</text>
|
|
||||||
</g>
|
|
||||||
<!-- taxpayer -->
|
|
||||||
<g id="node37" class="node">
|
|
||||||
<title>taxpayer</title>
|
|
||||||
<path fill="#fff9c4" stroke="black" d="M881.91,-52C881.91,-52 814.09,-52 814.09,-52 808.09,-52 802.09,-46 802.09,-40 802.09,-40 802.09,-28 802.09,-28 802.09,-22 808.09,-16 814.09,-16 814.09,-16 881.91,-16 881.91,-16 887.91,-16 893.91,-22 893.91,-28 893.91,-28 893.91,-40 893.91,-40 893.91,-46 887.91,-52 881.91,-52"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="848" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">TaxPayer</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="848" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">(Contribuyente)</text>
|
|
||||||
</g>
|
|
||||||
<!-- receipt->taxpayer -->
|
|
||||||
<g id="edge37" class="edge">
|
|
||||||
<title>receipt->taxpayer</title>
|
|
||||||
<path fill="none" stroke="#f9a825" d="M848,-150.97C848,-150.97 848,-63.89 848,-63.89"/>
|
|
||||||
<polygon fill="#f9a825" stroke="#f9a825" points="851.5,-63.89 848,-53.89 844.5,-63.89 851.5,-63.89"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="855" y="-125.1" font-family="Helvetica,sans-Serif" font-size="9.00">N:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- pos -->
|
|
||||||
<g id="node38" class="node">
|
|
||||||
<title>pos</title>
|
|
||||||
<path fill="#fff9c4" stroke="black" d="M998.58,-52C998.58,-52 923.42,-52 923.42,-52 917.42,-52 911.42,-46 911.42,-40 911.42,-40 911.42,-28 911.42,-28 911.42,-22 917.42,-16 923.42,-16 923.42,-16 998.58,-16 998.58,-16 1004.58,-16 1010.58,-22 1010.58,-28 1010.58,-28 1010.58,-40 1010.58,-40 1010.58,-46 1004.58,-52 998.58,-52"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="961" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">PointOfSales</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="961" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">(Punto de Venta)</text>
|
|
||||||
</g>
|
|
||||||
<!-- receipt->pos -->
|
|
||||||
<g id="edge38" class="edge">
|
|
||||||
<title>receipt->pos</title>
|
|
||||||
<path fill="none" stroke="#f9a825" d="M893.36,-169C910.17,-169 925.21,-169 925.21,-169 925.21,-169 925.21,-64 925.21,-64"/>
|
|
||||||
<polygon fill="#f9a825" stroke="#f9a825" points="928.71,-64 925.21,-54 921.71,-64 928.71,-64"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="930" y="-125.1" font-family="Helvetica,sans-Serif" font-size="9.00">N:1</text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 50 KiB |
@@ -1,184 +0,0 @@
|
|||||||
digraph FrontendArchitecture {
|
|
||||||
// Graph settings
|
|
||||||
rankdir=TB
|
|
||||||
compound=true
|
|
||||||
splines=ortho
|
|
||||||
node [shape=box, style="rounded,filled", fontname="Helvetica", fontsize=11]
|
|
||||||
edge [fontname="Helvetica", fontsize=9]
|
|
||||||
|
|
||||||
label="AMAR Mascotas - Frontend Architecture (Next.js)\n\n"
|
|
||||||
labelloc="t"
|
|
||||||
fontsize=16
|
|
||||||
fontname="Helvetica-Bold"
|
|
||||||
|
|
||||||
// Next.js Core Cluster
|
|
||||||
subgraph cluster_nextjs {
|
|
||||||
label="Next.js 13+ (App Router)"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#E3F2FD"
|
|
||||||
color="#1565C0"
|
|
||||||
|
|
||||||
app_router [label="App Router\n/app/*", fillcolor="#BBDEFB"]
|
|
||||||
layout [label="Layout Components\n(RootLayout, etc)", fillcolor="#BBDEFB"]
|
|
||||||
middleware [label="Middleware\n(Auth redirect)", fillcolor="#BBDEFB"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Public Frontend Cluster
|
|
||||||
subgraph cluster_public {
|
|
||||||
label="Public Frontend (/(frontend))"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#E8F5E9"
|
|
||||||
color="#2E7D32"
|
|
||||||
|
|
||||||
home [label="Home Page\n/", fillcolor="#C8E6C9"]
|
|
||||||
services_page [label="Services Catalog\n/servicios", fillcolor="#C8E6C9"]
|
|
||||||
cart_page [label="Cart\n/carrito", fillcolor="#C8E6C9"]
|
|
||||||
login_page [label="Login/Register\n/login", fillcolor="#C8E6C9"]
|
|
||||||
profile_page [label="User Profile\n/perfil", fillcolor="#C8E6C9"]
|
|
||||||
pets_page [label="My Pets\n/mascotas", fillcolor="#C8E6C9"]
|
|
||||||
requests_page [label="My Requests\n/solicitudes", fillcolor="#C8E6C9"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backoffice Cluster
|
|
||||||
subgraph cluster_backoffice {
|
|
||||||
label="Backoffice (/(backoffice)/admin)"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#FFF3E0"
|
|
||||||
color="#E65100"
|
|
||||||
|
|
||||||
admin_dash [label="Dashboard\n/admin", fillcolor="#FFE0B2"]
|
|
||||||
admin_visits [label="Visits Management\n/admin/visits", fillcolor="#FFE0B2"]
|
|
||||||
admin_pets [label="Pets Overview\n/admin/pets", fillcolor="#FFE0B2"]
|
|
||||||
admin_requests [label="Service Requests\n/admin/solicitudes", fillcolor="#FFE0B2"]
|
|
||||||
admin_calendar [label="Calendar View\n/admin/calendario", fillcolor="#FFE0B2"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Components Cluster
|
|
||||||
subgraph cluster_components {
|
|
||||||
label="Shared Components (/components)"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#F3E5F5"
|
|
||||||
color="#7B1FA2"
|
|
||||||
|
|
||||||
sidebar [label="Sidebar\nNavigation", fillcolor="#E1BEE7"]
|
|
||||||
navbar [label="NavbarBackoffice\nTop Bar", fillcolor="#E1BEE7"]
|
|
||||||
visits_section [label="VisitsSection\n(List + Actions)", fillcolor="#E1BEE7"]
|
|
||||||
drawer [label="VisitsDrawer\n(Side Panel)", fillcolor="#E1BEE7"]
|
|
||||||
tables [label="DataTable\nComponents", fillcolor="#E1BEE7"]
|
|
||||||
forms [label="Form Components\n(Pet, Visit, etc)", fillcolor="#E1BEE7"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Services Layer Cluster
|
|
||||||
subgraph cluster_services {
|
|
||||||
label="Services Layer (/services)"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#FFEBEE"
|
|
||||||
color="#C62828"
|
|
||||||
|
|
||||||
http_service [label="HttpService\n(Axios wrapper)", fillcolor="#FFCDD2"]
|
|
||||||
auth_api [label="authAPI\n(login/register)", fillcolor="#FFCDD2"]
|
|
||||||
visits_api [label="visitsAPI\n(CRUD visits)", fillcolor="#FFCDD2"]
|
|
||||||
orders_api [label="OrdersAPI\n(service requests)", fillcolor="#FFCDD2"]
|
|
||||||
petowners_api [label="petOwnersAPI\n(clients)", fillcolor="#FFCDD2"]
|
|
||||||
vets_api [label="VeterinariansAPI\n(professionals)", fillcolor="#FFCDD2"]
|
|
||||||
services_api [label="servicesAPI\n(catalog)", fillcolor="#FFCDD2"]
|
|
||||||
cart_api [label="CartAPI\n(shopping)", fillcolor="#FFCDD2"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// State Management Cluster
|
|
||||||
subgraph cluster_state {
|
|
||||||
label="State Management (/redux, /contexts)"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#E0F7FA"
|
|
||||||
color="#00838F"
|
|
||||||
|
|
||||||
redux_store [label="Redux Store\n(Global State)", fillcolor="#B2EBF2"]
|
|
||||||
auth_slice [label="Auth Slice\n(user, token)", fillcolor="#B2EBF2"]
|
|
||||||
visits_slice [label="Visits Slice\n(visit data)", fillcolor="#B2EBF2"]
|
|
||||||
cart_slice [label="Cart Slice\n(items)", fillcolor="#B2EBF2"]
|
|
||||||
auth_context [label="AuthContext\n(Provider)", fillcolor="#B2EBF2"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backend API Cluster (external)
|
|
||||||
subgraph cluster_backend {
|
|
||||||
label="Django Backend API"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#ECEFF1"
|
|
||||||
color="#455A64"
|
|
||||||
|
|
||||||
api_mascotas [label="/mascotas/api/v1/\n(Pets, Vets, Visits)", fillcolor="#CFD8DC"]
|
|
||||||
api_productos [label="/productos/\n(Services, Prices)", fillcolor="#CFD8DC"]
|
|
||||||
api_solicitudes [label="/solicitudes/\n(Requests)", fillcolor="#CFD8DC"]
|
|
||||||
api_auth [label="/api/token/\n(JWT Auth)", fillcolor="#CFD8DC"]
|
|
||||||
api_payments [label="/payments/\n(MercadoPago)", fillcolor="#CFD8DC"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// User Types
|
|
||||||
subgraph cluster_users {
|
|
||||||
label="User Types"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#FCE4EC"
|
|
||||||
color="#AD1457"
|
|
||||||
|
|
||||||
petowner_user [label="PetOwner\n(Cliente)", shape=ellipse, fillcolor="#F8BBD9"]
|
|
||||||
vet_user [label="Veterinarian\n(Profesional)", shape=ellipse, fillcolor="#F8BBD9"]
|
|
||||||
admin_user [label="Admin/Staff\n(Interno)", shape=ellipse, fillcolor="#F8BBD9"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Relationships - Router
|
|
||||||
app_router -> layout [color="#1565C0"]
|
|
||||||
app_router -> middleware [color="#1565C0"]
|
|
||||||
|
|
||||||
// Public pages
|
|
||||||
home -> services_page [color="#2E7D32"]
|
|
||||||
services_page -> cart_page [color="#2E7D32"]
|
|
||||||
cart_page -> login_page [label="if not auth", color="#2E7D32", style=dashed]
|
|
||||||
login_page -> profile_page [color="#2E7D32"]
|
|
||||||
profile_page -> pets_page [color="#2E7D32"]
|
|
||||||
profile_page -> requests_page [color="#2E7D32"]
|
|
||||||
|
|
||||||
// Backoffice pages
|
|
||||||
admin_dash -> admin_visits [color="#E65100"]
|
|
||||||
admin_dash -> admin_pets [color="#E65100"]
|
|
||||||
admin_dash -> admin_requests [color="#E65100"]
|
|
||||||
admin_dash -> admin_calendar [color="#E65100"]
|
|
||||||
|
|
||||||
// Components used by pages
|
|
||||||
admin_visits -> visits_section [color="#7B1FA2", style=dashed]
|
|
||||||
admin_visits -> drawer [color="#7B1FA2", style=dashed]
|
|
||||||
admin_dash -> sidebar [color="#7B1FA2", style=dashed]
|
|
||||||
admin_dash -> navbar [color="#7B1FA2", style=dashed]
|
|
||||||
|
|
||||||
// Services to APIs
|
|
||||||
http_service -> auth_api [color="#C62828"]
|
|
||||||
http_service -> visits_api [color="#C62828"]
|
|
||||||
http_service -> orders_api [color="#C62828"]
|
|
||||||
http_service -> petowners_api [color="#C62828"]
|
|
||||||
http_service -> vets_api [color="#C62828"]
|
|
||||||
http_service -> services_api [color="#C62828"]
|
|
||||||
http_service -> cart_api [color="#C62828"]
|
|
||||||
|
|
||||||
// Services to Backend
|
|
||||||
auth_api -> api_auth [label="POST /api/token/", color="#455A64"]
|
|
||||||
visits_api -> api_mascotas [label="CRUD /vet-visits/", color="#455A64"]
|
|
||||||
orders_api -> api_solicitudes [label="CRUD /service-requests/", color="#455A64"]
|
|
||||||
petowners_api -> api_mascotas [label="CRUD /pet-owners/", color="#455A64"]
|
|
||||||
vets_api -> api_mascotas [label="GET /veterinarians/", color="#455A64"]
|
|
||||||
services_api -> api_productos [label="GET /services/", color="#455A64"]
|
|
||||||
cart_api -> api_productos [label="CRUD /cart/", color="#455A64"]
|
|
||||||
|
|
||||||
// State connections
|
|
||||||
auth_slice -> redux_store [color="#00838F"]
|
|
||||||
visits_slice -> redux_store [color="#00838F"]
|
|
||||||
cart_slice -> redux_store [color="#00838F"]
|
|
||||||
auth_context -> redux_store [color="#00838F", style=dashed]
|
|
||||||
|
|
||||||
// User access paths
|
|
||||||
petowner_user -> home [label="public access", color="#AD1457"]
|
|
||||||
petowner_user -> profile_page [label="authenticated", color="#AD1457", style=dashed]
|
|
||||||
vet_user -> admin_dash [label="backoffice", color="#AD1457"]
|
|
||||||
admin_user -> admin_dash [label="full access", color="#AD1457"]
|
|
||||||
|
|
||||||
// API routing through HttpService
|
|
||||||
http_service -> api_auth [label="JWT refresh", color="#455A64", style=dashed]
|
|
||||||
}
|
|
||||||
@@ -1,595 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
|
||||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<!-- Generated by graphviz version 14.0.5 (0)
|
|
||||||
-->
|
|
||||||
<!-- Title: FrontendArchitecture Pages: 1 -->
|
|
||||||
<svg width="2391pt" height="715pt"
|
|
||||||
viewBox="0.00 0.00 2391.00 715.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
||||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 710.85)">
|
|
||||||
<title>FrontendArchitecture</title>
|
|
||||||
<polygon fill="white" stroke="none" points="-4,4 -4,-710.85 2387,-710.85 2387,4 -4,4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1191.5" y="-687.65" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">AMAR Mascotas - Frontend Architecture (Next.js)</text>
|
|
||||||
<g id="clust1" class="cluster">
|
|
||||||
<title>cluster_nextjs</title>
|
|
||||||
<path fill="#e3f2fd" stroke="#1565c0" d="M20,-452.8C20,-452.8 230,-452.8 230,-452.8 236,-452.8 242,-458.8 242,-464.8 242,-464.8 242,-634.32 242,-634.32 242,-640.32 236,-646.32 230,-646.32 230,-646.32 20,-646.32 20,-646.32 14,-646.32 8,-640.32 8,-634.32 8,-634.32 8,-464.8 8,-464.8 8,-458.8 14,-452.8 20,-452.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="125" y="-627.12" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Next.js 13+ (App Router)</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust2" class="cluster">
|
|
||||||
<title>cluster_public</title>
|
|
||||||
<path fill="#e8f5e9" stroke="#2e7d32" d="M262,-8C262,-8 454,-8 454,-8 460,-8 466,-14 466,-20 466,-20 466,-520 466,-520 466,-526 460,-532 454,-532 454,-532 262,-532 262,-532 256,-532 250,-526 250,-520 250,-520 250,-20 250,-20 250,-14 256,-8 262,-8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="358" y="-512.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Public Frontend (/(frontend))</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust3" class="cluster">
|
|
||||||
<title>cluster_backoffice</title>
|
|
||||||
<path fill="#fff3e0" stroke="#e65100" d="M647,-344.8C647,-344.8 1095,-344.8 1095,-344.8 1101,-344.8 1107,-350.8 1107,-356.8 1107,-356.8 1107,-520 1107,-520 1107,-526 1101,-532 1095,-532 1095,-532 647,-532 647,-532 641,-532 635,-526 635,-520 635,-520 635,-356.8 635,-356.8 635,-350.8 641,-344.8 647,-344.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="871" y="-512.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Backoffice (/(backoffice)/admin)</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust4" class="cluster">
|
|
||||||
<title>cluster_components</title>
|
|
||||||
<path fill="#f3e5f5" stroke="#7b1fa2" d="M486,-236.8C486,-236.8 1091,-236.8 1091,-236.8 1097,-236.8 1103,-242.8 1103,-248.8 1103,-248.8 1103,-304 1103,-304 1103,-310 1097,-316 1091,-316 1091,-316 486,-316 486,-316 480,-316 474,-310 474,-304 474,-304 474,-248.8 474,-248.8 474,-242.8 480,-236.8 486,-236.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="788.5" y="-296.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Shared Components (/components)</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust5" class="cluster">
|
|
||||||
<title>cluster_services</title>
|
|
||||||
<path fill="#ffebee" stroke="#c62828" d="M1222,-452.8C1222,-452.8 1922,-452.8 1922,-452.8 1928,-452.8 1934,-458.8 1934,-464.8 1934,-464.8 1934,-634.32 1934,-634.32 1934,-640.32 1928,-646.32 1922,-646.32 1922,-646.32 1222,-646.32 1222,-646.32 1216,-646.32 1210,-640.32 1210,-634.32 1210,-634.32 1210,-464.8 1210,-464.8 1210,-458.8 1216,-452.8 1222,-452.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1572" y="-627.12" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Services Layer (/services)</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust6" class="cluster">
|
|
||||||
<title>cluster_state</title>
|
|
||||||
<path fill="#e0f7fa" stroke="#00838f" d="M2031,-452.8C2031,-452.8 2363,-452.8 2363,-452.8 2369,-452.8 2375,-458.8 2375,-464.8 2375,-464.8 2375,-634.32 2375,-634.32 2375,-640.32 2369,-646.32 2363,-646.32 2363,-646.32 2031,-646.32 2031,-646.32 2025,-646.32 2019,-640.32 2019,-634.32 2019,-634.32 2019,-464.8 2019,-464.8 2019,-458.8 2025,-452.8 2031,-452.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2197" y="-627.12" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">State Management (/redux, /contexts)</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust7" class="cluster">
|
|
||||||
<title>cluster_backend</title>
|
|
||||||
<path fill="#eceff1" stroke="#455a64" d="M1382,-344.8C1382,-344.8 1892,-344.8 1892,-344.8 1898,-344.8 1904,-350.8 1904,-356.8 1904,-356.8 1904,-412 1904,-412 1904,-418 1898,-424 1892,-424 1892,-424 1382,-424 1382,-424 1376,-424 1370,-418 1370,-412 1370,-412 1370,-356.8 1370,-356.8 1370,-350.8 1376,-344.8 1382,-344.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1637" y="-404.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Django Backend API</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust8" class="cluster">
|
|
||||||
<title>cluster_users</title>
|
|
||||||
<path fill="#fce4ec" stroke="#ad1457" d="M824,-560.8C824,-560.8 1158,-560.8 1158,-560.8 1164,-560.8 1170,-566.8 1170,-572.8 1170,-572.8 1170,-640.65 1170,-640.65 1170,-646.65 1164,-652.65 1158,-652.65 1158,-652.65 824,-652.65 824,-652.65 818,-652.65 812,-646.65 812,-640.65 812,-640.65 812,-572.8 812,-572.8 812,-566.8 818,-560.8 824,-560.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="991" y="-633.45" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">User Types</text>
|
|
||||||
</g>
|
|
||||||
<!-- app_router -->
|
|
||||||
<g id="node1" class="node">
|
|
||||||
<title>app_router</title>
|
|
||||||
<path fill="#bbdefb" stroke="black" d="M155.82,-611.12C155.82,-611.12 108.18,-611.12 108.18,-611.12 102.18,-611.12 96.18,-605.12 96.18,-599.12 96.18,-599.12 96.18,-587.12 96.18,-587.12 96.18,-581.12 102.18,-575.12 108.18,-575.12 108.18,-575.12 155.82,-575.12 155.82,-575.12 161.82,-575.12 167.82,-581.12 167.82,-587.12 167.82,-587.12 167.82,-599.12 167.82,-599.12 167.82,-605.12 161.82,-611.12 155.82,-611.12"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="132" y="-596.42" font-family="Helvetica,sans-Serif" font-size="11.00">App Router</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="132" y="-583.22" font-family="Helvetica,sans-Serif" font-size="11.00">/app/*</text>
|
|
||||||
</g>
|
|
||||||
<!-- layout -->
|
|
||||||
<g id="node2" class="node">
|
|
||||||
<title>layout</title>
|
|
||||||
<path fill="#bbdefb" stroke="black" d="M118.23,-496.8C118.23,-496.8 27.77,-496.8 27.77,-496.8 21.77,-496.8 15.77,-490.8 15.77,-484.8 15.77,-484.8 15.77,-472.8 15.77,-472.8 15.77,-466.8 21.77,-460.8 27.77,-460.8 27.77,-460.8 118.23,-460.8 118.23,-460.8 124.23,-460.8 130.23,-466.8 130.23,-472.8 130.23,-472.8 130.23,-484.8 130.23,-484.8 130.23,-490.8 124.23,-496.8 118.23,-496.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="73" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">Layout Components</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="73" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">(RootLayout, etc)</text>
|
|
||||||
</g>
|
|
||||||
<!-- app_router->layout -->
|
|
||||||
<g id="edge1" class="edge">
|
|
||||||
<title>app_router->layout</title>
|
|
||||||
<path fill="none" stroke="#1565c0" d="M113.2,-574.76C113.2,-574.76 113.2,-508.64 113.2,-508.64"/>
|
|
||||||
<polygon fill="#1565c0" stroke="#1565c0" points="116.7,-508.64 113.2,-498.64 109.7,-508.64 116.7,-508.64"/>
|
|
||||||
</g>
|
|
||||||
<!-- middleware -->
|
|
||||||
<g id="node3" class="node">
|
|
||||||
<title>middleware</title>
|
|
||||||
<path fill="#bbdefb" stroke="black" d="M221.84,-496.8C221.84,-496.8 160.16,-496.8 160.16,-496.8 154.16,-496.8 148.16,-490.8 148.16,-484.8 148.16,-484.8 148.16,-472.8 148.16,-472.8 148.16,-466.8 154.16,-460.8 160.16,-460.8 160.16,-460.8 221.84,-460.8 221.84,-460.8 227.84,-460.8 233.84,-466.8 233.84,-472.8 233.84,-472.8 233.84,-484.8 233.84,-484.8 233.84,-490.8 227.84,-496.8 221.84,-496.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="191" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">Middleware</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="191" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">(Auth redirect)</text>
|
|
||||||
</g>
|
|
||||||
<!-- app_router->middleware -->
|
|
||||||
<g id="edge2" class="edge">
|
|
||||||
<title>app_router->middleware</title>
|
|
||||||
<path fill="none" stroke="#1565c0" d="M157.99,-574.76C157.99,-574.76 157.99,-508.64 157.99,-508.64"/>
|
|
||||||
<polygon fill="#1565c0" stroke="#1565c0" points="161.49,-508.64 157.99,-498.64 154.49,-508.64 161.49,-508.64"/>
|
|
||||||
</g>
|
|
||||||
<!-- home -->
|
|
||||||
<g id="node4" class="node">
|
|
||||||
<title>home</title>
|
|
||||||
<path fill="#c8e6c9" stroke="black" d="M442.04,-496.8C442.04,-496.8 391.96,-496.8 391.96,-496.8 385.96,-496.8 379.96,-490.8 379.96,-484.8 379.96,-484.8 379.96,-472.8 379.96,-472.8 379.96,-466.8 385.96,-460.8 391.96,-460.8 391.96,-460.8 442.04,-460.8 442.04,-460.8 448.04,-460.8 454.04,-466.8 454.04,-472.8 454.04,-472.8 454.04,-484.8 454.04,-484.8 454.04,-490.8 448.04,-496.8 442.04,-496.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="417" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">Home Page</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="417" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">/</text>
|
|
||||||
</g>
|
|
||||||
<!-- services_page -->
|
|
||||||
<g id="node5" class="node">
|
|
||||||
<title>services_page</title>
|
|
||||||
<path fill="#c8e6c9" stroke="black" d="M445.57,-388.8C445.57,-388.8 370.43,-388.8 370.43,-388.8 364.43,-388.8 358.43,-382.8 358.43,-376.8 358.43,-376.8 358.43,-364.8 358.43,-364.8 358.43,-358.8 364.43,-352.8 370.43,-352.8 370.43,-352.8 445.57,-352.8 445.57,-352.8 451.57,-352.8 457.57,-358.8 457.57,-364.8 457.57,-364.8 457.57,-376.8 457.57,-376.8 457.57,-382.8 451.57,-388.8 445.57,-388.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="408" y="-374.1" font-family="Helvetica,sans-Serif" font-size="11.00">Services Catalog</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="408" y="-360.9" font-family="Helvetica,sans-Serif" font-size="11.00">/servicios</text>
|
|
||||||
</g>
|
|
||||||
<!-- home->services_page -->
|
|
||||||
<g id="edge3" class="edge">
|
|
||||||
<title>home->services_page</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M417,-460.48C417,-460.48 417,-400.72 417,-400.72"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="420.5,-400.72 417,-390.72 413.5,-400.72 420.5,-400.72"/>
|
|
||||||
</g>
|
|
||||||
<!-- cart_page -->
|
|
||||||
<g id="node6" class="node">
|
|
||||||
<title>cart_page</title>
|
|
||||||
<path fill="#c8e6c9" stroke="black" d="M425,-280.8C425,-280.8 395,-280.8 395,-280.8 389,-280.8 383,-274.8 383,-268.8 383,-268.8 383,-256.8 383,-256.8 383,-250.8 389,-244.8 395,-244.8 395,-244.8 425,-244.8 425,-244.8 431,-244.8 437,-250.8 437,-256.8 437,-256.8 437,-268.8 437,-268.8 437,-274.8 431,-280.8 425,-280.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="410" y="-266.1" font-family="Helvetica,sans-Serif" font-size="11.00">Cart</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="410" y="-252.9" font-family="Helvetica,sans-Serif" font-size="11.00">/carrito</text>
|
|
||||||
</g>
|
|
||||||
<!-- services_page->cart_page -->
|
|
||||||
<g id="edge4" class="edge">
|
|
||||||
<title>services_page->cart_page</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M410,-352.48C410,-352.48 410,-292.72 410,-292.72"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="413.5,-292.72 410,-282.72 406.5,-292.72 413.5,-292.72"/>
|
|
||||||
</g>
|
|
||||||
<!-- login_page -->
|
|
||||||
<g id="node7" class="node">
|
|
||||||
<title>login_page</title>
|
|
||||||
<path fill="#c8e6c9" stroke="black" d="M444.47,-198C444.47,-198 381.53,-198 381.53,-198 375.53,-198 369.53,-192 369.53,-186 369.53,-186 369.53,-174 369.53,-174 369.53,-168 375.53,-162 381.53,-162 381.53,-162 444.47,-162 444.47,-162 450.47,-162 456.47,-168 456.47,-174 456.47,-174 456.47,-186 456.47,-186 456.47,-192 450.47,-198 444.47,-198"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="413" y="-183.3" font-family="Helvetica,sans-Serif" font-size="11.00">Login/Register</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="413" y="-170.1" font-family="Helvetica,sans-Serif" font-size="11.00">/login</text>
|
|
||||||
</g>
|
|
||||||
<!-- cart_page->login_page -->
|
|
||||||
<g id="edge5" class="edge">
|
|
||||||
<title>cart_page->login_page</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" stroke-dasharray="5,2" d="M410,-244.42C410,-244.42 410,-209.84 410,-209.84"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="413.5,-209.84 410,-199.84 406.5,-209.84 413.5,-209.84"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="432.76" y="-218.7" font-family="Helvetica,sans-Serif" font-size="9.00">if not auth</text>
|
|
||||||
</g>
|
|
||||||
<!-- profile_page -->
|
|
||||||
<g id="node8" class="node">
|
|
||||||
<title>profile_page</title>
|
|
||||||
<path fill="#c8e6c9" stroke="black" d="M438.73,-125C438.73,-125 389.27,-125 389.27,-125 383.27,-125 377.27,-119 377.27,-113 377.27,-113 377.27,-101 377.27,-101 377.27,-95 383.27,-89 389.27,-89 389.27,-89 438.73,-89 438.73,-89 444.73,-89 450.73,-95 450.73,-101 450.73,-101 450.73,-113 450.73,-113 450.73,-119 444.73,-125 438.73,-125"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="414" y="-110.3" font-family="Helvetica,sans-Serif" font-size="11.00">User Profile</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="414" y="-97.1" font-family="Helvetica,sans-Serif" font-size="11.00">/perfil</text>
|
|
||||||
</g>
|
|
||||||
<!-- login_page->profile_page -->
|
|
||||||
<g id="edge6" class="edge">
|
|
||||||
<title>login_page->profile_page</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M414,-161.58C414,-161.58 414,-136.93 414,-136.93"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="417.5,-136.93 414,-126.93 410.5,-136.93 417.5,-136.93"/>
|
|
||||||
</g>
|
|
||||||
<!-- pets_page -->
|
|
||||||
<g id="node9" class="node">
|
|
||||||
<title>pets_page</title>
|
|
||||||
<path fill="#c8e6c9" stroke="black" d="M347.06,-52C347.06,-52 304.94,-52 304.94,-52 298.94,-52 292.94,-46 292.94,-40 292.94,-40 292.94,-28 292.94,-28 292.94,-22 298.94,-16 304.94,-16 304.94,-16 347.06,-16 347.06,-16 353.06,-16 359.06,-22 359.06,-28 359.06,-28 359.06,-40 359.06,-40 359.06,-46 353.06,-52 347.06,-52"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="326" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">My Pets</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="326" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">/mascotas</text>
|
|
||||||
</g>
|
|
||||||
<!-- profile_page->pets_page -->
|
|
||||||
<g id="edge7" class="edge">
|
|
||||||
<title>profile_page->pets_page</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M376.8,-107C352.66,-107 325.68,-107 325.68,-107 325.68,-107 325.68,-63.93 325.68,-63.93"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="329.18,-63.93 325.68,-53.93 322.18,-63.93 329.18,-63.93"/>
|
|
||||||
</g>
|
|
||||||
<!-- requests_page -->
|
|
||||||
<g id="node10" class="node">
|
|
||||||
<title>requests_page</title>
|
|
||||||
<path fill="#c8e6c9" stroke="black" d="M445.09,-52C445.09,-52 388.91,-52 388.91,-52 382.91,-52 376.91,-46 376.91,-40 376.91,-40 376.91,-28 376.91,-28 376.91,-22 382.91,-16 388.91,-16 388.91,-16 445.09,-16 445.09,-16 451.09,-16 457.09,-22 457.09,-28 457.09,-28 457.09,-40 457.09,-40 457.09,-46 451.09,-52 445.09,-52"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="417" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">My Requests</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="417" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">/solicitudes</text>
|
|
||||||
</g>
|
|
||||||
<!-- profile_page->requests_page -->
|
|
||||||
<g id="edge8" class="edge">
|
|
||||||
<title>profile_page->requests_page</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M414,-88.58C414,-88.58 414,-63.93 414,-63.93"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="417.5,-63.93 414,-53.93 410.5,-63.93 417.5,-63.93"/>
|
|
||||||
</g>
|
|
||||||
<!-- admin_dash -->
|
|
||||||
<g id="node11" class="node">
|
|
||||||
<title>admin_dash</title>
|
|
||||||
<path fill="#ffe0b2" stroke="black" d="M892.91,-496.8C892.91,-496.8 847.09,-496.8 847.09,-496.8 841.09,-496.8 835.09,-490.8 835.09,-484.8 835.09,-484.8 835.09,-472.8 835.09,-472.8 835.09,-466.8 841.09,-460.8 847.09,-460.8 847.09,-460.8 892.91,-460.8 892.91,-460.8 898.91,-460.8 904.91,-466.8 904.91,-472.8 904.91,-472.8 904.91,-484.8 904.91,-484.8 904.91,-490.8 898.91,-496.8 892.91,-496.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="870" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">Dashboard</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="870" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">/admin</text>
|
|
||||||
</g>
|
|
||||||
<!-- admin_visits -->
|
|
||||||
<g id="node12" class="node">
|
|
||||||
<title>admin_visits</title>
|
|
||||||
<path fill="#ffe0b2" stroke="black" d="M740.77,-388.8C740.77,-388.8 655.23,-388.8 655.23,-388.8 649.23,-388.8 643.23,-382.8 643.23,-376.8 643.23,-376.8 643.23,-364.8 643.23,-364.8 643.23,-358.8 649.23,-352.8 655.23,-352.8 655.23,-352.8 740.77,-352.8 740.77,-352.8 746.77,-352.8 752.77,-358.8 752.77,-364.8 752.77,-364.8 752.77,-376.8 752.77,-376.8 752.77,-382.8 746.77,-388.8 740.77,-388.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="698" y="-374.1" font-family="Helvetica,sans-Serif" font-size="11.00">Visits Management</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="698" y="-360.9" font-family="Helvetica,sans-Serif" font-size="11.00">/admin/visits</text>
|
|
||||||
</g>
|
|
||||||
<!-- admin_dash->admin_visits -->
|
|
||||||
<g id="edge9" class="edge">
|
|
||||||
<title>admin_dash->admin_visits</title>
|
|
||||||
<path fill="none" stroke="#e65100" d="M834.78,-468C784.46,-468 698,-468 698,-468 698,-468 698,-400.6 698,-400.6"/>
|
|
||||||
<polygon fill="#e65100" stroke="#e65100" points="701.5,-400.6 698,-390.6 694.5,-400.6 701.5,-400.6"/>
|
|
||||||
</g>
|
|
||||||
<!-- admin_pets -->
|
|
||||||
<g id="node13" class="node">
|
|
||||||
<title>admin_pets</title>
|
|
||||||
<path fill="#ffe0b2" stroke="black" d="M845.45,-388.8C845.45,-388.8 782.55,-388.8 782.55,-388.8 776.55,-388.8 770.55,-382.8 770.55,-376.8 770.55,-376.8 770.55,-364.8 770.55,-364.8 770.55,-358.8 776.55,-352.8 782.55,-352.8 782.55,-352.8 845.45,-352.8 845.45,-352.8 851.45,-352.8 857.45,-358.8 857.45,-364.8 857.45,-364.8 857.45,-376.8 857.45,-376.8 857.45,-382.8 851.45,-388.8 845.45,-388.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="814" y="-374.1" font-family="Helvetica,sans-Serif" font-size="11.00">Pets Overview</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="814" y="-360.9" font-family="Helvetica,sans-Serif" font-size="11.00">/admin/pets</text>
|
|
||||||
</g>
|
|
||||||
<!-- admin_dash->admin_pets -->
|
|
||||||
<g id="edge10" class="edge">
|
|
||||||
<title>admin_dash->admin_pets</title>
|
|
||||||
<path fill="none" stroke="#e65100" d="M846.27,-460.48C846.27,-460.48 846.27,-400.72 846.27,-400.72"/>
|
|
||||||
<polygon fill="#e65100" stroke="#e65100" points="849.77,-400.72 846.27,-390.72 842.77,-400.72 849.77,-400.72"/>
|
|
||||||
</g>
|
|
||||||
<!-- admin_requests -->
|
|
||||||
<g id="node14" class="node">
|
|
||||||
<title>admin_requests</title>
|
|
||||||
<path fill="#ffe0b2" stroke="black" d="M966.72,-388.8C966.72,-388.8 887.28,-388.8 887.28,-388.8 881.28,-388.8 875.28,-382.8 875.28,-376.8 875.28,-376.8 875.28,-364.8 875.28,-364.8 875.28,-358.8 881.28,-352.8 887.28,-352.8 887.28,-352.8 966.72,-352.8 966.72,-352.8 972.72,-352.8 978.72,-358.8 978.72,-364.8 978.72,-364.8 978.72,-376.8 978.72,-376.8 978.72,-382.8 972.72,-388.8 966.72,-388.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="927" y="-374.1" font-family="Helvetica,sans-Serif" font-size="11.00">Service Requests</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="927" y="-360.9" font-family="Helvetica,sans-Serif" font-size="11.00">/admin/solicitudes</text>
|
|
||||||
</g>
|
|
||||||
<!-- admin_dash->admin_requests -->
|
|
||||||
<g id="edge11" class="edge">
|
|
||||||
<title>admin_dash->admin_requests</title>
|
|
||||||
<path fill="none" stroke="#e65100" d="M890.1,-460.48C890.1,-460.48 890.1,-400.72 890.1,-400.72"/>
|
|
||||||
<polygon fill="#e65100" stroke="#e65100" points="893.6,-400.72 890.1,-390.72 886.6,-400.72 893.6,-400.72"/>
|
|
||||||
</g>
|
|
||||||
<!-- admin_calendar -->
|
|
||||||
<g id="node15" class="node">
|
|
||||||
<title>admin_calendar</title>
|
|
||||||
<path fill="#ffe0b2" stroke="black" d="M1087.41,-388.8C1087.41,-388.8 1008.59,-388.8 1008.59,-388.8 1002.59,-388.8 996.59,-382.8 996.59,-376.8 996.59,-376.8 996.59,-364.8 996.59,-364.8 996.59,-358.8 1002.59,-352.8 1008.59,-352.8 1008.59,-352.8 1087.41,-352.8 1087.41,-352.8 1093.41,-352.8 1099.41,-358.8 1099.41,-364.8 1099.41,-364.8 1099.41,-376.8 1099.41,-376.8 1099.41,-382.8 1093.41,-388.8 1087.41,-388.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1048" y="-374.1" font-family="Helvetica,sans-Serif" font-size="11.00">Calendar View</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1048" y="-360.9" font-family="Helvetica,sans-Serif" font-size="11.00">/admin/calendario</text>
|
|
||||||
</g>
|
|
||||||
<!-- admin_dash->admin_calendar -->
|
|
||||||
<g id="edge12" class="edge">
|
|
||||||
<title>admin_dash->admin_calendar</title>
|
|
||||||
<path fill="none" stroke="#e65100" d="M905.19,-470C949.1,-470 1018.5,-470 1018.5,-470 1018.5,-470 1018.5,-400.51 1018.5,-400.51"/>
|
|
||||||
<polygon fill="#e65100" stroke="#e65100" points="1022,-400.51 1018.5,-390.51 1015,-400.51 1022,-400.51"/>
|
|
||||||
</g>
|
|
||||||
<!-- sidebar -->
|
|
||||||
<g id="node16" class="node">
|
|
||||||
<title>sidebar</title>
|
|
||||||
<path fill="#e1bee7" stroke="black" d="M537.99,-280.8C537.99,-280.8 494.01,-280.8 494.01,-280.8 488.01,-280.8 482.01,-274.8 482.01,-268.8 482.01,-268.8 482.01,-256.8 482.01,-256.8 482.01,-250.8 488.01,-244.8 494.01,-244.8 494.01,-244.8 537.99,-244.8 537.99,-244.8 543.99,-244.8 549.99,-250.8 549.99,-256.8 549.99,-256.8 549.99,-268.8 549.99,-268.8 549.99,-274.8 543.99,-280.8 537.99,-280.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="516" y="-266.1" font-family="Helvetica,sans-Serif" font-size="11.00">Sidebar</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="516" y="-252.9" font-family="Helvetica,sans-Serif" font-size="11.00">Navigation</text>
|
|
||||||
</g>
|
|
||||||
<!-- admin_dash->sidebar -->
|
|
||||||
<g id="edge15" class="edge">
|
|
||||||
<title>admin_dash->sidebar</title>
|
|
||||||
<path fill="none" stroke="#7b1fa2" stroke-dasharray="5,2" d="M834.76,-482C745.22,-482 516,-482 516,-482 516,-482 516,-292.63 516,-292.63"/>
|
|
||||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="519.5,-292.63 516,-282.63 512.5,-292.63 519.5,-292.63"/>
|
|
||||||
</g>
|
|
||||||
<!-- navbar -->
|
|
||||||
<g id="node17" class="node">
|
|
||||||
<title>navbar</title>
|
|
||||||
<path fill="#e1bee7" stroke="black" d="M658.1,-280.8C658.1,-280.8 579.9,-280.8 579.9,-280.8 573.9,-280.8 567.9,-274.8 567.9,-268.8 567.9,-268.8 567.9,-256.8 567.9,-256.8 567.9,-250.8 573.9,-244.8 579.9,-244.8 579.9,-244.8 658.1,-244.8 658.1,-244.8 664.1,-244.8 670.1,-250.8 670.1,-256.8 670.1,-256.8 670.1,-268.8 670.1,-268.8 670.1,-274.8 664.1,-280.8 658.1,-280.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="619" y="-266.1" font-family="Helvetica,sans-Serif" font-size="11.00">NavbarBackoffice</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="619" y="-252.9" font-family="Helvetica,sans-Serif" font-size="11.00">Top Bar</text>
|
|
||||||
</g>
|
|
||||||
<!-- admin_dash->navbar -->
|
|
||||||
<g id="edge16" class="edge">
|
|
||||||
<title>admin_dash->navbar</title>
|
|
||||||
<path fill="none" stroke="#7b1fa2" stroke-dasharray="5,2" d="M834.61,-475C762.55,-475 605.56,-475 605.56,-475 605.56,-475 605.56,-292.77 605.56,-292.77"/>
|
|
||||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="609.06,-292.77 605.56,-282.77 602.06,-292.77 609.06,-292.77"/>
|
|
||||||
</g>
|
|
||||||
<!-- visits_section -->
|
|
||||||
<g id="node18" class="node">
|
|
||||||
<title>visits_section</title>
|
|
||||||
<path fill="#e1bee7" stroke="black" d="M765.53,-280.8C765.53,-280.8 700.47,-280.8 700.47,-280.8 694.47,-280.8 688.47,-274.8 688.47,-268.8 688.47,-268.8 688.47,-256.8 688.47,-256.8 688.47,-250.8 694.47,-244.8 700.47,-244.8 700.47,-244.8 765.53,-244.8 765.53,-244.8 771.53,-244.8 777.53,-250.8 777.53,-256.8 777.53,-256.8 777.53,-268.8 777.53,-268.8 777.53,-274.8 771.53,-280.8 765.53,-280.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="733" y="-266.1" font-family="Helvetica,sans-Serif" font-size="11.00">VisitsSection</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="733" y="-252.9" font-family="Helvetica,sans-Serif" font-size="11.00">(List + Actions)</text>
|
|
||||||
</g>
|
|
||||||
<!-- admin_visits->visits_section -->
|
|
||||||
<g id="edge13" class="edge">
|
|
||||||
<title>admin_visits->visits_section</title>
|
|
||||||
<path fill="none" stroke="#7b1fa2" stroke-dasharray="5,2" d="M709.91,-352.48C709.91,-352.48 709.91,-292.72 709.91,-292.72"/>
|
|
||||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="713.41,-292.72 709.91,-282.72 706.41,-292.72 713.41,-292.72"/>
|
|
||||||
</g>
|
|
||||||
<!-- drawer -->
|
|
||||||
<g id="node19" class="node">
|
|
||||||
<title>drawer</title>
|
|
||||||
<path fill="#e1bee7" stroke="black" d="M860.86,-280.8C860.86,-280.8 807.14,-280.8 807.14,-280.8 801.14,-280.8 795.14,-274.8 795.14,-268.8 795.14,-268.8 795.14,-256.8 795.14,-256.8 795.14,-250.8 801.14,-244.8 807.14,-244.8 807.14,-244.8 860.86,-244.8 860.86,-244.8 866.86,-244.8 872.86,-250.8 872.86,-256.8 872.86,-256.8 872.86,-268.8 872.86,-268.8 872.86,-274.8 866.86,-280.8 860.86,-280.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="834" y="-266.1" font-family="Helvetica,sans-Serif" font-size="11.00">VisitsDrawer</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="834" y="-252.9" font-family="Helvetica,sans-Serif" font-size="11.00">(Side Panel)</text>
|
|
||||||
</g>
|
|
||||||
<!-- admin_visits->drawer -->
|
|
||||||
<g id="edge14" class="edge">
|
|
||||||
<title>admin_visits->drawer</title>
|
|
||||||
<path fill="none" stroke="#7b1fa2" stroke-dasharray="5,2" d="M731.34,-352.59C731.34,-336.98 731.34,-317 731.34,-317 731.34,-317 826.29,-317 826.29,-317 826.29,-317 826.29,-292.66 826.29,-292.66"/>
|
|
||||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="829.79,-292.66 826.29,-282.66 822.79,-292.66 829.79,-292.66"/>
|
|
||||||
</g>
|
|
||||||
<!-- tables -->
|
|
||||||
<g id="node20" class="node">
|
|
||||||
<title>tables</title>
|
|
||||||
<path fill="#e1bee7" stroke="black" d="M957.18,-280.8C957.18,-280.8 902.82,-280.8 902.82,-280.8 896.82,-280.8 890.82,-274.8 890.82,-268.8 890.82,-268.8 890.82,-256.8 890.82,-256.8 890.82,-250.8 896.82,-244.8 902.82,-244.8 902.82,-244.8 957.18,-244.8 957.18,-244.8 963.18,-244.8 969.18,-250.8 969.18,-256.8 969.18,-256.8 969.18,-268.8 969.18,-268.8 969.18,-274.8 963.18,-280.8 957.18,-280.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="930" y="-266.1" font-family="Helvetica,sans-Serif" font-size="11.00">DataTable</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="930" y="-252.9" font-family="Helvetica,sans-Serif" font-size="11.00">Components</text>
|
|
||||||
</g>
|
|
||||||
<!-- forms -->
|
|
||||||
<g id="node21" class="node">
|
|
||||||
<title>forms</title>
|
|
||||||
<path fill="#e1bee7" stroke="black" d="M1082.54,-280.8C1082.54,-280.8 999.46,-280.8 999.46,-280.8 993.46,-280.8 987.46,-274.8 987.46,-268.8 987.46,-268.8 987.46,-256.8 987.46,-256.8 987.46,-250.8 993.46,-244.8 999.46,-244.8 999.46,-244.8 1082.54,-244.8 1082.54,-244.8 1088.54,-244.8 1094.54,-250.8 1094.54,-256.8 1094.54,-256.8 1094.54,-268.8 1094.54,-268.8 1094.54,-274.8 1088.54,-280.8 1082.54,-280.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1041" y="-266.1" font-family="Helvetica,sans-Serif" font-size="11.00">Form Components</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1041" y="-252.9" font-family="Helvetica,sans-Serif" font-size="11.00">(Pet, Visit, etc)</text>
|
|
||||||
</g>
|
|
||||||
<!-- http_service -->
|
|
||||||
<g id="node22" class="node">
|
|
||||||
<title>http_service</title>
|
|
||||||
<path fill="#ffcdd2" stroke="black" d="M1644.51,-611.12C1644.51,-611.12 1575.49,-611.12 1575.49,-611.12 1569.49,-611.12 1563.49,-605.12 1563.49,-599.12 1563.49,-599.12 1563.49,-587.12 1563.49,-587.12 1563.49,-581.12 1569.49,-575.12 1575.49,-575.12 1575.49,-575.12 1644.51,-575.12 1644.51,-575.12 1650.51,-575.12 1656.51,-581.12 1656.51,-587.12 1656.51,-587.12 1656.51,-599.12 1656.51,-599.12 1656.51,-605.12 1650.51,-611.12 1644.51,-611.12"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1610" y="-596.42" font-family="Helvetica,sans-Serif" font-size="11.00">HttpService</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1610" y="-583.22" font-family="Helvetica,sans-Serif" font-size="11.00">(Axios wrapper)</text>
|
|
||||||
</g>
|
|
||||||
<!-- auth_api -->
|
|
||||||
<g id="node23" class="node">
|
|
||||||
<title>auth_api</title>
|
|
||||||
<path fill="#ffcdd2" stroke="black" d="M1914.15,-496.8C1914.15,-496.8 1851.85,-496.8 1851.85,-496.8 1845.85,-496.8 1839.85,-490.8 1839.85,-484.8 1839.85,-484.8 1839.85,-472.8 1839.85,-472.8 1839.85,-466.8 1845.85,-460.8 1851.85,-460.8 1851.85,-460.8 1914.15,-460.8 1914.15,-460.8 1920.15,-460.8 1926.15,-466.8 1926.15,-472.8 1926.15,-472.8 1926.15,-484.8 1926.15,-484.8 1926.15,-490.8 1920.15,-496.8 1914.15,-496.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1883" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">authAPI</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1883" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">(login/register)</text>
|
|
||||||
</g>
|
|
||||||
<!-- http_service->auth_api -->
|
|
||||||
<g id="edge17" class="edge">
|
|
||||||
<title>http_service->auth_api</title>
|
|
||||||
<path fill="none" stroke="#c62828" d="M1656.93,-602C1734.99,-602 1883,-602 1883,-602 1883,-602 1883,-508.76 1883,-508.76"/>
|
|
||||||
<polygon fill="#c62828" stroke="#c62828" points="1886.5,-508.76 1883,-498.76 1879.5,-508.76 1886.5,-508.76"/>
|
|
||||||
</g>
|
|
||||||
<!-- visits_api -->
|
|
||||||
<g id="node24" class="node">
|
|
||||||
<title>visits_api</title>
|
|
||||||
<path fill="#ffcdd2" stroke="black" d="M1810.3,-496.8C1810.3,-496.8 1751.7,-496.8 1751.7,-496.8 1745.7,-496.8 1739.7,-490.8 1739.7,-484.8 1739.7,-484.8 1739.7,-472.8 1739.7,-472.8 1739.7,-466.8 1745.7,-460.8 1751.7,-460.8 1751.7,-460.8 1810.3,-460.8 1810.3,-460.8 1816.3,-460.8 1822.3,-466.8 1822.3,-472.8 1822.3,-472.8 1822.3,-484.8 1822.3,-484.8 1822.3,-490.8 1816.3,-496.8 1810.3,-496.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1781" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">visitsAPI</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1781" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">(CRUD visits)</text>
|
|
||||||
</g>
|
|
||||||
<!-- http_service->visits_api -->
|
|
||||||
<g id="edge18" class="edge">
|
|
||||||
<title>http_service->visits_api</title>
|
|
||||||
<path fill="none" stroke="#c62828" d="M1656.73,-593C1707.36,-593 1781,-593 1781,-593 1781,-593 1781,-508.66 1781,-508.66"/>
|
|
||||||
<polygon fill="#c62828" stroke="#c62828" points="1784.5,-508.66 1781,-498.66 1777.5,-508.66 1784.5,-508.66"/>
|
|
||||||
</g>
|
|
||||||
<!-- orders_api -->
|
|
||||||
<g id="node25" class="node">
|
|
||||||
<title>orders_api</title>
|
|
||||||
<path fill="#ffcdd2" stroke="black" d="M1309.71,-496.8C1309.71,-496.8 1230.29,-496.8 1230.29,-496.8 1224.29,-496.8 1218.29,-490.8 1218.29,-484.8 1218.29,-484.8 1218.29,-472.8 1218.29,-472.8 1218.29,-466.8 1224.29,-460.8 1230.29,-460.8 1230.29,-460.8 1309.71,-460.8 1309.71,-460.8 1315.71,-460.8 1321.71,-466.8 1321.71,-472.8 1321.71,-472.8 1321.71,-484.8 1321.71,-484.8 1321.71,-490.8 1315.71,-496.8 1309.71,-496.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1270" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">OrdersAPI</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1270" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">(service requests)</text>
|
|
||||||
</g>
|
|
||||||
<!-- http_service->orders_api -->
|
|
||||||
<g id="edge19" class="edge">
|
|
||||||
<title>http_service->orders_api</title>
|
|
||||||
<path fill="none" stroke="#c62828" d="M1563.08,-602C1469.81,-602 1270,-602 1270,-602 1270,-602 1270,-508.76 1270,-508.76"/>
|
|
||||||
<polygon fill="#c62828" stroke="#c62828" points="1273.5,-508.76 1270,-498.76 1266.5,-508.76 1273.5,-508.76"/>
|
|
||||||
</g>
|
|
||||||
<!-- petowners_api -->
|
|
||||||
<g id="node26" class="node">
|
|
||||||
<title>petowners_api</title>
|
|
||||||
<path fill="#ffcdd2" stroke="black" d="M1709.46,-496.8C1709.46,-496.8 1646.54,-496.8 1646.54,-496.8 1640.54,-496.8 1634.54,-490.8 1634.54,-484.8 1634.54,-484.8 1634.54,-472.8 1634.54,-472.8 1634.54,-466.8 1640.54,-460.8 1646.54,-460.8 1646.54,-460.8 1709.46,-460.8 1709.46,-460.8 1715.46,-460.8 1721.46,-466.8 1721.46,-472.8 1721.46,-472.8 1721.46,-484.8 1721.46,-484.8 1721.46,-490.8 1715.46,-496.8 1709.46,-496.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1678" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">petOwnersAPI</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1678" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">(clients)</text>
|
|
||||||
</g>
|
|
||||||
<!-- http_service->petowners_api -->
|
|
||||||
<g id="edge20" class="edge">
|
|
||||||
<title>http_service->petowners_api</title>
|
|
||||||
<path fill="none" stroke="#c62828" d="M1645.53,-574.76C1645.53,-574.76 1645.53,-508.64 1645.53,-508.64"/>
|
|
||||||
<polygon fill="#c62828" stroke="#c62828" points="1649.03,-508.64 1645.53,-498.64 1642.03,-508.64 1649.03,-508.64"/>
|
|
||||||
</g>
|
|
||||||
<!-- vets_api -->
|
|
||||||
<g id="node27" class="node">
|
|
||||||
<title>vets_api</title>
|
|
||||||
<path fill="#ffcdd2" stroke="black" d="M1604.27,-496.8C1604.27,-496.8 1529.73,-496.8 1529.73,-496.8 1523.73,-496.8 1517.73,-490.8 1517.73,-484.8 1517.73,-484.8 1517.73,-472.8 1517.73,-472.8 1517.73,-466.8 1523.73,-460.8 1529.73,-460.8 1529.73,-460.8 1604.27,-460.8 1604.27,-460.8 1610.27,-460.8 1616.27,-466.8 1616.27,-472.8 1616.27,-472.8 1616.27,-484.8 1616.27,-484.8 1616.27,-490.8 1610.27,-496.8 1604.27,-496.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1567" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">VeterinariansAPI</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1567" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">(professionals)</text>
|
|
||||||
</g>
|
|
||||||
<!-- http_service->vets_api -->
|
|
||||||
<g id="edge21" class="edge">
|
|
||||||
<title>http_service->vets_api</title>
|
|
||||||
<path fill="none" stroke="#c62828" d="M1589.88,-574.76C1589.88,-574.76 1589.88,-508.64 1589.88,-508.64"/>
|
|
||||||
<polygon fill="#c62828" stroke="#c62828" points="1593.38,-508.64 1589.88,-498.64 1586.38,-508.64 1593.38,-508.64"/>
|
|
||||||
</g>
|
|
||||||
<!-- services_api -->
|
|
||||||
<g id="node28" class="node">
|
|
||||||
<title>services_api</title>
|
|
||||||
<path fill="#ffcdd2" stroke="black" d="M1488.04,-496.8C1488.04,-496.8 1437.96,-496.8 1437.96,-496.8 1431.96,-496.8 1425.96,-490.8 1425.96,-484.8 1425.96,-484.8 1425.96,-472.8 1425.96,-472.8 1425.96,-466.8 1431.96,-460.8 1437.96,-460.8 1437.96,-460.8 1488.04,-460.8 1488.04,-460.8 1494.04,-460.8 1500.04,-466.8 1500.04,-472.8 1500.04,-472.8 1500.04,-484.8 1500.04,-484.8 1500.04,-490.8 1494.04,-496.8 1488.04,-496.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1463" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">servicesAPI</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1463" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">(catalog)</text>
|
|
||||||
</g>
|
|
||||||
<!-- http_service->services_api -->
|
|
||||||
<g id="edge22" class="edge">
|
|
||||||
<title>http_service->services_api</title>
|
|
||||||
<path fill="none" stroke="#c62828" d="M1563.08,-584C1520.06,-584 1463,-584 1463,-584 1463,-584 1463,-508.64 1463,-508.64"/>
|
|
||||||
<polygon fill="#c62828" stroke="#c62828" points="1466.5,-508.64 1463,-498.64 1459.5,-508.64 1466.5,-508.64"/>
|
|
||||||
</g>
|
|
||||||
<!-- cart_api -->
|
|
||||||
<g id="node29" class="node">
|
|
||||||
<title>cart_api</title>
|
|
||||||
<path fill="#ffcdd2" stroke="black" d="M1395.99,-496.8C1395.99,-496.8 1352.01,-496.8 1352.01,-496.8 1346.01,-496.8 1340.01,-490.8 1340.01,-484.8 1340.01,-484.8 1340.01,-472.8 1340.01,-472.8 1340.01,-466.8 1346.01,-460.8 1352.01,-460.8 1352.01,-460.8 1395.99,-460.8 1395.99,-460.8 1401.99,-460.8 1407.99,-466.8 1407.99,-472.8 1407.99,-472.8 1407.99,-484.8 1407.99,-484.8 1407.99,-490.8 1401.99,-496.8 1395.99,-496.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1374" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">CartAPI</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1374" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">(shopping)</text>
|
|
||||||
</g>
|
|
||||||
<!-- http_service->cart_api -->
|
|
||||||
<g id="edge23" class="edge">
|
|
||||||
<title>http_service->cart_api</title>
|
|
||||||
<path fill="none" stroke="#c62828" d="M1563.09,-593C1494.27,-593 1374,-593 1374,-593 1374,-593 1374,-508.66 1374,-508.66"/>
|
|
||||||
<polygon fill="#c62828" stroke="#c62828" points="1377.5,-508.66 1374,-498.66 1370.5,-508.66 1377.5,-508.66"/>
|
|
||||||
</g>
|
|
||||||
<!-- api_auth -->
|
|
||||||
<g id="node38" class="node">
|
|
||||||
<title>api_auth</title>
|
|
||||||
<path fill="#cfd8dc" stroke="black" d="M1773.81,-388.8C1773.81,-388.8 1726.19,-388.8 1726.19,-388.8 1720.19,-388.8 1714.19,-382.8 1714.19,-376.8 1714.19,-376.8 1714.19,-364.8 1714.19,-364.8 1714.19,-358.8 1720.19,-352.8 1726.19,-352.8 1726.19,-352.8 1773.81,-352.8 1773.81,-352.8 1779.81,-352.8 1785.81,-358.8 1785.81,-364.8 1785.81,-364.8 1785.81,-376.8 1785.81,-376.8 1785.81,-382.8 1779.81,-388.8 1773.81,-388.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1750" y="-374.1" font-family="Helvetica,sans-Serif" font-size="11.00">/api/token/</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1750" y="-360.9" font-family="Helvetica,sans-Serif" font-size="11.00">(JWT Auth)</text>
|
|
||||||
</g>
|
|
||||||
<!-- http_service->api_auth -->
|
|
||||||
<g id="edge39" class="edge">
|
|
||||||
<title>http_service->api_auth</title>
|
|
||||||
<path fill="none" stroke="#455a64" stroke-dasharray="5,2" d="M1656.96,-584C1690.82,-584 1730.58,-584 1730.58,-584 1730.58,-584 1730.58,-400.49 1730.58,-400.49"/>
|
|
||||||
<polygon fill="#455a64" stroke="#455a64" points="1734.08,-400.49 1730.58,-390.49 1727.08,-400.49 1734.08,-400.49"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1986.5" y="-476.1" font-family="Helvetica,sans-Serif" font-size="9.00">JWT refresh</text>
|
|
||||||
</g>
|
|
||||||
<!-- auth_api->api_auth -->
|
|
||||||
<g id="edge24" class="edge">
|
|
||||||
<title>auth_api->api_auth</title>
|
|
||||||
<path fill="none" stroke="#455a64" d="M1867.88,-460.31C1867.88,-439.04 1867.88,-407 1867.88,-407 1867.88,-407 1770.44,-407 1770.44,-407 1770.44,-407 1770.44,-400.41 1770.44,-400.41"/>
|
|
||||||
<polygon fill="#455a64" stroke="#455a64" points="1773.94,-400.41 1770.44,-390.41 1766.94,-400.41 1773.94,-400.41"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1909.27" y="-434.7" font-family="Helvetica,sans-Serif" font-size="9.00">POST /api/token/</text>
|
|
||||||
</g>
|
|
||||||
<!-- api_mascotas -->
|
|
||||||
<g id="node35" class="node">
|
|
||||||
<title>api_mascotas</title>
|
|
||||||
<path fill="#cfd8dc" stroke="black" d="M1683.93,-388.8C1683.93,-388.8 1602.07,-388.8 1602.07,-388.8 1596.07,-388.8 1590.07,-382.8 1590.07,-376.8 1590.07,-376.8 1590.07,-364.8 1590.07,-364.8 1590.07,-358.8 1596.07,-352.8 1602.07,-352.8 1602.07,-352.8 1683.93,-352.8 1683.93,-352.8 1689.93,-352.8 1695.93,-358.8 1695.93,-364.8 1695.93,-364.8 1695.93,-376.8 1695.93,-376.8 1695.93,-382.8 1689.93,-388.8 1683.93,-388.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1643" y="-374.1" font-family="Helvetica,sans-Serif" font-size="11.00">/mascotas/api/v1/</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1643" y="-360.9" font-family="Helvetica,sans-Serif" font-size="11.00">(Pets, Vets, Visits)</text>
|
|
||||||
</g>
|
|
||||||
<!-- visits_api->api_mascotas -->
|
|
||||||
<g id="edge25" class="edge">
|
|
||||||
<title>visits_api->api_mascotas</title>
|
|
||||||
<path fill="none" stroke="#455a64" d="M1755.07,-460.59C1755.07,-444.98 1755.07,-425 1755.07,-425 1755.07,-425 1675.46,-425 1675.46,-425 1675.46,-425 1675.46,-400.66 1675.46,-400.66"/>
|
|
||||||
<polygon fill="#455a64" stroke="#455a64" points="1678.96,-400.66 1675.46,-390.66 1671.96,-400.66 1678.96,-400.66"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1814.25" y="-434.7" font-family="Helvetica,sans-Serif" font-size="9.00">CRUD /vet-visits/</text>
|
|
||||||
</g>
|
|
||||||
<!-- api_solicitudes -->
|
|
||||||
<g id="node37" class="node">
|
|
||||||
<title>api_solicitudes</title>
|
|
||||||
<path fill="#cfd8dc" stroke="black" d="M1439.74,-388.8C1439.74,-388.8 1390.26,-388.8 1390.26,-388.8 1384.26,-388.8 1378.26,-382.8 1378.26,-376.8 1378.26,-376.8 1378.26,-364.8 1378.26,-364.8 1378.26,-358.8 1384.26,-352.8 1390.26,-352.8 1390.26,-352.8 1439.74,-352.8 1439.74,-352.8 1445.74,-352.8 1451.74,-358.8 1451.74,-364.8 1451.74,-364.8 1451.74,-376.8 1451.74,-376.8 1451.74,-382.8 1445.74,-388.8 1439.74,-388.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1415" y="-374.1" font-family="Helvetica,sans-Serif" font-size="11.00">/solicitudes/</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1415" y="-360.9" font-family="Helvetica,sans-Serif" font-size="11.00">(Requests)</text>
|
|
||||||
</g>
|
|
||||||
<!-- orders_api->api_solicitudes -->
|
|
||||||
<g id="edge26" class="edge">
|
|
||||||
<title>orders_api->api_solicitudes</title>
|
|
||||||
<path fill="none" stroke="#455a64" d="M1270,-460.51C1270,-429.76 1270,-371 1270,-371 1270,-371 1366.52,-371 1366.52,-371"/>
|
|
||||||
<polygon fill="#455a64" stroke="#455a64" points="1366.52,-374.5 1376.52,-371 1366.52,-367.5 1366.52,-374.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1320.76" y="-434.7" font-family="Helvetica,sans-Serif" font-size="9.00">CRUD /service-requests/</text>
|
|
||||||
</g>
|
|
||||||
<!-- petowners_api->api_mascotas -->
|
|
||||||
<g id="edge27" class="edge">
|
|
||||||
<title>petowners_api->api_mascotas</title>
|
|
||||||
<path fill="none" stroke="#455a64" d="M1655,-460.48C1655,-460.48 1655,-400.72 1655,-400.72"/>
|
|
||||||
<polygon fill="#455a64" stroke="#455a64" points="1658.5,-400.72 1655,-390.72 1651.5,-400.72 1658.5,-400.72"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1715.01" y="-434.7" font-family="Helvetica,sans-Serif" font-size="9.00">CRUD /pet-owners/</text>
|
|
||||||
</g>
|
|
||||||
<!-- vets_api->api_mascotas -->
|
|
||||||
<g id="edge28" class="edge">
|
|
||||||
<title>vets_api->api_mascotas</title>
|
|
||||||
<path fill="none" stroke="#455a64" d="M1603.17,-460.48C1603.17,-460.48 1603.17,-400.72 1603.17,-400.72"/>
|
|
||||||
<polygon fill="#455a64" stroke="#455a64" points="1606.67,-400.72 1603.17,-390.72 1599.67,-400.72 1606.67,-400.72"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1609.76" y="-434.7" font-family="Helvetica,sans-Serif" font-size="9.00">GET /veterinarians/</text>
|
|
||||||
</g>
|
|
||||||
<!-- api_productos -->
|
|
||||||
<g id="node36" class="node">
|
|
||||||
<title>api_productos</title>
|
|
||||||
<path fill="#cfd8dc" stroke="black" d="M1560.09,-388.8C1560.09,-388.8 1481.91,-388.8 1481.91,-388.8 1475.91,-388.8 1469.91,-382.8 1469.91,-376.8 1469.91,-376.8 1469.91,-364.8 1469.91,-364.8 1469.91,-358.8 1475.91,-352.8 1481.91,-352.8 1481.91,-352.8 1560.09,-352.8 1560.09,-352.8 1566.09,-352.8 1572.09,-358.8 1572.09,-364.8 1572.09,-364.8 1572.09,-376.8 1572.09,-376.8 1572.09,-382.8 1566.09,-388.8 1560.09,-388.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1521" y="-374.1" font-family="Helvetica,sans-Serif" font-size="11.00">/productos/</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1521" y="-360.9" font-family="Helvetica,sans-Serif" font-size="11.00">(Services, Prices)</text>
|
|
||||||
</g>
|
|
||||||
<!-- services_api->api_productos -->
|
|
||||||
<g id="edge29" class="edge">
|
|
||||||
<title>services_api->api_productos</title>
|
|
||||||
<path fill="none" stroke="#455a64" d="M1489.99,-460.48C1489.99,-460.48 1489.99,-400.72 1489.99,-400.72"/>
|
|
||||||
<polygon fill="#455a64" stroke="#455a64" points="1493.49,-400.72 1489.99,-390.72 1486.49,-400.72 1493.49,-400.72"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1517.5" y="-434.7" font-family="Helvetica,sans-Serif" font-size="9.00">GET /services/</text>
|
|
||||||
</g>
|
|
||||||
<!-- cart_api->api_productos -->
|
|
||||||
<g id="edge30" class="edge">
|
|
||||||
<title>cart_api->api_productos</title>
|
|
||||||
<path fill="none" stroke="#455a64" d="M1393.13,-460.48C1393.13,-451.67 1393.13,-443 1393.13,-443 1393.13,-443 1479.95,-443 1479.95,-443 1479.95,-443 1479.95,-400.53 1479.95,-400.53"/>
|
|
||||||
<polygon fill="#455a64" stroke="#455a64" points="1483.45,-400.53 1479.95,-390.53 1476.45,-400.53 1483.45,-400.53"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1433.25" y="-434.7" font-family="Helvetica,sans-Serif" font-size="9.00">CRUD /cart/</text>
|
|
||||||
</g>
|
|
||||||
<!-- redux_store -->
|
|
||||||
<g id="node30" class="node">
|
|
||||||
<title>redux_store</title>
|
|
||||||
<path fill="#b2ebf2" stroke="black" d="M2228.93,-496.8C2228.93,-496.8 2169.07,-496.8 2169.07,-496.8 2163.07,-496.8 2157.07,-490.8 2157.07,-484.8 2157.07,-484.8 2157.07,-472.8 2157.07,-472.8 2157.07,-466.8 2163.07,-460.8 2169.07,-460.8 2169.07,-460.8 2228.93,-460.8 2228.93,-460.8 2234.93,-460.8 2240.93,-466.8 2240.93,-472.8 2240.93,-472.8 2240.93,-484.8 2240.93,-484.8 2240.93,-490.8 2234.93,-496.8 2228.93,-496.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2199" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">Redux Store</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2199" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">(Global State)</text>
|
|
||||||
</g>
|
|
||||||
<!-- auth_slice -->
|
|
||||||
<g id="node31" class="node">
|
|
||||||
<title>auth_slice</title>
|
|
||||||
<path fill="#b2ebf2" stroke="black" d="M2092.87,-611.12C2092.87,-611.12 2039.13,-611.12 2039.13,-611.12 2033.13,-611.12 2027.13,-605.12 2027.13,-599.12 2027.13,-599.12 2027.13,-587.12 2027.13,-587.12 2027.13,-581.12 2033.13,-575.12 2039.13,-575.12 2039.13,-575.12 2092.87,-575.12 2092.87,-575.12 2098.87,-575.12 2104.87,-581.12 2104.87,-587.12 2104.87,-587.12 2104.87,-599.12 2104.87,-599.12 2104.87,-605.12 2098.87,-611.12 2092.87,-611.12"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2066" y="-596.42" font-family="Helvetica,sans-Serif" font-size="11.00">Auth Slice</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2066" y="-583.22" font-family="Helvetica,sans-Serif" font-size="11.00">(user, token)</text>
|
|
||||||
</g>
|
|
||||||
<!-- auth_slice->redux_store -->
|
|
||||||
<g id="edge31" class="edge">
|
|
||||||
<title>auth_slice->redux_store</title>
|
|
||||||
<path fill="none" stroke="#00838f" d="M2066,-574.79C2066,-542.53 2066,-479 2066,-479 2066,-479 2145.4,-479 2145.4,-479"/>
|
|
||||||
<polygon fill="#00838f" stroke="#00838f" points="2145.4,-482.5 2155.4,-479 2145.4,-475.5 2145.4,-482.5"/>
|
|
||||||
</g>
|
|
||||||
<!-- visits_slice -->
|
|
||||||
<g id="node32" class="node">
|
|
||||||
<title>visits_slice</title>
|
|
||||||
<path fill="#b2ebf2" stroke="black" d="M2179.59,-611.12C2179.59,-611.12 2134.41,-611.12 2134.41,-611.12 2128.41,-611.12 2122.41,-605.12 2122.41,-599.12 2122.41,-599.12 2122.41,-587.12 2122.41,-587.12 2122.41,-581.12 2128.41,-575.12 2134.41,-575.12 2134.41,-575.12 2179.59,-575.12 2179.59,-575.12 2185.59,-575.12 2191.59,-581.12 2191.59,-587.12 2191.59,-587.12 2191.59,-599.12 2191.59,-599.12 2191.59,-605.12 2185.59,-611.12 2179.59,-611.12"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2157" y="-596.42" font-family="Helvetica,sans-Serif" font-size="11.00">Visits Slice</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2157" y="-583.22" font-family="Helvetica,sans-Serif" font-size="11.00">(visit data)</text>
|
|
||||||
</g>
|
|
||||||
<!-- visits_slice->redux_store -->
|
|
||||||
<g id="edge32" class="edge">
|
|
||||||
<title>visits_slice->redux_store</title>
|
|
||||||
<path fill="none" stroke="#00838f" d="M2174.33,-574.76C2174.33,-574.76 2174.33,-508.64 2174.33,-508.64"/>
|
|
||||||
<polygon fill="#00838f" stroke="#00838f" points="2177.83,-508.64 2174.33,-498.64 2170.83,-508.64 2177.83,-508.64"/>
|
|
||||||
</g>
|
|
||||||
<!-- cart_slice -->
|
|
||||||
<g id="node33" class="node">
|
|
||||||
<title>cart_slice</title>
|
|
||||||
<path fill="#b2ebf2" stroke="black" d="M2260.84,-611.12C2260.84,-611.12 2221.16,-611.12 2221.16,-611.12 2215.16,-611.12 2209.16,-605.12 2209.16,-599.12 2209.16,-599.12 2209.16,-587.12 2209.16,-587.12 2209.16,-581.12 2215.16,-575.12 2221.16,-575.12 2221.16,-575.12 2260.84,-575.12 2260.84,-575.12 2266.84,-575.12 2272.84,-581.12 2272.84,-587.12 2272.84,-587.12 2272.84,-599.12 2272.84,-599.12 2272.84,-605.12 2266.84,-611.12 2260.84,-611.12"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2241" y="-596.42" font-family="Helvetica,sans-Serif" font-size="11.00">Cart Slice</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2241" y="-583.22" font-family="Helvetica,sans-Serif" font-size="11.00">(items)</text>
|
|
||||||
</g>
|
|
||||||
<!-- cart_slice->redux_store -->
|
|
||||||
<g id="edge33" class="edge">
|
|
||||||
<title>cart_slice->redux_store</title>
|
|
||||||
<path fill="none" stroke="#00838f" d="M2225.05,-574.76C2225.05,-574.76 2225.05,-508.64 2225.05,-508.64"/>
|
|
||||||
<polygon fill="#00838f" stroke="#00838f" points="2228.55,-508.64 2225.05,-498.64 2221.55,-508.64 2228.55,-508.64"/>
|
|
||||||
</g>
|
|
||||||
<!-- auth_context -->
|
|
||||||
<g id="node34" class="node">
|
|
||||||
<title>auth_context</title>
|
|
||||||
<path fill="#b2ebf2" stroke="black" d="M2355.27,-611.12C2355.27,-611.12 2302.73,-611.12 2302.73,-611.12 2296.73,-611.12 2290.73,-605.12 2290.73,-599.12 2290.73,-599.12 2290.73,-587.12 2290.73,-587.12 2290.73,-581.12 2296.73,-575.12 2302.73,-575.12 2302.73,-575.12 2355.27,-575.12 2355.27,-575.12 2361.27,-575.12 2367.27,-581.12 2367.27,-587.12 2367.27,-587.12 2367.27,-599.12 2367.27,-599.12 2367.27,-605.12 2361.27,-611.12 2355.27,-611.12"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2329" y="-596.42" font-family="Helvetica,sans-Serif" font-size="11.00">AuthContext</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2329" y="-583.22" font-family="Helvetica,sans-Serif" font-size="11.00">(Provider)</text>
|
|
||||||
</g>
|
|
||||||
<!-- auth_context->redux_store -->
|
|
||||||
<g id="edge34" class="edge">
|
|
||||||
<title>auth_context->redux_store</title>
|
|
||||||
<path fill="none" stroke="#00838f" stroke-dasharray="5,2" d="M2329,-574.79C2329,-542.53 2329,-479 2329,-479 2329,-479 2252.72,-479 2252.72,-479"/>
|
|
||||||
<polygon fill="#00838f" stroke="#00838f" points="2252.72,-475.5 2242.72,-479 2252.72,-482.5 2252.72,-475.5"/>
|
|
||||||
</g>
|
|
||||||
<!-- api_payments -->
|
|
||||||
<g id="node39" class="node">
|
|
||||||
<title>api_payments</title>
|
|
||||||
<path fill="#cfd8dc" stroke="black" d="M1883.91,-388.8C1883.91,-388.8 1816.09,-388.8 1816.09,-388.8 1810.09,-388.8 1804.09,-382.8 1804.09,-376.8 1804.09,-376.8 1804.09,-364.8 1804.09,-364.8 1804.09,-358.8 1810.09,-352.8 1816.09,-352.8 1816.09,-352.8 1883.91,-352.8 1883.91,-352.8 1889.91,-352.8 1895.91,-358.8 1895.91,-364.8 1895.91,-364.8 1895.91,-376.8 1895.91,-376.8 1895.91,-382.8 1889.91,-388.8 1883.91,-388.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1850" y="-374.1" font-family="Helvetica,sans-Serif" font-size="11.00">/payments/</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1850" y="-360.9" font-family="Helvetica,sans-Serif" font-size="11.00">(MercadoPago)</text>
|
|
||||||
</g>
|
|
||||||
<!-- petowner_user -->
|
|
||||||
<g id="node40" class="node">
|
|
||||||
<title>petowner_user</title>
|
|
||||||
<ellipse fill="#f8bbd9" stroke="black" cx="866" cy="-593.12" rx="45.9" ry="24.32"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="866" y="-596.42" font-family="Helvetica,sans-Serif" font-size="11.00">PetOwner</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="866" y="-583.22" font-family="Helvetica,sans-Serif" font-size="11.00">(Cliente)</text>
|
|
||||||
</g>
|
|
||||||
<!-- petowner_user->home -->
|
|
||||||
<g id="edge35" class="edge">
|
|
||||||
<title>petowner_user->home</title>
|
|
||||||
<path fill="none" stroke="#ad1457" d="M827.6,-579.22C827.6,-551.07 827.6,-490 827.6,-490 827.6,-490 466.04,-490 466.04,-490"/>
|
|
||||||
<polygon fill="#ad1457" stroke="#ad1457" points="466.04,-486.5 456.04,-490 466.04,-493.5 466.04,-486.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="830.01" y="-542.7" font-family="Helvetica,sans-Serif" font-size="9.00">public access</text>
|
|
||||||
</g>
|
|
||||||
<!-- petowner_user->profile_page -->
|
|
||||||
<g id="edge36" class="edge">
|
|
||||||
<title>petowner_user->profile_page</title>
|
|
||||||
<path fill="none" stroke="#ad1457" stroke-dasharray="5,2" d="M819.7,-593C733.92,-593 558.94,-593 558.94,-593 558.94,-593 558.94,-107 558.94,-107 558.94,-107 462.46,-107 462.46,-107"/>
|
|
||||||
<polygon fill="#ad1457" stroke="#ad1457" points="462.46,-103.5 452.46,-107 462.46,-110.5 462.46,-103.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1154.02" y="-326.7" font-family="Helvetica,sans-Serif" font-size="9.00">authenticated</text>
|
|
||||||
</g>
|
|
||||||
<!-- vet_user -->
|
|
||||||
<g id="node41" class="node">
|
|
||||||
<title>vet_user</title>
|
|
||||||
<ellipse fill="#f8bbd9" stroke="black" cx="985" cy="-593.12" rx="55.41" ry="24.32"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="985" y="-596.42" font-family="Helvetica,sans-Serif" font-size="11.00">Veterinarian</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="985" y="-583.22" font-family="Helvetica,sans-Serif" font-size="11.00">(Profesional)</text>
|
|
||||||
</g>
|
|
||||||
<!-- vet_user->admin_dash -->
|
|
||||||
<g id="edge37" class="edge">
|
|
||||||
<title>vet_user->admin_dash</title>
|
|
||||||
<path fill="none" stroke="#ad1457" d="M954.15,-572.61C954.15,-542.01 954.15,-488 954.15,-488 954.15,-488 916.71,-488 916.71,-488"/>
|
|
||||||
<polygon fill="#ad1457" stroke="#ad1457" points="916.71,-484.5 906.71,-488 916.71,-491.5 916.71,-484.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="921.26" y="-542.7" font-family="Helvetica,sans-Serif" font-size="9.00">backoffice</text>
|
|
||||||
</g>
|
|
||||||
<!-- admin_user -->
|
|
||||||
<g id="node42" class="node">
|
|
||||||
<title>admin_user</title>
|
|
||||||
<ellipse fill="#f8bbd9" stroke="black" cx="1110" cy="-593.12" rx="51.52" ry="24.32"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1110" y="-596.42" font-family="Helvetica,sans-Serif" font-size="11.00">Admin/Staff</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1110" y="-583.22" font-family="Helvetica,sans-Serif" font-size="11.00">(Interno)</text>
|
|
||||||
</g>
|
|
||||||
<!-- admin_user->admin_dash -->
|
|
||||||
<g id="edge38" class="edge">
|
|
||||||
<title>admin_user->admin_dash</title>
|
|
||||||
<path fill="none" stroke="#ad1457" d="M1078.95,-573.25C1078.95,-540.55 1078.95,-479 1078.95,-479 1078.95,-479 916.49,-479 916.49,-479"/>
|
|
||||||
<polygon fill="#ad1457" stroke="#ad1457" points="916.49,-475.5 906.49,-479 916.49,-482.5 916.49,-475.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1181.01" y="-542.7" font-family="Helvetica,sans-Serif" font-size="9.00">full access</text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 51 KiB |
@@ -1,234 +0,0 @@
|
|||||||
digraph DataModel {
|
|
||||||
// Graph settings
|
|
||||||
rankdir=TB
|
|
||||||
compound=true
|
|
||||||
splines=ortho
|
|
||||||
node [shape=record, style="filled", fontname="Helvetica", fontsize=10]
|
|
||||||
edge [fontname="Helvetica", fontsize=8]
|
|
||||||
|
|
||||||
label="AMAR Mascotas - Data Model (Entity Relationships)\n\n"
|
|
||||||
labelloc="t"
|
|
||||||
fontsize=16
|
|
||||||
fontname="Helvetica-Bold"
|
|
||||||
|
|
||||||
// === USERS & AUTHENTICATION ===
|
|
||||||
subgraph cluster_auth {
|
|
||||||
label="Users & Authentication"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#E8F5E9"
|
|
||||||
color="#2E7D32"
|
|
||||||
|
|
||||||
auth_user [label="{auth.User|id: PK\luser name: str\lemail: str\lis_staff: bool\lis_superuser: bool\l}", fillcolor="#C8E6C9"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// === PET OWNERS & PETS ===
|
|
||||||
subgraph cluster_mascotas_owners {
|
|
||||||
label="Pet Owners & Pets"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#E3F2FD"
|
|
||||||
color="#1565C0"
|
|
||||||
|
|
||||||
petowner [label="{PetOwner|id: PK\lemail: str (unique)\lphone: str\lfirst_name: str\llast_name: str\lneighborhood_id: FK\lcampaign_id: FK (opt)\lgeo_latitude: float\lgeo_longitude: float\laddress: str\l}", fillcolor="#BBDEFB"]
|
|
||||||
|
|
||||||
pet [label="{Pet|id: PK\lowner_id: FK\lname: str\lpet_type: DOG/CAT\lbreed_id: FK (opt)\lgender: M/F\lage: int\lweight: decimal\lheight: decimal\lbirth_date: date\lallergies: text\lneutered: bool\lis_deceased: bool\lstate: puppy/adult/...\lprofile_picture: file\l}", fillcolor="#BBDEFB"]
|
|
||||||
|
|
||||||
petvaccine [label="{PetVaccine|id: PK\lpet_id: FK\lvaccine_id: FK\lapplication_date: date\lnext_application: date\l}", fillcolor="#90CAF9"]
|
|
||||||
|
|
||||||
petstudy [label="{PetStudy|id: PK\lpet_id: FK\lstudy_id: FK\ldate: date\lresult: text\limages: files\l}", fillcolor="#90CAF9"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// === VETERINARIANS ===
|
|
||||||
subgraph cluster_veterinarians {
|
|
||||||
label="Veterinarians"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#FFF3E0"
|
|
||||||
color="#E65100"
|
|
||||||
|
|
||||||
veterinarian [label="{Veterinarian|id: PK\luser_id: FK\llicense: str\lemail: str\lphone: str\l}", fillcolor="#FFE0B2"]
|
|
||||||
|
|
||||||
availability [label="{Availability|id: PK\lveterinarian_id: FK\lstart_day: 0-6\lend_day: 0-6\lstart_time: time\lend_time: time\l}", fillcolor="#FFCC80"]
|
|
||||||
|
|
||||||
unavailability [label="{Unavailability|id: PK\lveterinarian_id: FK\lstart_date: date\lend_date: date\lstart_time: time\lend_time: time\lreason: str\l}", fillcolor="#FFCC80"]
|
|
||||||
|
|
||||||
vet_specialty [label="{M2M: Vet-Specialty|veterinarian_id: FK\lspecialty_id: FK\l}", shape=diamond, fillcolor="#FFB74D"]
|
|
||||||
|
|
||||||
vet_neighborhood [label="{M2M: Vet-Neighborhood|veterinarian_id: FK\lneighborhood_id: FK\l}", shape=diamond, fillcolor="#FFB74D"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// === SERVICES & PRICING ===
|
|
||||||
subgraph cluster_productos {
|
|
||||||
label="Services & Pricing"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#F3E5F5"
|
|
||||||
color="#7B1FA2"
|
|
||||||
|
|
||||||
grupo [label="{Group|id: PK\lname: str\ldescription: text\l}", fillcolor="#E1BEE7"]
|
|
||||||
|
|
||||||
category [label="{Category|id: PK\lgroup_id: FK\lname: str\ldescription: text\lvalue: int (order)\l}", fillcolor="#E1BEE7"]
|
|
||||||
|
|
||||||
service [label="{Service|id: PK\lname: str\ldescription: text\lspecialty_id: FK\lcategory_id: FK\lduration: int (min)\lmodality: onsite/online\lpayment_sign_req: bool\lpet_type_filter: str\lage_filter: str\lweight_range: str\l}", fillcolor="#CE93D8"]
|
|
||||||
|
|
||||||
prices [label="{Prices|id: PK\lservice_id: FK\lveterinarian_id: FK (opt)\lprice: decimal\lprofessional_fee: decimal\lpayment_sign: decimal\lfrom_date: date\lto_date: date\lactive: bool\l}", fillcolor="#BA68C8"]
|
|
||||||
|
|
||||||
discounts [label="{Discounts|id: PK\lservice_id: FK\ldiscount: decimal (%)\lfrom_date: date\lto_date: date\lactive: bool\l}", fillcolor="#BA68C8"]
|
|
||||||
|
|
||||||
servicecombo [label="{ServiceCombo|id: PK\lname: str\ldescription: text\ldiscount_percent: decimal\ldiscount_fixed: decimal\l}", fillcolor="#E1BEE7"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// === CART & CHECKOUT ===
|
|
||||||
subgraph cluster_cart {
|
|
||||||
label="Cart & Checkout"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#E0F7FA"
|
|
||||||
color="#00838F"
|
|
||||||
|
|
||||||
cart [label="{Cart|id: PK\lpetowner_id: FK\lveterinarian_id: FK (opt)\luse_vet_prices: bool\lapply_turn_fee: bool\l}", fillcolor="#B2EBF2"]
|
|
||||||
|
|
||||||
cartitem [label="{CartItem|id: PK\lcart_id: FK\lpet_id: FK (opt)\lservice_id: FK\lprice: decimal\lquantity: int\ltotal: decimal (calc)\l}", fillcolor="#80DEEA"]
|
|
||||||
|
|
||||||
cartresumeitem [label="{CartResumeItem|id: PK\lcart_id: FK\lconcept: SUBTOTAL/\l DESCUENTO/\l ADELANTO/\l TOTAL/\l COSTO_SERVICIO\lamount: decimal\lorder: int\l}", fillcolor="#80DEEA"]
|
|
||||||
|
|
||||||
cartpetreason [label="{CartPetReason|id: PK\lcart_id: FK\lpet_id: FK\lreason: text\l}", fillcolor="#80DEEA"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// === SERVICE REQUESTS ===
|
|
||||||
subgraph cluster_solicitudes {
|
|
||||||
label="Service Requests (Workflow)"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#FFEBEE"
|
|
||||||
color="#C62828"
|
|
||||||
|
|
||||||
servicerequest [label="{ServiceRequest|id: PK\lpetowner_id: FK\lcart_id: FK\lveterinarian_id: FK (opt)\lstate: pending/vet_asked/\l vet_accepted/coordinated/\l payed/Confirmado/...\lreason: text\ldays_requested: JSON\ldate_coordinated: datetime\lhour_coordinated: time\lpay_number: str\lcampaign_id: FK (opt)\lattended_by_id: FK (opt)\l}", fillcolor="#FFCDD2"]
|
|
||||||
|
|
||||||
statehistory [label="{StateHistory|id: PK\lservice_request_id: FK\lstate: str\ladditional_data: JSON\lcreated_at: datetime\luser_id: FK\l}", fillcolor="#EF9A9A"]
|
|
||||||
|
|
||||||
vetasked [label="{VeterinarianAsked|id: PK\lservice_request_id: FK\lveterinarian_id: FK\ldate_asked: datetime\ldate_answered: datetime\laccepted: bool\l}", fillcolor="#EF9A9A"]
|
|
||||||
|
|
||||||
vetreminder [label="{ScheduledVetReminder|id: PK\lvet_asked_id: FK\lscheduled_for: datetime\lprocessed_at: datetime\lstatus: pending/sent/...\lcelery_task_id: str\l}", fillcolor="#E57373"]
|
|
||||||
|
|
||||||
payreminder [label="{PaymentReminder|id: PK\lservice_request_id: FK\lscheduled_for: datetime\lprocessed_at: datetime\lstatus: pending/sent/...\l}", fillcolor="#E57373"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// === VET VISITS ===
|
|
||||||
subgraph cluster_vetvisits {
|
|
||||||
label="Veterinary Visits"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#FFFDE7"
|
|
||||||
color="#F9A825"
|
|
||||||
|
|
||||||
vetvisit [label="{VetVisit|id: PK\lservice_request_id: FK (opt)\lowner_id: FK\lveterinarian_id: FK\ldate: date\lhour: time\lvisit_type: clinical/\l vaccination/\l telemedicina\lvisit_state: PENDING/\l IN_PROGRESS/\l COMPLETED/\l NO_REPORT/\l CANCELLED\lreason: text\lobservations: text\lprice: decimal\ldeposit: decimal\lvet_fee: decimal\lpay_transaction: str\lgoogle_event_id: str\lafip_receipt_id: FK (opt)\l}", fillcolor="#FFF9C4"]
|
|
||||||
|
|
||||||
vetvisitreport [label="{VetVisitReport|id: PK\lvisit_id: FK\lpet_id: FK\lreason: text\lphysical_exam: text\ldiagnosis: text\ltreatment: text\lpdf_file: file\l}", fillcolor="#FFF59D"]
|
|
||||||
|
|
||||||
vetvisitfollowup [label="{VetVisitFollowUp|id: PK\lreport_id: FK\ldate: date\ldescription: text\l}", fillcolor="#FFF176"]
|
|
||||||
|
|
||||||
vetvisitpetreason [label="{VetVisitPetReason|id: PK\lvisit_id: FK\lpet_id: FK\lreason: text\l}", fillcolor="#FFF176"]
|
|
||||||
|
|
||||||
visit_pets [label="{M2M: Visit-Pets|vetvisit_id: FK\lpet_id: FK\l}", shape=diamond, fillcolor="#FFEE58"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// === REFERENCE DATA ===
|
|
||||||
subgraph cluster_reference {
|
|
||||||
label="Reference Data"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#ECEFF1"
|
|
||||||
color="#455A64"
|
|
||||||
|
|
||||||
specialty [label="{Specialty|id: PK\lname: str\l}", fillcolor="#CFD8DC"]
|
|
||||||
neighborhood [label="{Neighborhood|id: PK\lname: str\ldistance_coefficient: decimal\lcoverage_area: GIS Polygon\l}", fillcolor="#CFD8DC"]
|
|
||||||
province [label="{Province|id: PK\lname: str\l}", fillcolor="#CFD8DC"]
|
|
||||||
locality [label="{Locality|id: PK\lprovince_id: FK\lname: str\l}", fillcolor="#CFD8DC"]
|
|
||||||
petbreed [label="{PetBreed|id: PK\lname: str\lpet_type: DOG/CAT\l}", fillcolor="#CFD8DC"]
|
|
||||||
vaccine [label="{Vaccine|id: PK\lname: str\lpet_type: DOG/CAT\lperiodicity: int (months)\l}", fillcolor="#CFD8DC"]
|
|
||||||
study [label="{Study|id: PK\lname: str\lpet_type: DOG/CAT\lgroup_id: FK (opt)\l}", fillcolor="#CFD8DC"]
|
|
||||||
campaign [label="{Campaign|id: PK\lname: str\lutm_source: str\lutm_medium: str\lis_active: bool\l}", fillcolor="#CFD8DC"]
|
|
||||||
tag [label="{Tag|id: PK\lname: str (unique)\l}", fillcolor="#CFD8DC"]
|
|
||||||
medication [label="{Medication|id: PK\lname: str\lgeneral_name_id: FK\ltype_id: FK\lpresentation_id: FK\lpet_type: str\l}", fillcolor="#CFD8DC"]
|
|
||||||
turnfeegroup [label="{IndividualTurnFeeGroup|id: PK\lname: str\lfee_percentage: decimal\l}", fillcolor="#CFD8DC"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// === AFIP/INVOICING ===
|
|
||||||
subgraph cluster_afip {
|
|
||||||
label="AFIP Invoicing"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#FCE4EC"
|
|
||||||
color="#AD1457"
|
|
||||||
|
|
||||||
receipt [label="{Receipt|id: PK\ldocument_number: bigint\lreceipt_number: int\lissued_date: date\ltotal_amount: decimal\lnet_taxed: decimal\lcae: str\lcae_expiration: date\l}", fillcolor="#F8BBD9"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// === RELATIONSHIPS ===
|
|
||||||
|
|
||||||
// Auth -> PetOwner/Vet
|
|
||||||
auth_user -> petowner [label="1:1 opt", style=dashed, color="#2E7D32"]
|
|
||||||
auth_user -> veterinarian [label="1:1", color="#2E7D32"]
|
|
||||||
|
|
||||||
// PetOwner relationships
|
|
||||||
petowner -> pet [label="1:N owns", color="#1565C0"]
|
|
||||||
petowner -> neighborhood [label="N:1 lives in", color="#1565C0"]
|
|
||||||
petowner -> campaign [label="N:1 opt", style=dashed, color="#666"]
|
|
||||||
petowner -> cart [label="1:N", color="#00838F"]
|
|
||||||
petowner -> servicerequest [label="1:N requests", color="#C62828"]
|
|
||||||
petowner -> vetvisit [label="1:N as owner", color="#F9A825"]
|
|
||||||
|
|
||||||
// Pet relationships
|
|
||||||
pet -> petbreed [label="N:1 opt", style=dashed, color="#666"]
|
|
||||||
pet -> petvaccine [label="1:N", color="#1565C0"]
|
|
||||||
pet -> petstudy [label="1:N", color="#1565C0"]
|
|
||||||
petvaccine -> vaccine [label="N:1", color="#666"]
|
|
||||||
petstudy -> study [label="N:1", color="#666"]
|
|
||||||
|
|
||||||
// Veterinarian relationships
|
|
||||||
veterinarian -> vet_specialty [label="1:N", color="#E65100"]
|
|
||||||
vet_specialty -> specialty [label="N:1", color="#E65100"]
|
|
||||||
veterinarian -> vet_neighborhood [label="1:N coverage", color="#E65100"]
|
|
||||||
vet_neighborhood -> neighborhood [label="N:1", color="#E65100"]
|
|
||||||
veterinarian -> availability [label="1:N", color="#E65100"]
|
|
||||||
veterinarian -> unavailability [label="1:N", color="#E65100"]
|
|
||||||
veterinarian -> turnfeegroup [label="N:M", color="#E65100", style=dashed]
|
|
||||||
|
|
||||||
// Service/Pricing relationships
|
|
||||||
grupo -> category [label="1:N", color="#7B1FA2"]
|
|
||||||
category -> service [label="1:N", color="#7B1FA2"]
|
|
||||||
service -> specialty [label="N:1 opt", style=dashed, color="#7B1FA2"]
|
|
||||||
service -> prices [label="1:N", color="#7B1FA2"]
|
|
||||||
service -> discounts [label="1:N", color="#7B1FA2"]
|
|
||||||
prices -> veterinarian [label="N:1 opt\n(vet-specific)", style=dashed, color="#7B1FA2"]
|
|
||||||
|
|
||||||
// Cart relationships
|
|
||||||
cart -> veterinarian [label="N:1 opt\n(assigned vet)", style=dashed, color="#00838F"]
|
|
||||||
cart -> cartitem [label="1:N", color="#00838F"]
|
|
||||||
cart -> cartresumeitem [label="1:N", color="#00838F"]
|
|
||||||
cart -> cartpetreason [label="1:N", color="#00838F"]
|
|
||||||
cartitem -> service [label="N:1", color="#00838F"]
|
|
||||||
cartitem -> pet [label="N:1 opt", style=dashed, color="#00838F"]
|
|
||||||
cartpetreason -> pet [label="N:1", color="#00838F"]
|
|
||||||
|
|
||||||
// ServiceRequest relationships
|
|
||||||
servicerequest -> cart [label="1:1", color="#C62828"]
|
|
||||||
servicerequest -> veterinarian [label="N:1 opt\n(assigned)", style=dashed, color="#C62828"]
|
|
||||||
servicerequest -> statehistory [label="1:N audit", color="#C62828"]
|
|
||||||
servicerequest -> vetasked [label="1:N", color="#C62828"]
|
|
||||||
servicerequest -> payreminder [label="1:N", color="#C62828"]
|
|
||||||
servicerequest -> campaign [label="N:1 opt", style=dashed, color="#666"]
|
|
||||||
servicerequest -> tag [label="N:M", color="#666"]
|
|
||||||
vetasked -> veterinarian [label="N:1", color="#C62828"]
|
|
||||||
vetasked -> vetreminder [label="1:N", color="#C62828"]
|
|
||||||
|
|
||||||
// VetVisit relationships
|
|
||||||
vetvisit -> servicerequest [label="1:1 opt\n(from request)", style=dashed, color="#F9A825"]
|
|
||||||
vetvisit -> veterinarian [label="N:1", color="#F9A825"]
|
|
||||||
vetvisit -> visit_pets [label="1:N", color="#F9A825"]
|
|
||||||
visit_pets -> pet [label="N:1", color="#F9A825"]
|
|
||||||
vetvisit -> vetvisitreport [label="1:N", color="#F9A825"]
|
|
||||||
vetvisit -> vetvisitpetreason [label="1:N", color="#F9A825"]
|
|
||||||
vetvisitreport -> pet [label="N:1", color="#F9A825"]
|
|
||||||
vetvisitreport -> vetvisitfollowup [label="1:N", color="#F9A825"]
|
|
||||||
vetvisitpetreason -> pet [label="N:1", color="#F9A825"]
|
|
||||||
|
|
||||||
// AFIP
|
|
||||||
vetvisit -> receipt [label="1:1 opt\n(invoice)", style=dashed, color="#AD1457"]
|
|
||||||
|
|
||||||
// Geography
|
|
||||||
province -> locality [label="1:N", color="#666"]
|
|
||||||
}
|
|
||||||
@@ -1,976 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
|
||||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<!-- Generated by graphviz version 14.0.5 (0)
|
|
||||||
-->
|
|
||||||
<!-- Title: DataModel Pages: 1 -->
|
|
||||||
<svg width="2547pt" height="1691pt"
|
|
||||||
viewBox="0.00 0.00 2547.00 1691.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
||||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1687.2)">
|
|
||||||
<title>DataModel</title>
|
|
||||||
<polygon fill="white" stroke="none" points="-4,4 -4,-1687.2 2543,-1687.2 2543,4 -4,4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1269.5" y="-1664" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">AMAR Mascotas - Data Model (Entity Relationships)</text>
|
|
||||||
<g id="clust1" class="cluster">
|
|
||||||
<title>cluster_auth</title>
|
|
||||||
<path fill="#e8f5e9" stroke="#2e7d32" d="M550,-1367.6C550,-1367.6 703,-1367.6 703,-1367.6 709,-1367.6 715,-1373.6 715,-1379.6 715,-1379.6 715,-1515 715,-1515 715,-1521 709,-1527 703,-1527 703,-1527 550,-1527 550,-1527 544,-1527 538,-1521 538,-1515 538,-1515 538,-1379.6 538,-1379.6 538,-1373.6 544,-1367.6 550,-1367.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="626.5" y="-1507.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Users & Authentication</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust2" class="cluster">
|
|
||||||
<title>cluster_mascotas_owners</title>
|
|
||||||
<path fill="#e3f2fd" stroke="#1565c0" d="M482,-494.2C482,-494.2 678,-494.2 678,-494.2 684,-494.2 690,-500.2 690,-506.2 690,-506.2 690,-1192.4 690,-1192.4 690,-1198.4 684,-1204.4 678,-1204.4 678,-1204.4 482,-1204.4 482,-1204.4 476,-1204.4 470,-1198.4 470,-1192.4 470,-1192.4 470,-506.2 470,-506.2 470,-500.2 476,-494.2 482,-494.2"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="580" y="-1185.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Pet Owners & Pets</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust3" class="cluster">
|
|
||||||
<title>cluster_veterinarians</title>
|
|
||||||
<path fill="#fff3e0" stroke="#e65100" d="M806,-317.6C806,-317.6 1867,-317.6 1867,-317.6 1873,-317.6 1879,-323.6 1879,-329.6 1879,-329.6 1879,-647.6 1879,-647.6 1879,-653.6 1873,-659.6 1867,-659.6 1867,-659.6 806,-659.6 806,-659.6 800,-659.6 794,-653.6 794,-647.6 794,-647.6 794,-329.6 794,-329.6 794,-323.6 800,-317.6 806,-317.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1336.5" y="-640.4" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Veterinarians</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust4" class="cluster">
|
|
||||||
<title>cluster_productos</title>
|
|
||||||
<path fill="#f3e5f5" stroke="#7b1fa2" d="M67,-476.2C67,-476.2 318,-476.2 318,-476.2 324,-476.2 330,-482.2 330,-488.2 330,-488.2 330,-1515 330,-1515 330,-1521 324,-1527 318,-1527 318,-1527 67,-1527 67,-1527 61,-1527 55,-1521 55,-1515 55,-1515 55,-488.2 55,-488.2 55,-482.2 61,-476.2 67,-476.2"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="192.5" y="-1507.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Services & Pricing</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust5" class="cluster">
|
|
||||||
<title>cluster_cart</title>
|
|
||||||
<path fill="#e0f7fa" stroke="#00838f" d="M740,-991C740,-991 1066,-991 1066,-991 1072,-991 1078,-997 1078,-1003 1078,-1003 1078,-1515 1078,-1515 1078,-1521 1072,-1527 1066,-1527 1066,-1527 740,-1527 740,-1527 734,-1527 728,-1521 728,-1515 728,-1515 728,-1003 728,-1003 728,-997 734,-991 740,-991"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="903" y="-1507.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Cart & Checkout</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust6" class="cluster">
|
|
||||||
<title>cluster_solicitudes</title>
|
|
||||||
<path fill="#ffebee" stroke="#c62828" d="M1899,-494.2C1899,-494.2 2297,-494.2 2297,-494.2 2303,-494.2 2309,-500.2 2309,-506.2 2309,-506.2 2309,-1216.4 2309,-1216.4 2309,-1222.4 2303,-1228.4 2297,-1228.4 2297,-1228.4 1899,-1228.4 1899,-1228.4 1893,-1228.4 1887,-1222.4 1887,-1216.4 1887,-1216.4 1887,-506.2 1887,-506.2 1887,-500.2 1893,-494.2 1899,-494.2"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2098" y="-1209.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Service Requests (Workflow)</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust7" class="cluster">
|
|
||||||
<title>cluster_vetvisits</title>
|
|
||||||
<path fill="#fffde7" stroke="#f9a825" d="M1186,-762.8C1186,-762.8 1726,-762.8 1726,-762.8 1732,-762.8 1738,-768.8 1738,-774.8 1738,-774.8 1738,-1617 1738,-1617 1738,-1623 1732,-1629 1726,-1629 1726,-1629 1186,-1629 1186,-1629 1180,-1629 1174,-1623 1174,-1617 1174,-1617 1174,-774.8 1174,-774.8 1174,-768.8 1180,-762.8 1186,-762.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1456" y="-1609.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Veterinary Visits</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust8" class="cluster">
|
|
||||||
<title>cluster_reference</title>
|
|
||||||
<path fill="#eceff1" stroke="#455a64" d="M20,-8C20,-8 1189,-8 1189,-8 1195,-8 1201,-14 1201,-20 1201,-20 1201,-278 1201,-278 1201,-284 1195,-290 1189,-290 1189,-290 20,-290 20,-290 14,-290 8,-284 8,-278 8,-278 8,-20 8,-20 8,-14 14,-8 20,-8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="604.5" y="-270.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Reference Data</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust9" class="cluster">
|
|
||||||
<title>cluster_afip</title>
|
|
||||||
<path fill="#fce4ec" stroke="#ad1457" d="M2399,-997C2399,-997 2519,-997 2519,-997 2525,-997 2531,-1003 2531,-1009 2531,-1009 2531,-1180.4 2531,-1180.4 2531,-1186.4 2525,-1192.4 2519,-1192.4 2519,-1192.4 2399,-1192.4 2399,-1192.4 2393,-1192.4 2387,-1186.4 2387,-1180.4 2387,-1180.4 2387,-1009 2387,-1009 2387,-1003 2393,-997 2399,-997"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2459" y="-1173.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">AFIP Invoicing</text>
|
|
||||||
</g>
|
|
||||||
<!-- auth_user -->
|
|
||||||
<g id="node1" class="node">
|
|
||||||
<title>auth_user</title>
|
|
||||||
<polygon fill="#c8e6c9" stroke="black" points="604.14,-1376.1 604.14,-1464.1 701.86,-1464.1 701.86,-1376.1 604.14,-1376.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="653" y="-1451.1" font-family="Helvetica,sans-Serif" font-size="10.00">auth.User</text>
|
|
||||||
<polyline fill="none" stroke="black" points="604.14,-1444.1 701.86,-1444.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="612.14" y="-1431.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="612.14" y="-1419.1" font-family="Helvetica,sans-Serif" font-size="10.00">user name: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="612.14" y="-1407.1" font-family="Helvetica,sans-Serif" font-size="10.00">email: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="612.14" y="-1395.1" font-family="Helvetica,sans-Serif" font-size="10.00">is_staff: bool</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="612.14" y="-1383.1" font-family="Helvetica,sans-Serif" font-size="10.00">is_superuser: bool</text>
|
|
||||||
</g>
|
|
||||||
<!-- petowner -->
|
|
||||||
<g id="node2" class="node">
|
|
||||||
<title>petowner</title>
|
|
||||||
<polygon fill="#bbdefb" stroke="black" points="565.81,-993.5 565.81,-1141.5 680.19,-1141.5 680.19,-993.5 565.81,-993.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="623" y="-1128.5" font-family="Helvetica,sans-Serif" font-size="10.00">PetOwner</text>
|
|
||||||
<polyline fill="none" stroke="black" points="565.81,-1121.5 680.19,-1121.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="573.81" y="-1108.5" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="573.81" y="-1096.5" font-family="Helvetica,sans-Serif" font-size="10.00">email: str (unique)</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="573.81" y="-1084.5" font-family="Helvetica,sans-Serif" font-size="10.00">phone: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="573.81" y="-1072.5" font-family="Helvetica,sans-Serif" font-size="10.00">first_name: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="573.81" y="-1060.5" font-family="Helvetica,sans-Serif" font-size="10.00">last_name: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="573.81" y="-1048.5" font-family="Helvetica,sans-Serif" font-size="10.00">neighborhood_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="573.81" y="-1036.5" font-family="Helvetica,sans-Serif" font-size="10.00">campaign_id: FK (opt)</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="573.81" y="-1024.5" font-family="Helvetica,sans-Serif" font-size="10.00">geo_latitude: float</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="573.81" y="-1012.5" font-family="Helvetica,sans-Serif" font-size="10.00">geo_longitude: float</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="573.81" y="-1000.5" font-family="Helvetica,sans-Serif" font-size="10.00">address: str</text>
|
|
||||||
</g>
|
|
||||||
<!-- auth_user->petowner -->
|
|
||||||
<g id="edge1" class="edge">
|
|
||||||
<title>auth_user->petowner</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" stroke-dasharray="5,2" d="M642.17,-1375.8C642.17,-1375.8 642.17,-1153.26 642.17,-1153.26"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="645.67,-1153.26 642.17,-1143.26 638.67,-1153.26 645.67,-1153.26"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="645.23" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:1 opt</text>
|
|
||||||
</g>
|
|
||||||
<!-- veterinarian -->
|
|
||||||
<g id="node6" class="node">
|
|
||||||
<title>veterinarian</title>
|
|
||||||
<polygon fill="#ffe0b2" stroke="black" points="1538.04,-508.7 1538.04,-596.7 1607.96,-596.7 1607.96,-508.7 1538.04,-508.7"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1573" y="-583.7" font-family="Helvetica,sans-Serif" font-size="10.00">Veterinarian</text>
|
|
||||||
<polyline fill="none" stroke="black" points="1538.04,-576.7 1607.96,-576.7"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1546.04" y="-563.7" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1546.04" y="-551.7" font-family="Helvetica,sans-Serif" font-size="10.00">user_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1546.04" y="-539.7" font-family="Helvetica,sans-Serif" font-size="10.00">license: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1546.04" y="-527.7" font-family="Helvetica,sans-Serif" font-size="10.00">email: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1546.04" y="-515.7" font-family="Helvetica,sans-Serif" font-size="10.00">phone: str</text>
|
|
||||||
</g>
|
|
||||||
<!-- auth_user->veterinarian -->
|
|
||||||
<g id="edge2" class="edge">
|
|
||||||
<title>auth_user->veterinarian</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M695.21,-1375.77C695.21,-1200.06 695.21,-562 695.21,-562 695.21,-562 1526.26,-562 1526.26,-562"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1526.26,-565.5 1536.26,-562 1526.26,-558.5 1526.26,-565.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="787.56" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">1:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- pet -->
|
|
||||||
<g id="node3" class="node">
|
|
||||||
<title>pet</title>
|
|
||||||
<polygon fill="#bbdefb" stroke="black" points="574.97,-705.3 574.97,-913.3 681.03,-913.3 681.03,-705.3 574.97,-705.3"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="628" y="-900.3" font-family="Helvetica,sans-Serif" font-size="10.00">Pet</text>
|
|
||||||
<polyline fill="none" stroke="black" points="574.97,-893.3 681.03,-893.3"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-880.3" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-868.3" font-family="Helvetica,sans-Serif" font-size="10.00">owner_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-856.3" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-844.3" font-family="Helvetica,sans-Serif" font-size="10.00">pet_type: DOG/CAT</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-832.3" font-family="Helvetica,sans-Serif" font-size="10.00">breed_id: FK (opt)</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-820.3" font-family="Helvetica,sans-Serif" font-size="10.00">gender: M/F</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-808.3" font-family="Helvetica,sans-Serif" font-size="10.00">age: int</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-796.3" font-family="Helvetica,sans-Serif" font-size="10.00">weight: decimal</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-784.3" font-family="Helvetica,sans-Serif" font-size="10.00">height: decimal</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-772.3" font-family="Helvetica,sans-Serif" font-size="10.00">birth_date: date</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-760.3" font-family="Helvetica,sans-Serif" font-size="10.00">allergies: text</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-748.3" font-family="Helvetica,sans-Serif" font-size="10.00">neutered: bool</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-736.3" font-family="Helvetica,sans-Serif" font-size="10.00">is_deceased: bool</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-724.3" font-family="Helvetica,sans-Serif" font-size="10.00">state: puppy/adult/...</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-712.3" font-family="Helvetica,sans-Serif" font-size="10.00">profile_picture: file</text>
|
|
||||||
</g>
|
|
||||||
<!-- petowner->pet -->
|
|
||||||
<g id="edge3" class="edge">
|
|
||||||
<title>petowner->pet</title>
|
|
||||||
<path fill="none" stroke="#1565c0" d="M627.58,-993.16C627.58,-993.16 627.58,-924.87 627.58,-924.87"/>
|
|
||||||
<polygon fill="#1565c0" stroke="#1565c0" points="631.08,-924.87 627.58,-914.87 624.08,-924.87 631.08,-924.87"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="588.67" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">1:N owns</text>
|
|
||||||
</g>
|
|
||||||
<!-- cart -->
|
|
||||||
<g id="node17" class="node">
|
|
||||||
<title>cart</title>
|
|
||||||
<polygon fill="#b2ebf2" stroke="black" points="906.36,-1376.1 906.36,-1464.1 1029.64,-1464.1 1029.64,-1376.1 906.36,-1376.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="968" y="-1451.1" font-family="Helvetica,sans-Serif" font-size="10.00">Cart</text>
|
|
||||||
<polyline fill="none" stroke="black" points="906.36,-1444.1 1029.64,-1444.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="914.36" y="-1431.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="914.36" y="-1419.1" font-family="Helvetica,sans-Serif" font-size="10.00">petowner_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="914.36" y="-1407.1" font-family="Helvetica,sans-Serif" font-size="10.00">veterinarian_id: FK (opt)</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="914.36" y="-1395.1" font-family="Helvetica,sans-Serif" font-size="10.00">use_vet_prices: bool</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="914.36" y="-1383.1" font-family="Helvetica,sans-Serif" font-size="10.00">apply_turn_fee: bool</text>
|
|
||||||
</g>
|
|
||||||
<!-- petowner->cart -->
|
|
||||||
<g id="edge6" class="edge">
|
|
||||||
<title>petowner->cart</title>
|
|
||||||
<path fill="none" stroke="#00838f" d="M680.65,-1130C765.7,-1130 914.82,-1130 914.82,-1130 914.82,-1130 914.82,-1364.21 914.82,-1364.21"/>
|
|
||||||
<polygon fill="#00838f" stroke="#00838f" points="911.32,-1364.21 914.82,-1374.21 918.32,-1364.21 911.32,-1364.21"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="790.22" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- servicerequest -->
|
|
||||||
<g id="node21" class="node">
|
|
||||||
<title>servicerequest</title>
|
|
||||||
<polygon fill="#ffcdd2" stroke="black" points="1895.4,-969.5 1895.4,-1165.5 2032.6,-1165.5 2032.6,-969.5 1895.4,-969.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1964" y="-1152.5" font-family="Helvetica,sans-Serif" font-size="10.00">ServiceRequest</text>
|
|
||||||
<polyline fill="none" stroke="black" points="1895.4,-1145.5 2032.6,-1145.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1132.5" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1120.5" font-family="Helvetica,sans-Serif" font-size="10.00">petowner_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1108.5" font-family="Helvetica,sans-Serif" font-size="10.00">cart_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1096.5" font-family="Helvetica,sans-Serif" font-size="10.00">veterinarian_id: FK (opt)</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1084.5" font-family="Helvetica,sans-Serif" font-size="10.00">state: pending/vet_asked/</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1072.5" font-family="Helvetica,sans-Serif" font-size="10.00"> vet_accepted/coordinated/</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1060.5" font-family="Helvetica,sans-Serif" font-size="10.00"> payed/Confirmado/...</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1048.5" font-family="Helvetica,sans-Serif" font-size="10.00">reason: text</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1036.5" font-family="Helvetica,sans-Serif" font-size="10.00">days_requested: JSON</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1024.5" font-family="Helvetica,sans-Serif" font-size="10.00">date_coordinated: datetime</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1012.5" font-family="Helvetica,sans-Serif" font-size="10.00">hour_coordinated: time</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1000.5" font-family="Helvetica,sans-Serif" font-size="10.00">pay_number: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-988.5" font-family="Helvetica,sans-Serif" font-size="10.00">campaign_id: FK (opt)</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-976.5" font-family="Helvetica,sans-Serif" font-size="10.00">attended_by_id: FK (opt)</text>
|
|
||||||
</g>
|
|
||||||
<!-- petowner->servicerequest -->
|
|
||||||
<g id="edge7" class="edge">
|
|
||||||
<title>petowner->servicerequest</title>
|
|
||||||
<path fill="none" stroke="#c62828" d="M623.15,-1141.95C623.15,-1145.73 623.15,-1148 623.15,-1148 623.15,-1148 1883.69,-1148 1883.69,-1148"/>
|
|
||||||
<polygon fill="#c62828" stroke="#c62828" points="1883.69,-1151.5 1893.69,-1148 1883.69,-1144.5 1883.69,-1151.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1109" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:N requests</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisit -->
|
|
||||||
<g id="node26" class="node">
|
|
||||||
<title>vetvisit</title>
|
|
||||||
<polygon fill="#fff9c4" stroke="black" points="1588.19,-1274.1 1588.19,-1566.1 1729.81,-1566.1 1729.81,-1274.1 1588.19,-1274.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1659" y="-1553.1" font-family="Helvetica,sans-Serif" font-size="10.00">VetVisit</text>
|
|
||||||
<polyline fill="none" stroke="black" points="1588.19,-1546.1 1729.81,-1546.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1533.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1521.1" font-family="Helvetica,sans-Serif" font-size="10.00">service_request_id: FK (opt)</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1509.1" font-family="Helvetica,sans-Serif" font-size="10.00">owner_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1497.1" font-family="Helvetica,sans-Serif" font-size="10.00">veterinarian_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1485.1" font-family="Helvetica,sans-Serif" font-size="10.00">date: date</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1473.1" font-family="Helvetica,sans-Serif" font-size="10.00">hour: time</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1461.1" font-family="Helvetica,sans-Serif" font-size="10.00">visit_type: clinical/</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1449.1" font-family="Helvetica,sans-Serif" font-size="10.00"> vaccination/</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1437.1" font-family="Helvetica,sans-Serif" font-size="10.00"> telemedicina</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1425.1" font-family="Helvetica,sans-Serif" font-size="10.00">visit_state: PENDING/</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1413.1" font-family="Helvetica,sans-Serif" font-size="10.00"> IN_PROGRESS/</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1401.1" font-family="Helvetica,sans-Serif" font-size="10.00"> COMPLETED/</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1389.1" font-family="Helvetica,sans-Serif" font-size="10.00"> NO_REPORT/</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1377.1" font-family="Helvetica,sans-Serif" font-size="10.00"> CANCELLED</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1365.1" font-family="Helvetica,sans-Serif" font-size="10.00">reason: text</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1353.1" font-family="Helvetica,sans-Serif" font-size="10.00">observations: text</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1341.1" font-family="Helvetica,sans-Serif" font-size="10.00">price: decimal</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1329.1" font-family="Helvetica,sans-Serif" font-size="10.00">deposit: decimal</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1317.1" font-family="Helvetica,sans-Serif" font-size="10.00">vet_fee: decimal</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1305.1" font-family="Helvetica,sans-Serif" font-size="10.00">pay_transaction: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1293.1" font-family="Helvetica,sans-Serif" font-size="10.00">google_event_id: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1281.1" font-family="Helvetica,sans-Serif" font-size="10.00">afip_receipt_id: FK (opt)</text>
|
|
||||||
</g>
|
|
||||||
<!-- petowner->vetvisit -->
|
|
||||||
<g id="edge8" class="edge">
|
|
||||||
<title>petowner->vetvisit</title>
|
|
||||||
<path fill="none" stroke="#f9a825" d="M661.18,-1141.78C661.18,-1223.08 661.18,-1342 661.18,-1342 661.18,-1342 1576.53,-1342 1576.53,-1342"/>
|
|
||||||
<polygon fill="#f9a825" stroke="#f9a825" points="1576.53,-1345.5 1586.53,-1342 1576.53,-1338.5 1576.53,-1345.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1249.57" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:N as owner</text>
|
|
||||||
</g>
|
|
||||||
<!-- neighborhood -->
|
|
||||||
<g id="node32" class="node">
|
|
||||||
<title>neighborhood</title>
|
|
||||||
<polygon fill="#cfd8dc" stroke="black" points="139.79,-139.1 139.79,-215.1 284.21,-215.1 284.21,-139.1 139.79,-139.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="212" y="-202.1" font-family="Helvetica,sans-Serif" font-size="10.00">Neighborhood</text>
|
|
||||||
<polyline fill="none" stroke="black" points="139.79,-195.1 284.21,-195.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="147.79" y="-182.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="147.79" y="-170.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="147.79" y="-158.1" font-family="Helvetica,sans-Serif" font-size="10.00">distance_coefficient: decimal</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="147.79" y="-146.1" font-family="Helvetica,sans-Serif" font-size="10.00">coverage_area: GIS Polygon</text>
|
|
||||||
</g>
|
|
||||||
<!-- petowner->neighborhood -->
|
|
||||||
<g id="edge4" class="edge">
|
|
||||||
<title>petowner->neighborhood</title>
|
|
||||||
<path fill="none" stroke="#1565c0" d="M565.5,-1008C489.96,-1008 366.79,-1008 366.79,-1008 366.79,-1008 366.79,-210 366.79,-210 366.79,-210 295.92,-210 295.92,-210"/>
|
|
||||||
<polygon fill="#1565c0" stroke="#1565c0" points="295.92,-206.5 285.92,-210 295.92,-213.5 295.92,-206.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="25.56" y="-550.3" font-family="Helvetica,sans-Serif" font-size="8.00">N:1 lives in</text>
|
|
||||||
</g>
|
|
||||||
<!-- campaign -->
|
|
||||||
<g id="node38" class="node">
|
|
||||||
<title>campaign</title>
|
|
||||||
<polygon fill="#cfd8dc" stroke="black" points="828.77,-133.1 828.77,-221.1 919.23,-221.1 919.23,-133.1 828.77,-133.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="874" y="-208.1" font-family="Helvetica,sans-Serif" font-size="10.00">Campaign</text>
|
|
||||||
<polyline fill="none" stroke="black" points="828.77,-201.1 919.23,-201.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="836.77" y="-188.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="836.77" y="-176.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="836.77" y="-164.1" font-family="Helvetica,sans-Serif" font-size="10.00">utm_source: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="836.77" y="-152.1" font-family="Helvetica,sans-Serif" font-size="10.00">utm_medium: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="836.77" y="-140.1" font-family="Helvetica,sans-Serif" font-size="10.00">is_active: bool</text>
|
|
||||||
</g>
|
|
||||||
<!-- petowner->campaign -->
|
|
||||||
<g id="edge5" class="edge">
|
|
||||||
<title>petowner->campaign</title>
|
|
||||||
<path fill="none" stroke="#666666" stroke-dasharray="5,2" d="M680.53,-1005C685.43,-1005 688.57,-1005 688.57,-1005 688.57,-1005 688.57,-210 688.57,-210 688.57,-210 817.13,-210 817.13,-210"/>
|
|
||||||
<polygon fill="#666666" stroke="#666666" points="817.13,-213.5 827.13,-210 817.13,-206.5 817.13,-213.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="408.9" y="-550.3" font-family="Helvetica,sans-Serif" font-size="8.00">N:1 opt</text>
|
|
||||||
</g>
|
|
||||||
<!-- petvaccine -->
|
|
||||||
<g id="node4" class="node">
|
|
||||||
<title>petvaccine</title>
|
|
||||||
<polygon fill="#90caf9" stroke="black" points="568.07,-508.7 568.07,-596.7 681.93,-596.7 681.93,-508.7 568.07,-508.7"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="625" y="-583.7" font-family="Helvetica,sans-Serif" font-size="10.00">PetVaccine</text>
|
|
||||||
<polyline fill="none" stroke="black" points="568.07,-576.7 681.93,-576.7"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="576.07" y="-563.7" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="576.07" y="-551.7" font-family="Helvetica,sans-Serif" font-size="10.00">pet_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="576.07" y="-539.7" font-family="Helvetica,sans-Serif" font-size="10.00">vaccine_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="576.07" y="-527.7" font-family="Helvetica,sans-Serif" font-size="10.00">application_date: date</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="576.07" y="-515.7" font-family="Helvetica,sans-Serif" font-size="10.00">next_application: date</text>
|
|
||||||
</g>
|
|
||||||
<!-- pet->petvaccine -->
|
|
||||||
<g id="edge10" class="edge">
|
|
||||||
<title>pet->petvaccine</title>
|
|
||||||
<path fill="none" stroke="#1565c0" d="M628,-704.93C628,-704.93 628,-608.65 628,-608.65"/>
|
|
||||||
<polygon fill="#1565c0" stroke="#1565c0" points="631.5,-608.65 628,-598.65 624.5,-608.65 631.5,-608.65"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="633.22" y="-674.8" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- petstudy -->
|
|
||||||
<g id="node5" class="node">
|
|
||||||
<title>petstudy</title>
|
|
||||||
<polygon fill="#90caf9" stroke="black" points="478.21,-502.7 478.21,-602.7 549.79,-602.7 549.79,-502.7 478.21,-502.7"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="514" y="-589.7" font-family="Helvetica,sans-Serif" font-size="10.00">PetStudy</text>
|
|
||||||
<polyline fill="none" stroke="black" points="478.21,-582.7 549.79,-582.7"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="486.21" y="-569.7" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="486.21" y="-557.7" font-family="Helvetica,sans-Serif" font-size="10.00">pet_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="486.21" y="-545.7" font-family="Helvetica,sans-Serif" font-size="10.00">study_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="486.21" y="-533.7" font-family="Helvetica,sans-Serif" font-size="10.00">date: date</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="486.21" y="-521.7" font-family="Helvetica,sans-Serif" font-size="10.00">result: text</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="486.21" y="-509.7" font-family="Helvetica,sans-Serif" font-size="10.00">images: files</text>
|
|
||||||
</g>
|
|
||||||
<!-- pet->petstudy -->
|
|
||||||
<g id="edge11" class="edge">
|
|
||||||
<title>pet->petstudy</title>
|
|
||||||
<path fill="none" stroke="#1565c0" d="M574.67,-713C545,-713 514,-713 514,-713 514,-713 514,-614.49 514,-614.49"/>
|
|
||||||
<polygon fill="#1565c0" stroke="#1565c0" points="517.5,-614.49 514,-604.49 510.5,-614.49 517.5,-614.49"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="558.22" y="-674.8" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- petbreed -->
|
|
||||||
<g id="node35" class="node">
|
|
||||||
<title>petbreed</title>
|
|
||||||
<polygon fill="#cfd8dc" stroke="black" points="16.26,-145.1 16.26,-209.1 121.74,-209.1 121.74,-145.1 16.26,-145.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="69" y="-196.1" font-family="Helvetica,sans-Serif" font-size="10.00">PetBreed</text>
|
|
||||||
<polyline fill="none" stroke="black" points="16.26,-189.1 121.74,-189.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="24.26" y="-176.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="24.26" y="-164.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="24.26" y="-152.1" font-family="Helvetica,sans-Serif" font-size="10.00">pet_type: DOG/CAT</text>
|
|
||||||
</g>
|
|
||||||
<!-- pet->petbreed -->
|
|
||||||
<g id="edge9" class="edge">
|
|
||||||
<title>pet->petbreed</title>
|
|
||||||
<path fill="none" stroke="#666666" stroke-dasharray="5,2" d="M574.5,-721C429.5,-721 39.78,-721 39.78,-721 39.78,-721 39.78,-221.01 39.78,-221.01"/>
|
|
||||||
<polygon fill="#666666" stroke="#666666" points="43.28,-221.01 39.78,-211.01 36.28,-221.01 43.28,-221.01"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="457.9" y="-459" font-family="Helvetica,sans-Serif" font-size="8.00">N:1 opt</text>
|
|
||||||
</g>
|
|
||||||
<!-- vaccine -->
|
|
||||||
<g id="node36" class="node">
|
|
||||||
<title>vaccine</title>
|
|
||||||
<polygon fill="#cfd8dc" stroke="black" points="501.32,-139.1 501.32,-215.1 620.68,-215.1 620.68,-139.1 501.32,-139.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="561" y="-202.1" font-family="Helvetica,sans-Serif" font-size="10.00">Vaccine</text>
|
|
||||||
<polyline fill="none" stroke="black" points="501.32,-195.1 620.68,-195.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="509.32" y="-182.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="509.32" y="-170.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="509.32" y="-158.1" font-family="Helvetica,sans-Serif" font-size="10.00">pet_type: DOG/CAT</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="509.32" y="-146.1" font-family="Helvetica,sans-Serif" font-size="10.00">periodicity: int (months)</text>
|
|
||||||
</g>
|
|
||||||
<!-- petvaccine->vaccine -->
|
|
||||||
<g id="edge12" class="edge">
|
|
||||||
<title>petvaccine->vaccine</title>
|
|
||||||
<path fill="none" stroke="#666666" d="M594.38,-508.51C594.38,-508.51 594.38,-226.98 594.38,-226.98"/>
|
|
||||||
<polygon fill="#666666" stroke="#666666" points="597.88,-226.98 594.38,-216.98 590.88,-226.98 597.88,-226.98"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="581.22" y="-379.7" font-family="Helvetica,sans-Serif" font-size="8.00">N:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- study -->
|
|
||||||
<g id="node37" class="node">
|
|
||||||
<title>study</title>
|
|
||||||
<polygon fill="#cfd8dc" stroke="black" points="378.26,-139.1 378.26,-215.1 483.74,-215.1 483.74,-139.1 378.26,-139.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="431" y="-202.1" font-family="Helvetica,sans-Serif" font-size="10.00">Study</text>
|
|
||||||
<polyline fill="none" stroke="black" points="378.26,-195.1 483.74,-195.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="386.26" y="-182.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="386.26" y="-170.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="386.26" y="-158.1" font-family="Helvetica,sans-Serif" font-size="10.00">pet_type: DOG/CAT</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="386.26" y="-146.1" font-family="Helvetica,sans-Serif" font-size="10.00">group_id: FK (opt)</text>
|
|
||||||
</g>
|
|
||||||
<!-- petstudy->study -->
|
|
||||||
<g id="edge13" class="edge">
|
|
||||||
<title>petstudy->study</title>
|
|
||||||
<path fill="none" stroke="#666666" d="M492.53,-502.42C492.53,-400.05 492.53,-177 492.53,-177 492.53,-177 491.7,-177 491.7,-177"/>
|
|
||||||
<polygon fill="#666666" stroke="#666666" points="495.74,-173.5 485.74,-177 495.74,-180.5 495.74,-173.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="495.22" y="-379.7" font-family="Helvetica,sans-Serif" font-size="8.00">N:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- availability -->
|
|
||||||
<g id="node7" class="node">
|
|
||||||
<title>availability</title>
|
|
||||||
<polygon fill="#ffcc80" stroke="black" points="1653.03,-332.1 1653.03,-432.1 1752.97,-432.1 1752.97,-332.1 1653.03,-332.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1703" y="-419.1" font-family="Helvetica,sans-Serif" font-size="10.00">Availability</text>
|
|
||||||
<polyline fill="none" stroke="black" points="1653.03,-412.1 1752.97,-412.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1661.03" y="-399.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1661.03" y="-387.1" font-family="Helvetica,sans-Serif" font-size="10.00">veterinarian_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1661.03" y="-375.1" font-family="Helvetica,sans-Serif" font-size="10.00">start_day: 0-6</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1661.03" y="-363.1" font-family="Helvetica,sans-Serif" font-size="10.00">end_day: 0-6</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1661.03" y="-351.1" font-family="Helvetica,sans-Serif" font-size="10.00">start_time: time</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1661.03" y="-339.1" font-family="Helvetica,sans-Serif" font-size="10.00">end_time: time</text>
|
|
||||||
</g>
|
|
||||||
<!-- veterinarian->availability -->
|
|
||||||
<g id="edge18" class="edge">
|
|
||||||
<title>veterinarian->availability</title>
|
|
||||||
<path fill="none" stroke="#e65100" d="M1608.25,-530C1642.2,-530 1688.41,-530 1688.41,-530 1688.41,-530 1688.41,-444.1 1688.41,-444.1"/>
|
|
||||||
<polygon fill="#e65100" stroke="#e65100" points="1691.91,-444.1 1688.41,-434.1 1684.91,-444.1 1691.91,-444.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1653.22" y="-459" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- unavailability -->
|
|
||||||
<g id="node8" class="node">
|
|
||||||
<title>unavailability</title>
|
|
||||||
<polygon fill="#ffcc80" stroke="black" points="1771.03,-326.1 1771.03,-438.1 1870.97,-438.1 1870.97,-326.1 1771.03,-326.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1821" y="-425.1" font-family="Helvetica,sans-Serif" font-size="10.00">Unavailability</text>
|
|
||||||
<polyline fill="none" stroke="black" points="1771.03,-418.1 1870.97,-418.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1779.03" y="-405.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1779.03" y="-393.1" font-family="Helvetica,sans-Serif" font-size="10.00">veterinarian_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1779.03" y="-381.1" font-family="Helvetica,sans-Serif" font-size="10.00">start_date: date</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1779.03" y="-369.1" font-family="Helvetica,sans-Serif" font-size="10.00">end_date: date</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1779.03" y="-357.1" font-family="Helvetica,sans-Serif" font-size="10.00">start_time: time</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1779.03" y="-345.1" font-family="Helvetica,sans-Serif" font-size="10.00">end_time: time</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1779.03" y="-333.1" font-family="Helvetica,sans-Serif" font-size="10.00">reason: str</text>
|
|
||||||
</g>
|
|
||||||
<!-- veterinarian->unavailability -->
|
|
||||||
<g id="edge19" class="edge">
|
|
||||||
<title>veterinarian->unavailability</title>
|
|
||||||
<path fill="none" stroke="#e65100" d="M1608.27,-553C1676.74,-553 1821,-553 1821,-553 1821,-553 1821,-450.05 1821,-450.05"/>
|
|
||||||
<polygon fill="#e65100" stroke="#e65100" points="1824.5,-450.05 1821,-440.05 1817.5,-450.05 1824.5,-450.05"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1729.22" y="-459" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- vet_specialty -->
|
|
||||||
<g id="node9" class="node">
|
|
||||||
<title>vet_specialty</title>
|
|
||||||
<polygon fill="#ffb74d" stroke="black" points="1442,-426.1 1249.44,-382.1 1442,-338.1 1634.56,-382.1 1442,-426.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1283.24" y="-391.1" font-family="Helvetica,sans-Serif" font-size="10.00">{M2M: Vet-Specialty|veterinarian_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1283.24" y="-379.1" font-family="Helvetica,sans-Serif" font-size="10.00">specialty_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1442" y="-367.1" font-family="Helvetica,sans-Serif" font-size="10.00">}</text>
|
|
||||||
</g>
|
|
||||||
<!-- veterinarian->vet_specialty -->
|
|
||||||
<g id="edge14" class="edge">
|
|
||||||
<title>veterinarian->vet_specialty</title>
|
|
||||||
<path fill="none" stroke="#e65100" d="M1584.65,-508.32C1584.65,-508.32 1584.65,-405.5 1584.65,-405.5"/>
|
|
||||||
<polygon fill="#e65100" stroke="#e65100" points="1588.15,-405.5 1584.65,-395.5 1581.15,-405.5 1588.15,-405.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1505.22" y="-459" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- vet_neighborhood -->
|
|
||||||
<g id="node10" class="node">
|
|
||||||
<title>vet_neighborhood</title>
|
|
||||||
<polygon fill="#ffb74d" stroke="black" points="1017,-426.1 802.19,-382.1 1017,-338.1 1231.81,-382.1 1017,-426.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="838.97" y="-391.1" font-family="Helvetica,sans-Serif" font-size="10.00">{M2M: Vet-Neighborhood|veterinarian_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="838.97" y="-379.1" font-family="Helvetica,sans-Serif" font-size="10.00">neighborhood_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1017" y="-367.1" font-family="Helvetica,sans-Serif" font-size="10.00">}</text>
|
|
||||||
</g>
|
|
||||||
<!-- veterinarian->vet_neighborhood -->
|
|
||||||
<g id="edge16" class="edge">
|
|
||||||
<title>veterinarian->vet_neighborhood</title>
|
|
||||||
<path fill="none" stroke="#e65100" d="M1537.71,-544C1447.43,-544 1215.15,-544 1215.15,-544 1215.15,-544 1215.15,-397.3 1215.15,-397.3"/>
|
|
||||||
<polygon fill="#e65100" stroke="#e65100" points="1218.65,-397.3 1215.15,-387.3 1211.65,-397.3 1218.65,-397.3"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1273.79" y="-459" font-family="Helvetica,sans-Serif" font-size="8.00">1:N coverage</text>
|
|
||||||
</g>
|
|
||||||
<!-- turnfeegroup -->
|
|
||||||
<g id="node41" class="node">
|
|
||||||
<title>turnfeegroup</title>
|
|
||||||
<polygon fill="#cfd8dc" stroke="black" points="1067.24,-145.1 1067.24,-209.1 1192.76,-209.1 1192.76,-145.1 1067.24,-145.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1130" y="-196.1" font-family="Helvetica,sans-Serif" font-size="10.00">IndividualTurnFeeGroup</text>
|
|
||||||
<polyline fill="none" stroke="black" points="1067.24,-189.1 1192.76,-189.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1075.24" y="-176.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1075.24" y="-164.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1075.24" y="-152.1" font-family="Helvetica,sans-Serif" font-size="10.00">fee_percentage: decimal</text>
|
|
||||||
</g>
|
|
||||||
<!-- veterinarian->turnfeegroup -->
|
|
||||||
<g id="edge20" class="edge">
|
|
||||||
<title>veterinarian->turnfeegroup</title>
|
|
||||||
<path fill="none" stroke="#e65100" stroke-dasharray="5,2" d="M1537.74,-526C1452.2,-526 1240.63,-526 1240.63,-526 1240.63,-526 1240.63,-177 1240.63,-177 1240.63,-177 1204.58,-177 1204.58,-177"/>
|
|
||||||
<polygon fill="#e65100" stroke="#e65100" points="1204.58,-173.5 1194.58,-177 1204.58,-180.5 1204.58,-173.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="777.33" y="-379.7" font-family="Helvetica,sans-Serif" font-size="8.00">N:M</text>
|
|
||||||
</g>
|
|
||||||
<!-- specialty -->
|
|
||||||
<g id="node31" class="node">
|
|
||||||
<title>specialty</title>
|
|
||||||
<polygon fill="#cfd8dc" stroke="black" points="753.16,-151.1 753.16,-203.1 810.84,-203.1 810.84,-151.1 753.16,-151.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="782" y="-190.1" font-family="Helvetica,sans-Serif" font-size="10.00">Specialty</text>
|
|
||||||
<polyline fill="none" stroke="black" points="753.16,-183.1 810.84,-183.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="761.16" y="-170.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="761.16" y="-158.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
|
||||||
</g>
|
|
||||||
<!-- vet_specialty->specialty -->
|
|
||||||
<g id="edge15" class="edge">
|
|
||||||
<title>vet_specialty->specialty</title>
|
|
||||||
<path fill="none" stroke="#e65100" d="M1442,-337.81C1442,-315.45 1442,-293 1442,-293 1442,-293 806.51,-293 806.51,-293 806.51,-293 806.51,-214.91 806.51,-214.91"/>
|
|
||||||
<polygon fill="#e65100" stroke="#e65100" points="810.01,-214.91 806.51,-204.91 803.01,-214.91 810.01,-214.91"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="751.22" y="-300.4" font-family="Helvetica,sans-Serif" font-size="8.00">N:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- vet_neighborhood->neighborhood -->
|
|
||||||
<g id="edge17" class="edge">
|
|
||||||
<title>vet_neighborhood->neighborhood</title>
|
|
||||||
<path fill="none" stroke="#e65100" d="M800.26,-382C572.59,-382 247.21,-382 247.21,-382 247.21,-382 247.21,-226.67 247.21,-226.67"/>
|
|
||||||
<polygon fill="#e65100" stroke="#e65100" points="250.71,-226.67 247.21,-216.67 243.71,-226.67 250.71,-226.67"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="657.22" y="-300.4" font-family="Helvetica,sans-Serif" font-size="8.00">N:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- grupo -->
|
|
||||||
<g id="node11" class="node">
|
|
||||||
<title>grupo</title>
|
|
||||||
<polygon fill="#e1bee7" stroke="black" points="85.98,-1388.1 85.98,-1452.1 172.02,-1452.1 172.02,-1388.1 85.98,-1388.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="129" y="-1439.1" font-family="Helvetica,sans-Serif" font-size="10.00">Group</text>
|
|
||||||
<polyline fill="none" stroke="black" points="85.98,-1432.1 172.02,-1432.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="93.98" y="-1419.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="93.98" y="-1407.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="93.98" y="-1395.1" font-family="Helvetica,sans-Serif" font-size="10.00">description: text</text>
|
|
||||||
</g>
|
|
||||||
<!-- category -->
|
|
||||||
<g id="node12" class="node">
|
|
||||||
<title>category</title>
|
|
||||||
<polygon fill="#e1bee7" stroke="black" points="166.6,-1023.5 166.6,-1111.5 255.4,-1111.5 255.4,-1023.5 166.6,-1023.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="211" y="-1098.5" font-family="Helvetica,sans-Serif" font-size="10.00">Category</text>
|
|
||||||
<polyline fill="none" stroke="black" points="166.6,-1091.5 255.4,-1091.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="174.6" y="-1078.5" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="174.6" y="-1066.5" font-family="Helvetica,sans-Serif" font-size="10.00">group_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="174.6" y="-1054.5" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="174.6" y="-1042.5" font-family="Helvetica,sans-Serif" font-size="10.00">description: text</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="174.6" y="-1030.5" font-family="Helvetica,sans-Serif" font-size="10.00">value: int (order)</text>
|
|
||||||
</g>
|
|
||||||
<!-- grupo->category -->
|
|
||||||
<g id="edge21" class="edge">
|
|
||||||
<title>grupo->category</title>
|
|
||||||
<path fill="none" stroke="#7b1fa2" d="M172.38,-1420C177.51,-1420 180.96,-1420 180.96,-1420 180.96,-1420 180.96,-1123.3 180.96,-1123.3"/>
|
|
||||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="184.46,-1123.3 180.96,-1113.3 177.46,-1123.3 184.46,-1123.3"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="151.22" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- service -->
|
|
||||||
<g id="node13" class="node">
|
|
||||||
<title>service</title>
|
|
||||||
<polygon fill="#ce93d8" stroke="black" points="199.63,-729.3 199.63,-889.3 322.37,-889.3 322.37,-729.3 199.63,-729.3"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="261" y="-876.3" font-family="Helvetica,sans-Serif" font-size="10.00">Service</text>
|
|
||||||
<polyline fill="none" stroke="black" points="199.63,-869.3 322.37,-869.3"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="207.63" y="-856.3" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="207.63" y="-844.3" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="207.63" y="-832.3" font-family="Helvetica,sans-Serif" font-size="10.00">description: text</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="207.63" y="-820.3" font-family="Helvetica,sans-Serif" font-size="10.00">specialty_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="207.63" y="-808.3" font-family="Helvetica,sans-Serif" font-size="10.00">category_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="207.63" y="-796.3" font-family="Helvetica,sans-Serif" font-size="10.00">duration: int (min)</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="207.63" y="-784.3" font-family="Helvetica,sans-Serif" font-size="10.00">modality: onsite/online</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="207.63" y="-772.3" font-family="Helvetica,sans-Serif" font-size="10.00">payment_sign_req: bool</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="207.63" y="-760.3" font-family="Helvetica,sans-Serif" font-size="10.00">pet_type_filter: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="207.63" y="-748.3" font-family="Helvetica,sans-Serif" font-size="10.00">age_filter: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="207.63" y="-736.3" font-family="Helvetica,sans-Serif" font-size="10.00">weight_range: str</text>
|
|
||||||
</g>
|
|
||||||
<!-- category->service -->
|
|
||||||
<g id="edge22" class="edge">
|
|
||||||
<title>category->service</title>
|
|
||||||
<path fill="none" stroke="#7b1fa2" d="M227.52,-1023.11C227.52,-1023.11 227.52,-901.07 227.52,-901.07"/>
|
|
||||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="231.02,-901.07 227.52,-891.07 224.02,-901.08 231.02,-901.07"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="250.22" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- prices -->
|
|
||||||
<g id="node14" class="node">
|
|
||||||
<title>prices</title>
|
|
||||||
<polygon fill="#ba68c8" stroke="black" points="63.3,-484.7 63.3,-620.7 192.7,-620.7 192.7,-484.7 63.3,-484.7"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="128" y="-607.7" font-family="Helvetica,sans-Serif" font-size="10.00">Prices</text>
|
|
||||||
<polyline fill="none" stroke="black" points="63.3,-600.7 192.7,-600.7"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="71.3" y="-587.7" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="71.3" y="-575.7" font-family="Helvetica,sans-Serif" font-size="10.00">service_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="71.3" y="-563.7" font-family="Helvetica,sans-Serif" font-size="10.00">veterinarian_id: FK (opt)</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="71.3" y="-551.7" font-family="Helvetica,sans-Serif" font-size="10.00">price: decimal</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="71.3" y="-539.7" font-family="Helvetica,sans-Serif" font-size="10.00">professional_fee: decimal</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="71.3" y="-527.7" font-family="Helvetica,sans-Serif" font-size="10.00">payment_sign: decimal</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="71.3" y="-515.7" font-family="Helvetica,sans-Serif" font-size="10.00">from_date: date</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="71.3" y="-503.7" font-family="Helvetica,sans-Serif" font-size="10.00">to_date: date</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="71.3" y="-491.7" font-family="Helvetica,sans-Serif" font-size="10.00">active: bool</text>
|
|
||||||
</g>
|
|
||||||
<!-- service->prices -->
|
|
||||||
<g id="edge24" class="edge">
|
|
||||||
<title>service->prices</title>
|
|
||||||
<path fill="none" stroke="#7b1fa2" d="M204.92,-728.98C204.92,-673.26 204.92,-609 204.92,-609 204.92,-609 203.72,-609 203.72,-609"/>
|
|
||||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="204.41,-605.5 194.41,-609 204.41,-612.5 204.41,-605.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="184.22" y="-674.8" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- discounts -->
|
|
||||||
<g id="node15" class="node">
|
|
||||||
<title>discounts</title>
|
|
||||||
<polygon fill="#ba68c8" stroke="black" points="210.2,-502.7 210.2,-602.7 321.8,-602.7 321.8,-502.7 210.2,-502.7"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="266" y="-589.7" font-family="Helvetica,sans-Serif" font-size="10.00">Discounts</text>
|
|
||||||
<polyline fill="none" stroke="black" points="210.2,-582.7 321.8,-582.7"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="218.2" y="-569.7" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="218.2" y="-557.7" font-family="Helvetica,sans-Serif" font-size="10.00">service_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="218.2" y="-545.7" font-family="Helvetica,sans-Serif" font-size="10.00">discount: decimal (%)</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="218.2" y="-533.7" font-family="Helvetica,sans-Serif" font-size="10.00">from_date: date</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="218.2" y="-521.7" font-family="Helvetica,sans-Serif" font-size="10.00">to_date: date</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="218.2" y="-509.7" font-family="Helvetica,sans-Serif" font-size="10.00">active: bool</text>
|
|
||||||
</g>
|
|
||||||
<!-- service->discounts -->
|
|
||||||
<g id="edge25" class="edge">
|
|
||||||
<title>service->discounts</title>
|
|
||||||
<path fill="none" stroke="#7b1fa2" d="M247.4,-728.81C247.4,-728.81 247.4,-614.57 247.4,-614.57"/>
|
|
||||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="250.9,-614.57 247.4,-604.57 243.9,-614.57 250.9,-614.57"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="270.22" y="-674.8" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- service->specialty -->
|
|
||||||
<g id="edge23" class="edge">
|
|
||||||
<title>service->specialty</title>
|
|
||||||
<path fill="none" stroke="#7b1fa2" stroke-dasharray="5,2" d="M284.6,-728.87C284.6,-675.42 284.6,-615 284.6,-615 284.6,-615 777.67,-615 777.67,-615 777.67,-615 777.67,-215.02 777.67,-215.02"/>
|
|
||||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="781.17,-215.02 777.67,-205.02 774.17,-215.02 781.17,-215.02"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="538.9" y="-459" font-family="Helvetica,sans-Serif" font-size="8.00">N:1 opt</text>
|
|
||||||
</g>
|
|
||||||
<!-- prices->veterinarian -->
|
|
||||||
<g id="edge26" class="edge">
|
|
||||||
<title>prices->veterinarian</title>
|
|
||||||
<path fill="none" stroke="#7b1fa2" stroke-dasharray="5,2" d="M192.84,-493C472.5,-493 1561.35,-493 1561.35,-493 1561.35,-493 1561.35,-496.86 1561.35,-496.86"/>
|
|
||||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="1557.85,-496.86 1561.35,-506.86 1564.85,-496.86 1557.85,-496.86"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="434" y="-679.6" font-family="Helvetica,sans-Serif" font-size="8.00">N:1 opt</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="434" y="-670" font-family="Helvetica,sans-Serif" font-size="8.00">(vet-specific)</text>
|
|
||||||
</g>
|
|
||||||
<!-- servicecombo -->
|
|
||||||
<g id="node16" class="node">
|
|
||||||
<title>servicecombo</title>
|
|
||||||
<polygon fill="#e1bee7" stroke="black" points="189.91,-1376.1 189.91,-1464.1 322.09,-1464.1 322.09,-1376.1 189.91,-1376.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="256" y="-1451.1" font-family="Helvetica,sans-Serif" font-size="10.00">ServiceCombo</text>
|
|
||||||
<polyline fill="none" stroke="black" points="189.91,-1444.1 322.09,-1444.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="197.91" y="-1431.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="197.91" y="-1419.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="197.91" y="-1407.1" font-family="Helvetica,sans-Serif" font-size="10.00">description: text</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="197.91" y="-1395.1" font-family="Helvetica,sans-Serif" font-size="10.00">discount_percent: decimal</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="197.91" y="-1383.1" font-family="Helvetica,sans-Serif" font-size="10.00">discount_fixed: decimal</text>
|
|
||||||
</g>
|
|
||||||
<!-- cart->veterinarian -->
|
|
||||||
<g id="edge27" class="edge">
|
|
||||||
<title>cart->veterinarian</title>
|
|
||||||
<path fill="none" stroke="#00838f" stroke-dasharray="5,2" d="M949.06,-1375.77C949.06,-1202.29 949.06,-579 949.06,-579 949.06,-579 1526.15,-579 1526.15,-579"/>
|
|
||||||
<polygon fill="#00838f" stroke="#00838f" points="1526.15,-582.5 1536.15,-579 1526.15,-575.5 1526.15,-582.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1789.12" y="-943.8" font-family="Helvetica,sans-Serif" font-size="8.00">N:1 opt</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1789.12" y="-934.2" font-family="Helvetica,sans-Serif" font-size="8.00">(assigned vet)</text>
|
|
||||||
</g>
|
|
||||||
<!-- cartitem -->
|
|
||||||
<g id="node18" class="node">
|
|
||||||
<title>cartitem</title>
|
|
||||||
<polygon fill="#80deea" stroke="black" points="735.93,-1011.5 735.93,-1123.5 838.07,-1123.5 838.07,-1011.5 735.93,-1011.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="787" y="-1110.5" font-family="Helvetica,sans-Serif" font-size="10.00">CartItem</text>
|
|
||||||
<polyline fill="none" stroke="black" points="735.93,-1103.5 838.07,-1103.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="743.93" y="-1090.5" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="743.93" y="-1078.5" font-family="Helvetica,sans-Serif" font-size="10.00">cart_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="743.93" y="-1066.5" font-family="Helvetica,sans-Serif" font-size="10.00">pet_id: FK (opt)</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="743.93" y="-1054.5" font-family="Helvetica,sans-Serif" font-size="10.00">service_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="743.93" y="-1042.5" font-family="Helvetica,sans-Serif" font-size="10.00">price: decimal</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="743.93" y="-1030.5" font-family="Helvetica,sans-Serif" font-size="10.00">quantity: int</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="743.93" y="-1018.5" font-family="Helvetica,sans-Serif" font-size="10.00">total: decimal (calc)</text>
|
|
||||||
</g>
|
|
||||||
<!-- cart->cartitem -->
|
|
||||||
<g id="edge28" class="edge">
|
|
||||||
<title>cart->cartitem</title>
|
|
||||||
<path fill="none" stroke="#00838f" d="M923.27,-1375.74C923.27,-1291.12 923.27,-1115 923.27,-1115 923.27,-1115 850.03,-1115 850.03,-1115"/>
|
|
||||||
<polygon fill="#00838f" stroke="#00838f" points="850.03,-1111.5 840.03,-1115 850.03,-1118.5 850.03,-1111.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="862.22" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- cartresumeitem -->
|
|
||||||
<g id="node19" class="node">
|
|
||||||
<title>cartresumeitem</title>
|
|
||||||
<polygon fill="#80deea" stroke="black" points="957.93,-999.5 957.93,-1135.5 1070.07,-1135.5 1070.07,-999.5 957.93,-999.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1014" y="-1122.5" font-family="Helvetica,sans-Serif" font-size="10.00">CartResumeItem</text>
|
|
||||||
<polyline fill="none" stroke="black" points="957.93,-1115.5 1070.07,-1115.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="965.93" y="-1102.5" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="965.93" y="-1090.5" font-family="Helvetica,sans-Serif" font-size="10.00">cart_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="965.93" y="-1078.5" font-family="Helvetica,sans-Serif" font-size="10.00">concept: SUBTOTAL/</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="965.93" y="-1066.5" font-family="Helvetica,sans-Serif" font-size="10.00"> DESCUENTO/</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="965.93" y="-1054.5" font-family="Helvetica,sans-Serif" font-size="10.00"> ADELANTO/</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="965.93" y="-1042.5" font-family="Helvetica,sans-Serif" font-size="10.00"> TOTAL/</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="965.93" y="-1030.5" font-family="Helvetica,sans-Serif" font-size="10.00"> COSTO_SERVICIO</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="965.93" y="-1018.5" font-family="Helvetica,sans-Serif" font-size="10.00">amount: decimal</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="965.93" y="-1006.5" font-family="Helvetica,sans-Serif" font-size="10.00">order: int</text>
|
|
||||||
</g>
|
|
||||||
<!-- cart->cartresumeitem -->
|
|
||||||
<g id="edge29" class="edge">
|
|
||||||
<title>cart->cartresumeitem</title>
|
|
||||||
<path fill="none" stroke="#00838f" d="M981.83,-1375.8C981.83,-1375.8 981.83,-1147.31 981.83,-1147.31"/>
|
|
||||||
<polygon fill="#00838f" stroke="#00838f" points="985.33,-1147.31 981.83,-1137.31 978.33,-1147.31 985.33,-1147.31"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="990.22" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- cartpetreason -->
|
|
||||||
<g id="node20" class="node">
|
|
||||||
<title>cartpetreason</title>
|
|
||||||
<polygon fill="#80deea" stroke="black" points="855.82,-1029.5 855.82,-1105.5 940.18,-1105.5 940.18,-1029.5 855.82,-1029.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="898" y="-1092.5" font-family="Helvetica,sans-Serif" font-size="10.00">CartPetReason</text>
|
|
||||||
<polyline fill="none" stroke="black" points="855.82,-1085.5 940.18,-1085.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="863.82" y="-1072.5" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="863.82" y="-1060.5" font-family="Helvetica,sans-Serif" font-size="10.00">cart_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="863.82" y="-1048.5" font-family="Helvetica,sans-Serif" font-size="10.00">pet_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="863.82" y="-1036.5" font-family="Helvetica,sans-Serif" font-size="10.00">reason: text</text>
|
|
||||||
</g>
|
|
||||||
<!-- cart->cartpetreason -->
|
|
||||||
<g id="edge30" class="edge">
|
|
||||||
<title>cart->cartpetreason</title>
|
|
||||||
<path fill="none" stroke="#00838f" d="M931.73,-1375.8C931.73,-1375.8 931.73,-1117.47 931.73,-1117.47"/>
|
|
||||||
<polygon fill="#00838f" stroke="#00838f" points="935.23,-1117.47 931.73,-1107.47 928.23,-1117.47 935.23,-1117.47"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="919.22" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- cartitem->pet -->
|
|
||||||
<g id="edge32" class="edge">
|
|
||||||
<title>cartitem->pet</title>
|
|
||||||
<path fill="none" stroke="#00838f" stroke-dasharray="5,2" d="M747.42,-1011.12C747.42,-964.19 747.42,-905 747.42,-905 747.42,-905 692.94,-905 692.94,-905"/>
|
|
||||||
<polygon fill="#00838f" stroke="#00838f" points="692.94,-901.5 682.94,-905 692.94,-908.5 692.94,-901.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="640.9" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">N:1 opt</text>
|
|
||||||
</g>
|
|
||||||
<!-- cartitem->service -->
|
|
||||||
<g id="edge31" class="edge">
|
|
||||||
<title>cartitem->service</title>
|
|
||||||
<path fill="none" stroke="#00838f" d="M741.67,-1011.15C741.67,-997.62 741.67,-987 741.67,-987 741.67,-987 288.75,-987 288.75,-987 288.75,-987 288.75,-901.09 288.75,-901.09"/>
|
|
||||||
<polygon fill="#00838f" stroke="#00838f" points="292.25,-901.09 288.75,-891.09 285.25,-901.09 292.25,-901.09"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="446.22" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">N:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- cartpetreason->pet -->
|
|
||||||
<g id="edge33" class="edge">
|
|
||||||
<title>cartpetreason->pet</title>
|
|
||||||
<path fill="none" stroke="#00838f" d="M898,-1029.01C898,-978.45 898,-896 898,-896 898,-896 692.86,-896 692.86,-896"/>
|
|
||||||
<polygon fill="#00838f" stroke="#00838f" points="692.86,-892.5 682.86,-896 692.86,-899.5 692.86,-892.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="683.22" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">N:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- servicerequest->veterinarian -->
|
|
||||||
<g id="edge35" class="edge">
|
|
||||||
<title>servicerequest->veterinarian</title>
|
|
||||||
<path fill="none" stroke="#c62828" stroke-dasharray="5,2" d="M1895.22,-975C1780.2,-975 1561.26,-975 1561.26,-975 1561.26,-975 1561.26,-608.61 1561.26,-608.61"/>
|
|
||||||
<polygon fill="#c62828" stroke="#c62828" points="1564.76,-608.61 1561.26,-598.61 1557.76,-608.61 1564.76,-608.61"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1858.68" y="-811.7" font-family="Helvetica,sans-Serif" font-size="8.00">N:1 opt</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1858.68" y="-802.1" font-family="Helvetica,sans-Serif" font-size="8.00">(assigned)</text>
|
|
||||||
</g>
|
|
||||||
<!-- servicerequest->cart -->
|
|
||||||
<g id="edge34" class="edge">
|
|
||||||
<title>servicerequest->cart</title>
|
|
||||||
<path fill="none" stroke="#c62828" d="M1895.08,-1154C1675.36,-1154 1005.73,-1154 1005.73,-1154 1005.73,-1154 1005.73,-1364.35 1005.73,-1364.35"/>
|
|
||||||
<polygon fill="#c62828" stroke="#c62828" points="1002.23,-1364.35 1005.73,-1374.35 1009.23,-1364.35 1002.23,-1364.35"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1160.56" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- statehistory -->
|
|
||||||
<g id="node22" class="node">
|
|
||||||
<title>statehistory</title>
|
|
||||||
<polygon fill="#ef9a9a" stroke="black" points="2182.86,-759.3 2182.86,-859.3 2301.14,-859.3 2301.14,-759.3 2182.86,-759.3"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2242" y="-846.3" font-family="Helvetica,sans-Serif" font-size="10.00">StateHistory</text>
|
|
||||||
<polyline fill="none" stroke="black" points="2182.86,-839.3 2301.14,-839.3"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="2190.86" y="-826.3" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="2190.86" y="-814.3" font-family="Helvetica,sans-Serif" font-size="10.00">service_request_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="2190.86" y="-802.3" font-family="Helvetica,sans-Serif" font-size="10.00">state: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="2190.86" y="-790.3" font-family="Helvetica,sans-Serif" font-size="10.00">additional_data: JSON</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="2190.86" y="-778.3" font-family="Helvetica,sans-Serif" font-size="10.00">created_at: datetime</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="2190.86" y="-766.3" font-family="Helvetica,sans-Serif" font-size="10.00">user_id: FK</text>
|
|
||||||
</g>
|
|
||||||
<!-- servicerequest->statehistory -->
|
|
||||||
<g id="edge36" class="edge">
|
|
||||||
<title>servicerequest->statehistory</title>
|
|
||||||
<path fill="none" stroke="#c62828" d="M2033.08,-987C2115.41,-987 2242,-987 2242,-987 2242,-987 2242,-871.26 2242,-871.26"/>
|
|
||||||
<polygon fill="#c62828" stroke="#c62828" points="2245.5,-871.26 2242,-861.26 2238.5,-871.26 2245.5,-871.26"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2179.01" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">1:N audit</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetasked -->
|
|
||||||
<g id="node23" class="node">
|
|
||||||
<title>vetasked</title>
|
|
||||||
<polygon fill="#ef9a9a" stroke="black" points="1894.85,-759.3 1894.85,-859.3 2023.15,-859.3 2023.15,-759.3 1894.85,-759.3"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1959" y="-846.3" font-family="Helvetica,sans-Serif" font-size="10.00">VeterinarianAsked</text>
|
|
||||||
<polyline fill="none" stroke="black" points="1894.85,-839.3 2023.15,-839.3"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1902.85" y="-826.3" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1902.85" y="-814.3" font-family="Helvetica,sans-Serif" font-size="10.00">service_request_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1902.85" y="-802.3" font-family="Helvetica,sans-Serif" font-size="10.00">veterinarian_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1902.85" y="-790.3" font-family="Helvetica,sans-Serif" font-size="10.00">date_asked: datetime</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1902.85" y="-778.3" font-family="Helvetica,sans-Serif" font-size="10.00">date_answered: datetime</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1902.85" y="-766.3" font-family="Helvetica,sans-Serif" font-size="10.00">accepted: bool</text>
|
|
||||||
</g>
|
|
||||||
<!-- servicerequest->vetasked -->
|
|
||||||
<g id="edge37" class="edge">
|
|
||||||
<title>servicerequest->vetasked</title>
|
|
||||||
<path fill="none" stroke="#c62828" d="M1959.27,-969.15C1959.27,-969.15 1959.27,-871.22 1959.27,-871.22"/>
|
|
||||||
<polygon fill="#c62828" stroke="#c62828" points="1962.77,-871.22 1959.27,-861.22 1955.77,-871.22 1962.77,-871.22"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1967.22" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- payreminder -->
|
|
||||||
<g id="node25" class="node">
|
|
||||||
<title>payreminder</title>
|
|
||||||
<polygon fill="#e57373" stroke="black" points="2041.63,-765.3 2041.63,-853.3 2164.37,-853.3 2164.37,-765.3 2041.63,-765.3"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2103" y="-840.3" font-family="Helvetica,sans-Serif" font-size="10.00">PaymentReminder</text>
|
|
||||||
<polyline fill="none" stroke="black" points="2041.63,-833.3 2164.37,-833.3"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="2049.63" y="-820.3" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="2049.63" y="-808.3" font-family="Helvetica,sans-Serif" font-size="10.00">service_request_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="2049.63" y="-796.3" font-family="Helvetica,sans-Serif" font-size="10.00">scheduled_for: datetime</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="2049.63" y="-784.3" font-family="Helvetica,sans-Serif" font-size="10.00">processed_at: datetime</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="2049.63" y="-772.3" font-family="Helvetica,sans-Serif" font-size="10.00">status: pending/sent/...</text>
|
|
||||||
</g>
|
|
||||||
<!-- servicerequest->payreminder -->
|
|
||||||
<g id="edge38" class="edge">
|
|
||||||
<title>servicerequest->payreminder</title>
|
|
||||||
<path fill="none" stroke="#c62828" d="M2029.45,-969.03C2029.45,-895.98 2029.45,-809 2029.45,-809 2029.45,-809 2030.63,-809 2030.63,-809"/>
|
|
||||||
<polygon fill="#c62828" stroke="#c62828" points="2029.75,-812.5 2039.75,-809 2029.75,-805.5 2029.75,-812.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2055.22" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- servicerequest->campaign -->
|
|
||||||
<g id="edge39" class="edge">
|
|
||||||
<title>servicerequest->campaign</title>
|
|
||||||
<path fill="none" stroke="#666666" stroke-dasharray="5,2" d="M2026.3,-969.29C2026.3,-754.09 2026.3,-260 2026.3,-260 2026.3,-260 874,-260 874,-260 874,-260 874,-232.98 874,-232.98"/>
|
|
||||||
<polygon fill="#666666" stroke="#666666" points="877.5,-232.98 874,-222.98 870.5,-232.98 877.5,-232.98"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2341.9" y="-550.3" font-family="Helvetica,sans-Serif" font-size="8.00">N:1 opt</text>
|
|
||||||
</g>
|
|
||||||
<!-- tag -->
|
|
||||||
<g id="node39" class="node">
|
|
||||||
<title>tag</title>
|
|
||||||
<polygon fill="#cfd8dc" stroke="black" points="638.43,-151.1 638.43,-203.1 735.57,-203.1 735.57,-151.1 638.43,-151.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="687" y="-190.1" font-family="Helvetica,sans-Serif" font-size="10.00">Tag</text>
|
|
||||||
<polyline fill="none" stroke="black" points="638.43,-183.1 735.57,-183.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="646.43" y="-170.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="646.43" y="-158.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str (unique)</text>
|
|
||||||
</g>
|
|
||||||
<!-- servicerequest->tag -->
|
|
||||||
<g id="edge40" class="edge">
|
|
||||||
<title>servicerequest->tag</title>
|
|
||||||
<path fill="none" stroke="#666666" d="M1895.27,-981C1634.21,-981 718.72,-981 718.72,-981 718.72,-981 718.72,-214.9 718.72,-214.9"/>
|
|
||||||
<polygon fill="#666666" stroke="#666666" points="722.22,-214.9 718.72,-204.9 715.22,-214.9 722.22,-214.9"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="756.33" y="-550.3" font-family="Helvetica,sans-Serif" font-size="8.00">N:M</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetasked->veterinarian -->
|
|
||||||
<g id="edge41" class="edge">
|
|
||||||
<title>vetasked->veterinarian</title>
|
|
||||||
<path fill="none" stroke="#c62828" d="M1894.62,-765C1787.53,-765 1584.47,-765 1584.47,-765 1584.47,-765 1584.47,-608.46 1584.47,-608.46"/>
|
|
||||||
<polygon fill="#c62828" stroke="#c62828" points="1587.97,-608.46 1584.47,-598.46 1580.97,-608.46 1587.97,-608.46"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1848.22" y="-674.8" font-family="Helvetica,sans-Serif" font-size="8.00">N:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetreminder -->
|
|
||||||
<g id="node24" class="node">
|
|
||||||
<title>vetreminder</title>
|
|
||||||
<polygon fill="#e57373" stroke="black" points="1897.63,-502.7 1897.63,-602.7 2020.37,-602.7 2020.37,-502.7 1897.63,-502.7"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1959" y="-589.7" font-family="Helvetica,sans-Serif" font-size="10.00">ScheduledVetReminder</text>
|
|
||||||
<polyline fill="none" stroke="black" points="1897.63,-582.7 2020.37,-582.7"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1905.63" y="-569.7" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1905.63" y="-557.7" font-family="Helvetica,sans-Serif" font-size="10.00">vet_asked_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1905.63" y="-545.7" font-family="Helvetica,sans-Serif" font-size="10.00">scheduled_for: datetime</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1905.63" y="-533.7" font-family="Helvetica,sans-Serif" font-size="10.00">processed_at: datetime</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1905.63" y="-521.7" font-family="Helvetica,sans-Serif" font-size="10.00">status: pending/sent/...</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1905.63" y="-509.7" font-family="Helvetica,sans-Serif" font-size="10.00">celery_task_id: str</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetasked->vetreminder -->
|
|
||||||
<g id="edge42" class="edge">
|
|
||||||
<title>vetasked->vetreminder</title>
|
|
||||||
<path fill="none" stroke="#c62828" d="M1959,-758.91C1959,-758.91 1959,-614.49 1959,-614.49"/>
|
|
||||||
<polygon fill="#c62828" stroke="#c62828" points="1962.5,-614.49 1959,-604.49 1955.5,-614.49 1962.5,-614.49"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1965.22" y="-674.8" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisit->veterinarian -->
|
|
||||||
<g id="edge44" class="edge">
|
|
||||||
<title>vetvisit->veterinarian</title>
|
|
||||||
<path fill="none" stroke="#f9a825" d="M1617.03,-1273.86C1617.03,-1031.98 1617.03,-575 1617.03,-575 1617.03,-575 1616.13,-575 1616.13,-575"/>
|
|
||||||
<polygon fill="#f9a825" stroke="#f9a825" points="1619.63,-571.5 1609.63,-575 1619.63,-578.5 1619.63,-571.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2373.22" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">N:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisit->servicerequest -->
|
|
||||||
<g id="edge43" class="edge">
|
|
||||||
<title>vetvisit->servicerequest</title>
|
|
||||||
<path fill="none" stroke="#f9a825" stroke-dasharray="5,2" d="M1695.23,-1273.71C1695.23,-1214.56 1695.23,-1160 1695.23,-1160 1695.23,-1160 1883.62,-1160 1883.62,-1160"/>
|
|
||||||
<polygon fill="#f9a825" stroke="#f9a825" points="1883.62,-1163.5 1893.62,-1160 1883.62,-1156.5 1883.62,-1163.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1864.12" y="-1248.4" font-family="Helvetica,sans-Serif" font-size="8.00">1:1 opt</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1864.12" y="-1238.8" font-family="Helvetica,sans-Serif" font-size="8.00">(from request)</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisitreport -->
|
|
||||||
<g id="node27" class="node">
|
|
||||||
<title>vetvisitreport</title>
|
|
||||||
<polygon fill="#fff59d" stroke="black" points="1626.09,-1005.5 1626.09,-1129.5 1729.91,-1129.5 1729.91,-1005.5 1626.09,-1005.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1678" y="-1116.5" font-family="Helvetica,sans-Serif" font-size="10.00">VetVisitReport</text>
|
|
||||||
<polyline fill="none" stroke="black" points="1626.09,-1109.5 1729.91,-1109.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1634.09" y="-1096.5" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1634.09" y="-1084.5" font-family="Helvetica,sans-Serif" font-size="10.00">visit_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1634.09" y="-1072.5" font-family="Helvetica,sans-Serif" font-size="10.00">pet_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1634.09" y="-1060.5" font-family="Helvetica,sans-Serif" font-size="10.00">reason: text</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1634.09" y="-1048.5" font-family="Helvetica,sans-Serif" font-size="10.00">physical_exam: text</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1634.09" y="-1036.5" font-family="Helvetica,sans-Serif" font-size="10.00">diagnosis: text</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1634.09" y="-1024.5" font-family="Helvetica,sans-Serif" font-size="10.00">treatment: text</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1634.09" y="-1012.5" font-family="Helvetica,sans-Serif" font-size="10.00">pdf_file: file</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisit->vetvisitreport -->
|
|
||||||
<g id="edge47" class="edge">
|
|
||||||
<title>vetvisit->vetvisitreport</title>
|
|
||||||
<path fill="none" stroke="#f9a825" d="M1660.66,-1273.63C1660.66,-1273.63 1660.66,-1141.19 1660.66,-1141.19"/>
|
|
||||||
<polygon fill="#f9a825" stroke="#f9a825" points="1664.16,-1141.19 1660.66,-1131.19 1657.16,-1141.19 1664.16,-1141.19"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1675.22" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisitpetreason -->
|
|
||||||
<g id="node29" class="node">
|
|
||||||
<title>vetvisitpetreason</title>
|
|
||||||
<polygon fill="#fff176" stroke="black" points="1508.31,-1029.5 1508.31,-1105.5 1607.69,-1105.5 1607.69,-1029.5 1508.31,-1029.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1558" y="-1092.5" font-family="Helvetica,sans-Serif" font-size="10.00">VetVisitPetReason</text>
|
|
||||||
<polyline fill="none" stroke="black" points="1508.31,-1085.5 1607.69,-1085.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1516.31" y="-1072.5" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1516.31" y="-1060.5" font-family="Helvetica,sans-Serif" font-size="10.00">visit_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1516.31" y="-1048.5" font-family="Helvetica,sans-Serif" font-size="10.00">pet_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1516.31" y="-1036.5" font-family="Helvetica,sans-Serif" font-size="10.00">reason: text</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisit->vetvisitpetreason -->
|
|
||||||
<g id="edge48" class="edge">
|
|
||||||
<title>vetvisit->vetvisitpetreason</title>
|
|
||||||
<path fill="none" stroke="#f9a825" d="M1597.94,-1273.63C1597.94,-1273.63 1597.94,-1117.18 1597.94,-1117.18"/>
|
|
||||||
<polygon fill="#f9a825" stroke="#f9a825" points="1601.44,-1117.18 1597.94,-1107.18 1594.44,-1117.18 1601.44,-1117.18"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1598.22" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- visit_pets -->
|
|
||||||
<g id="node30" class="node">
|
|
||||||
<title>visit_pets</title>
|
|
||||||
<polygon fill="#ffee58" stroke="black" points="1336,-1111.5 1181.81,-1067.5 1336,-1023.5 1490.19,-1067.5 1336,-1111.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1210.47" y="-1076.5" font-family="Helvetica,sans-Serif" font-size="10.00">{M2M: Visit-Pets|vetvisit_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1210.47" y="-1064.5" font-family="Helvetica,sans-Serif" font-size="10.00">pet_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1336" y="-1052.5" font-family="Helvetica,sans-Serif" font-size="10.00">}</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisit->visit_pets -->
|
|
||||||
<g id="edge45" class="edge">
|
|
||||||
<title>vetvisit->visit_pets</title>
|
|
||||||
<path fill="none" stroke="#f9a825" d="M1587.72,-1308C1492.6,-1308 1336,-1308 1336,-1308 1336,-1308 1336,-1123.41 1336,-1123.41"/>
|
|
||||||
<polygon fill="#f9a825" stroke="#f9a825" points="1339.5,-1123.41 1336,-1113.41 1332.5,-1123.41 1339.5,-1123.41"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1476.22" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- receipt -->
|
|
||||||
<g id="node42" class="node">
|
|
||||||
<title>receipt</title>
|
|
||||||
<polygon fill="#f8bbd9" stroke="black" points="2394.58,-1005.5 2394.58,-1129.5 2523.42,-1129.5 2523.42,-1005.5 2394.58,-1005.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2459" y="-1116.5" font-family="Helvetica,sans-Serif" font-size="10.00">Receipt</text>
|
|
||||||
<polyline fill="none" stroke="black" points="2394.58,-1109.5 2523.42,-1109.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="2402.58" y="-1096.5" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="2402.58" y="-1084.5" font-family="Helvetica,sans-Serif" font-size="10.00">document_number: bigint</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="2402.58" y="-1072.5" font-family="Helvetica,sans-Serif" font-size="10.00">receipt_number: int</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="2402.58" y="-1060.5" font-family="Helvetica,sans-Serif" font-size="10.00">issued_date: date</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="2402.58" y="-1048.5" font-family="Helvetica,sans-Serif" font-size="10.00">total_amount: decimal</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="2402.58" y="-1036.5" font-family="Helvetica,sans-Serif" font-size="10.00">net_taxed: decimal</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="2402.58" y="-1024.5" font-family="Helvetica,sans-Serif" font-size="10.00">cae: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="2402.58" y="-1012.5" font-family="Helvetica,sans-Serif" font-size="10.00">cae_expiration: date</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisit->receipt -->
|
|
||||||
<g id="edge52" class="edge">
|
|
||||||
<title>vetvisit->receipt</title>
|
|
||||||
<path fill="none" stroke="#ad1457" stroke-dasharray="5,2" d="M1729.99,-1420C1925.78,-1420 2459,-1420 2459,-1420 2459,-1420 2459,-1141.22 2459,-1141.22"/>
|
|
||||||
<polygon fill="#ad1457" stroke="#ad1457" points="2462.5,-1141.22 2459,-1131.22 2455.5,-1141.22 2462.5,-1141.22"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2444.12" y="-1248.4" font-family="Helvetica,sans-Serif" font-size="8.00">1:1 opt</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2444.12" y="-1238.8" font-family="Helvetica,sans-Serif" font-size="8.00">(invoice)</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisitreport->pet -->
|
|
||||||
<g id="edge49" class="edge">
|
|
||||||
<title>vetvisitreport->pet</title>
|
|
||||||
<path fill="none" stroke="#f9a825" d="M1662.74,-1005.24C1662.74,-947.02 1662.74,-869 1662.74,-869 1662.74,-869 692.97,-869 692.97,-869"/>
|
|
||||||
<polygon fill="#f9a825" stroke="#f9a825" points="692.97,-865.5 682.97,-869 692.97,-872.5 692.97,-865.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1237.22" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">N:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisitfollowup -->
|
|
||||||
<g id="node28" class="node">
|
|
||||||
<title>vetvisitfollowup</title>
|
|
||||||
<polygon fill="#fff176" stroke="black" points="1632.21,-771.3 1632.21,-847.3 1723.79,-847.3 1723.79,-771.3 1632.21,-771.3"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1678" y="-834.3" font-family="Helvetica,sans-Serif" font-size="10.00">VetVisitFollowUp</text>
|
|
||||||
<polyline fill="none" stroke="black" points="1632.21,-827.3 1723.79,-827.3"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1640.21" y="-814.3" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1640.21" y="-802.3" font-family="Helvetica,sans-Serif" font-size="10.00">report_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1640.21" y="-790.3" font-family="Helvetica,sans-Serif" font-size="10.00">date: date</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="1640.21" y="-778.3" font-family="Helvetica,sans-Serif" font-size="10.00">description: text</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisitreport->vetvisitfollowup -->
|
|
||||||
<g id="edge50" class="edge">
|
|
||||||
<title>vetvisitreport->vetvisitfollowup</title>
|
|
||||||
<path fill="none" stroke="#f9a825" d="M1693.26,-1005.33C1693.26,-1005.33 1693.26,-859.22 1693.26,-859.22"/>
|
|
||||||
<polygon fill="#f9a825" stroke="#f9a825" points="1696.76,-859.22 1693.26,-849.22 1689.76,-859.22 1696.76,-859.22"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1684.22" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisitpetreason->pet -->
|
|
||||||
<g id="edge51" class="edge">
|
|
||||||
<title>vetvisitpetreason->pet</title>
|
|
||||||
<path fill="none" stroke="#f9a825" d="M1523.18,-1029.15C1523.18,-973.78 1523.18,-878 1523.18,-878 1523.18,-878 692.83,-878 692.83,-878"/>
|
|
||||||
<polygon fill="#f9a825" stroke="#f9a825" points="692.83,-874.5 682.83,-878 692.83,-881.5 692.83,-874.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="753.22" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">N:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- visit_pets->pet -->
|
|
||||||
<g id="edge46" class="edge">
|
|
||||||
<title>visit_pets->pet</title>
|
|
||||||
<path fill="none" stroke="#f9a825" d="M1198.48,-1062.29C1198.48,-1032.7 1198.48,-887 1198.48,-887 1198.48,-887 692.93,-887 692.93,-887"/>
|
|
||||||
<polygon fill="#f9a825" stroke="#f9a825" points="692.93,-883.5 682.93,-887 692.93,-890.5 692.93,-883.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="718.22" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">N:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- province -->
|
|
||||||
<g id="node33" class="node">
|
|
||||||
<title>province</title>
|
|
||||||
<polygon fill="#cfd8dc" stroke="black" points="302.16,-151.1 302.16,-203.1 359.84,-203.1 359.84,-151.1 302.16,-151.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="331" y="-190.1" font-family="Helvetica,sans-Serif" font-size="10.00">Province</text>
|
|
||||||
<polyline fill="none" stroke="black" points="302.16,-183.1 359.84,-183.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="310.16" y="-170.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="310.16" y="-158.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
|
||||||
</g>
|
|
||||||
<!-- locality -->
|
|
||||||
<g id="node34" class="node">
|
|
||||||
<title>locality</title>
|
|
||||||
<polygon fill="#cfd8dc" stroke="black" points="288.26,-16.5 288.26,-80.5 373.74,-80.5 373.74,-16.5 288.26,-16.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="331" y="-67.5" font-family="Helvetica,sans-Serif" font-size="10.00">Locality</text>
|
|
||||||
<polyline fill="none" stroke="black" points="288.26,-60.5 373.74,-60.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="296.26" y="-47.5" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="296.26" y="-35.5" font-family="Helvetica,sans-Serif" font-size="10.00">province_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="296.26" y="-23.5" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
|
||||||
</g>
|
|
||||||
<!-- province->locality -->
|
|
||||||
<g id="edge53" class="edge">
|
|
||||||
<title>province->locality</title>
|
|
||||||
<path fill="none" stroke="#666666" d="M331,-150.77C331,-150.77 331,-92.24 331,-92.24"/>
|
|
||||||
<polygon fill="#666666" stroke="#666666" points="334.5,-92.24 331,-82.24 327.5,-92.24 334.5,-92.24"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="337.22" y="-101.4" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- medication -->
|
|
||||||
<g id="node40" class="node">
|
|
||||||
<title>medication</title>
|
|
||||||
<polygon fill="#cfd8dc" stroke="black" points="937.19,-127.1 937.19,-227.1 1048.81,-227.1 1048.81,-127.1 937.19,-127.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="993" y="-214.1" font-family="Helvetica,sans-Serif" font-size="10.00">Medication</text>
|
|
||||||
<polyline fill="none" stroke="black" points="937.19,-207.1 1048.81,-207.1"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="945.19" y="-194.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="945.19" y="-182.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="945.19" y="-170.1" font-family="Helvetica,sans-Serif" font-size="10.00">general_name_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="945.19" y="-158.1" font-family="Helvetica,sans-Serif" font-size="10.00">type_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="945.19" y="-146.1" font-family="Helvetica,sans-Serif" font-size="10.00">presentation_id: FK</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="945.19" y="-134.1" font-family="Helvetica,sans-Serif" font-size="10.00">pet_type: str</text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 84 KiB |
@@ -1,195 +0,0 @@
|
|||||||
digraph DataModelSimple {
|
|
||||||
rankdir=TB
|
|
||||||
compound=true
|
|
||||||
splines=ortho
|
|
||||||
node [shape=box, style="rounded,filled", fontname="Helvetica", fontsize=10]
|
|
||||||
edge [fontname="Helvetica", fontsize=8]
|
|
||||||
nodesep=0.3
|
|
||||||
ranksep=1.2
|
|
||||||
newrank=true
|
|
||||||
|
|
||||||
label="AMAR Mascotas - Data Model Overview"
|
|
||||||
labelloc="t"
|
|
||||||
fontsize=16
|
|
||||||
fontname="Helvetica-Bold"
|
|
||||||
|
|
||||||
// === ROW 1: AUTH (center) ===
|
|
||||||
subgraph cluster_auth {
|
|
||||||
label="Users & Auth"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#E8F5E9"
|
|
||||||
color="#2E7D32"
|
|
||||||
auth_user [label="auth.User", fillcolor="#C8E6C9"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// === ROW 2: LEFT COLUMN - Pet Owners ===
|
|
||||||
subgraph cluster_mascotas {
|
|
||||||
label="Pet Owners & Pets"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#E3F2FD"
|
|
||||||
color="#1565C0"
|
|
||||||
|
|
||||||
petowner [label="PetOwner", fillcolor="#BBDEFB"]
|
|
||||||
pet [label="Pet", fillcolor="#BBDEFB"]
|
|
||||||
petvaccine [label="PetVaccine", fillcolor="#90CAF9"]
|
|
||||||
petstudy [label="PetStudy", fillcolor="#90CAF9"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// === ROW 2: CENTER COLUMN - Veterinarians ===
|
|
||||||
subgraph cluster_vets {
|
|
||||||
label="Veterinarians"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#FFF3E0"
|
|
||||||
color="#E65100"
|
|
||||||
|
|
||||||
veterinarian [label="Veterinarian", fillcolor="#FFE0B2"]
|
|
||||||
availability [label="Availability", fillcolor="#FFCC80"]
|
|
||||||
unavailability [label="Unavailability", fillcolor="#FFCC80"]
|
|
||||||
vet_specialty [label="Vet-Specialty", shape=diamond, fillcolor="#FFB74D"]
|
|
||||||
vet_neighborhood [label="Vet-Neighborhood", shape=diamond, fillcolor="#FFB74D"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// === ROW 2: RIGHT COLUMN - Services ===
|
|
||||||
subgraph cluster_productos {
|
|
||||||
label="Services & Pricing"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#F3E5F5"
|
|
||||||
color="#7B1FA2"
|
|
||||||
|
|
||||||
grupo [label="Group", fillcolor="#E1BEE7"]
|
|
||||||
category [label="Category", fillcolor="#E1BEE7"]
|
|
||||||
service [label="Service", fillcolor="#CE93D8"]
|
|
||||||
prices [label="Prices", fillcolor="#BA68C8"]
|
|
||||||
discounts [label="Discounts", fillcolor="#BA68C8"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// === ROW 3: LEFT - Cart ===
|
|
||||||
subgraph cluster_cart {
|
|
||||||
label="Cart & Checkout"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#E0F7FA"
|
|
||||||
color="#00838F"
|
|
||||||
|
|
||||||
cart [label="Cart", fillcolor="#B2EBF2"]
|
|
||||||
cartitem [label="CartItem", fillcolor="#80DEEA"]
|
|
||||||
cartresumeitem [label="CartResumeItem", fillcolor="#80DEEA"]
|
|
||||||
cartpetreason [label="CartPetReason", fillcolor="#80DEEA"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// === ROW 3: CENTER - Requests ===
|
|
||||||
subgraph cluster_solicitudes {
|
|
||||||
label="Service Requests"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#FFEBEE"
|
|
||||||
color="#C62828"
|
|
||||||
|
|
||||||
servicerequest [label="ServiceRequest", fillcolor="#FFCDD2"]
|
|
||||||
statehistory [label="StateHistory", fillcolor="#EF9A9A"]
|
|
||||||
vetasked [label="VeterinarianAsked", fillcolor="#EF9A9A"]
|
|
||||||
vetreminder [label="VetReminder", fillcolor="#E57373"]
|
|
||||||
payreminder [label="PayReminder", fillcolor="#E57373"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// === ROW 3: RIGHT - Visits ===
|
|
||||||
subgraph cluster_visits {
|
|
||||||
label="Veterinary Visits"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#FFFDE7"
|
|
||||||
color="#F9A825"
|
|
||||||
|
|
||||||
vetvisit [label="VetVisit", fillcolor="#FFF9C4"]
|
|
||||||
vetvisitreport [label="VetVisitReport", fillcolor="#FFF59D"]
|
|
||||||
vetvisitfollowup [label="FollowUp", fillcolor="#FFF176"]
|
|
||||||
vetvisitpetreason [label="VisitPetReason", fillcolor="#FFF176"]
|
|
||||||
visit_pets [label="Visit-Pets", shape=diamond, fillcolor="#FFEE58"]
|
|
||||||
receipt [label="Receipt\n(AFIP)", fillcolor="#F8BBD9"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// === ROW 4: REFERENCE DATA (bottom, full width) ===
|
|
||||||
subgraph cluster_reference {
|
|
||||||
label="Reference Data"
|
|
||||||
style="rounded,filled"
|
|
||||||
fillcolor="#ECEFF1"
|
|
||||||
color="#455A64"
|
|
||||||
|
|
||||||
subgraph {
|
|
||||||
rank=same
|
|
||||||
specialty [label="Specialty", fillcolor="#CFD8DC"]
|
|
||||||
neighborhood [label="Neighborhood", fillcolor="#CFD8DC"]
|
|
||||||
province [label="Province", fillcolor="#CFD8DC"]
|
|
||||||
locality [label="Locality", fillcolor="#CFD8DC"]
|
|
||||||
petbreed [label="PetBreed", fillcolor="#CFD8DC"]
|
|
||||||
vaccine [label="Vaccine", fillcolor="#CFD8DC"]
|
|
||||||
}
|
|
||||||
subgraph {
|
|
||||||
rank=same
|
|
||||||
study [label="Study", fillcolor="#CFD8DC"]
|
|
||||||
campaign [label="Campaign", fillcolor="#CFD8DC"]
|
|
||||||
tag [label="Tag", fillcolor="#CFD8DC"]
|
|
||||||
medication [label="Medication", fillcolor="#CFD8DC"]
|
|
||||||
turnfeegroup [label="TurnFeeGroup", fillcolor="#CFD8DC"]
|
|
||||||
}
|
|
||||||
specialty -> study [style=invis]
|
|
||||||
province -> locality
|
|
||||||
}
|
|
||||||
|
|
||||||
// === FORCE COLUMN ALIGNMENT WITH INVISIBLE EDGES ===
|
|
||||||
// Column 1: Pets -> Cart
|
|
||||||
petowner -> cart [style=invis, weight=10]
|
|
||||||
// Column 2: Vets -> Requests
|
|
||||||
veterinarian -> servicerequest [style=invis, weight=10]
|
|
||||||
// Column 3: Services -> Visits
|
|
||||||
service -> vetvisit [style=invis, weight=10]
|
|
||||||
|
|
||||||
// Force Reference Data to bottom
|
|
||||||
cart -> specialty [style=invis, weight=10]
|
|
||||||
servicerequest -> campaign [style=invis, weight=10]
|
|
||||||
vetvisit -> turnfeegroup [style=invis, weight=10]
|
|
||||||
|
|
||||||
// === INTERNAL CLUSTER EDGES ===
|
|
||||||
petowner -> pet [label="1:N"]
|
|
||||||
pet -> petvaccine [label="1:N"]
|
|
||||||
pet -> petstudy [label="1:N"]
|
|
||||||
|
|
||||||
veterinarian -> availability
|
|
||||||
veterinarian -> unavailability
|
|
||||||
veterinarian -> vet_specialty
|
|
||||||
veterinarian -> vet_neighborhood
|
|
||||||
|
|
||||||
grupo -> category [label="1:N"]
|
|
||||||
category -> service [label="1:N"]
|
|
||||||
service -> prices [label="1:N"]
|
|
||||||
service -> discounts [label="1:N"]
|
|
||||||
|
|
||||||
cart -> cartitem [label="1:N"]
|
|
||||||
cart -> cartresumeitem
|
|
||||||
cart -> cartpetreason
|
|
||||||
|
|
||||||
servicerequest -> statehistory [label="1:N"]
|
|
||||||
servicerequest -> vetasked [label="1:N"]
|
|
||||||
servicerequest -> payreminder
|
|
||||||
vetasked -> vetreminder
|
|
||||||
|
|
||||||
vetvisit -> visit_pets
|
|
||||||
vetvisit -> vetvisitreport [label="1:N"]
|
|
||||||
vetvisit -> vetvisitpetreason
|
|
||||||
vetvisitreport -> vetvisitfollowup
|
|
||||||
vetvisit -> receipt [style=dashed]
|
|
||||||
|
|
||||||
// === CROSS-CLUSTER RELATIONSHIPS ===
|
|
||||||
auth_user -> petowner [label="1:1 opt", style=dashed]
|
|
||||||
auth_user -> veterinarian [label="1:1"]
|
|
||||||
|
|
||||||
petowner -> cart [label="1:N"]
|
|
||||||
petowner -> servicerequest [label="1:N"]
|
|
||||||
servicerequest -> cart [label="1:1", constraint=false]
|
|
||||||
vetasked -> veterinarian [constraint=false]
|
|
||||||
vetvisit -> servicerequest [style=dashed, constraint=false]
|
|
||||||
|
|
||||||
// Reference links
|
|
||||||
petvaccine -> vaccine [constraint=false]
|
|
||||||
petstudy -> study [constraint=false]
|
|
||||||
vet_specialty -> specialty [constraint=false]
|
|
||||||
vet_neighborhood -> neighborhood [constraint=false]
|
|
||||||
cartitem -> service [constraint=false]
|
|
||||||
}
|
|
||||||
@@ -1,540 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
|
||||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<!-- Generated by graphviz version 14.0.5 (0)
|
|
||||||
-->
|
|
||||||
<!-- Title: DataModelSimple Pages: 1 -->
|
|
||||||
<svg width="2433pt" height="780pt"
|
|
||||||
viewBox="0.00 0.00 2433.00 780.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
||||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 776.4)">
|
|
||||||
<title>DataModelSimple</title>
|
|
||||||
<polygon fill="white" stroke="none" points="-4,4 -4,-776.4 2429,-776.4 2429,4 -4,4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1212.5" y="-753.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">AMAR Mascotas - Data Model Overview</text>
|
|
||||||
<g id="clust1" class="cluster">
|
|
||||||
<title>cluster_auth</title>
|
|
||||||
<path fill="#e8f5e9" stroke="#2e7d32" d="M890,-658C890,-658 975,-658 975,-658 981,-658 987,-664 987,-670 987,-670 987,-725.2 987,-725.2 987,-731.2 981,-737.2 975,-737.2 975,-737.2 890,-737.2 890,-737.2 884,-737.2 878,-731.2 878,-725.2 878,-725.2 878,-670 878,-670 878,-664 884,-658 890,-658"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="932.5" y="-718" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Users & Auth</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust2" class="cluster">
|
|
||||||
<title>cluster_mascotas</title>
|
|
||||||
<path fill="#e3f2fd" stroke="#1565c0" d="M1662,-262.6C1662,-262.6 1799,-262.6 1799,-262.6 1805,-262.6 1811,-268.6 1811,-274.6 1811,-274.6 1811,-593.4 1811,-593.4 1811,-599.4 1805,-605.4 1799,-605.4 1799,-605.4 1662,-605.4 1662,-605.4 1656,-605.4 1650,-599.4 1650,-593.4 1650,-593.4 1650,-274.6 1650,-274.6 1650,-268.6 1656,-262.6 1662,-262.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1730.5" y="-586.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Pet Owners & Pets</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust3" class="cluster">
|
|
||||||
<title>cluster_vets</title>
|
|
||||||
<path fill="#fff3e0" stroke="#e65100" d="M20,-262.6C20,-262.6 509,-262.6 509,-262.6 515,-262.6 521,-268.6 521,-274.6 521,-274.6 521,-461.6 521,-461.6 521,-467.6 515,-473.6 509,-473.6 509,-473.6 20,-473.6 20,-473.6 14,-473.6 8,-467.6 8,-461.6 8,-461.6 8,-274.6 8,-274.6 8,-268.6 14,-262.6 20,-262.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="264.5" y="-454.4" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Veterinarians</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust4" class="cluster">
|
|
||||||
<title>cluster_productos</title>
|
|
||||||
<path fill="#f3e5f5" stroke="#7b1fa2" d="M2277,-262.6C2277,-262.6 2405,-262.6 2405,-262.6 2411,-262.6 2417,-268.6 2417,-274.6 2417,-274.6 2417,-725.2 2417,-725.2 2417,-731.2 2411,-737.2 2405,-737.2 2405,-737.2 2277,-737.2 2277,-737.2 2271,-737.2 2265,-731.2 2265,-725.2 2265,-725.2 2265,-274.6 2265,-274.6 2265,-268.6 2271,-262.6 2277,-262.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2341" y="-718" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Services & Pricing</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust5" class="cluster">
|
|
||||||
<title>cluster_cart</title>
|
|
||||||
<path fill="#e0f7fa" stroke="#00838f" d="M853,-262.6C853,-262.6 1119,-262.6 1119,-262.6 1125,-262.6 1131,-268.6 1131,-274.6 1131,-274.6 1131,-461.6 1131,-461.6 1131,-467.6 1125,-473.6 1119,-473.6 1119,-473.6 853,-473.6 853,-473.6 847,-473.6 841,-467.6 841,-461.6 841,-461.6 841,-274.6 841,-274.6 841,-268.6 847,-262.6 853,-262.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="986" y="-454.4" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Cart & Checkout</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust6" class="cluster">
|
|
||||||
<title>cluster_solicitudes</title>
|
|
||||||
<path fill="#ffebee" stroke="#c62828" d="M541,-8C541,-8 821,-8 821,-8 827,-8 833,-14 833,-20 833,-20 833,-329.8 833,-329.8 833,-335.8 827,-341.8 821,-341.8 821,-341.8 541,-341.8 541,-341.8 535,-341.8 529,-335.8 529,-329.8 529,-329.8 529,-20 529,-20 529,-14 535,-8 541,-8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="681" y="-322.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Service Requests</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust7" class="cluster">
|
|
||||||
<title>cluster_visits</title>
|
|
||||||
<path fill="#fffde7" stroke="#f9a825" d="M1869,-8C1869,-8 2245,-8 2245,-8 2251,-8 2257,-14 2257,-20 2257,-20 2257,-329.8 2257,-329.8 2257,-335.8 2251,-341.8 2245,-341.8 2245,-341.8 1869,-341.8 1869,-341.8 1863,-341.8 1857,-335.8 1857,-329.8 1857,-329.8 1857,-20 1857,-20 1857,-14 1863,-8 1869,-8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2057" y="-322.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Veterinary Visits</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust8" class="cluster">
|
|
||||||
<title>cluster_reference</title>
|
|
||||||
<path fill="#eceff1" stroke="#455a64" d="M1151,-131C1151,-131 1630,-131 1630,-131 1636,-131 1642,-137 1642,-143 1642,-143 1642,-329.8 1642,-329.8 1642,-335.8 1636,-341.8 1630,-341.8 1630,-341.8 1151,-341.8 1151,-341.8 1145,-341.8 1139,-335.8 1139,-329.8 1139,-329.8 1139,-143 1139,-143 1139,-137 1145,-131 1151,-131"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1390.5" y="-322.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Reference Data</text>
|
|
||||||
</g>
|
|
||||||
<!-- auth_user -->
|
|
||||||
<g id="node1" class="node">
|
|
||||||
<title>auth_user</title>
|
|
||||||
<path fill="#c8e6c9" stroke="black" d="M949.68,-702C949.68,-702 914.32,-702 914.32,-702 908.32,-702 902.32,-696 902.32,-690 902.32,-690 902.32,-678 902.32,-678 902.32,-672 908.32,-666 914.32,-666 914.32,-666 949.68,-666 949.68,-666 955.68,-666 961.68,-672 961.68,-678 961.68,-678 961.68,-690 961.68,-690 961.68,-696 955.68,-702 949.68,-702"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="932" y="-681" font-family="Helvetica,sans-Serif" font-size="10.00">auth.User</text>
|
|
||||||
</g>
|
|
||||||
<!-- petowner -->
|
|
||||||
<g id="node2" class="node">
|
|
||||||
<title>petowner</title>
|
|
||||||
<path fill="#bbdefb" stroke="black" d="M1706.23,-570.2C1706.23,-570.2 1669.77,-570.2 1669.77,-570.2 1663.77,-570.2 1657.77,-564.2 1657.77,-558.2 1657.77,-558.2 1657.77,-546.2 1657.77,-546.2 1657.77,-540.2 1663.77,-534.2 1669.77,-534.2 1669.77,-534.2 1706.23,-534.2 1706.23,-534.2 1712.23,-534.2 1718.23,-540.2 1718.23,-546.2 1718.23,-546.2 1718.23,-558.2 1718.23,-558.2 1718.23,-564.2 1712.23,-570.2 1706.23,-570.2"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1688" y="-549.2" font-family="Helvetica,sans-Serif" font-size="10.00">PetOwner</text>
|
|
||||||
</g>
|
|
||||||
<!-- auth_user->petowner -->
|
|
||||||
<g id="edge32" class="edge">
|
|
||||||
<title>auth_user->petowner</title>
|
|
||||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M943.44,-665.62C943.44,-631.77 943.44,-563 943.44,-563 943.44,-563 1645.91,-563 1645.91,-563"/>
|
|
||||||
<polygon fill="black" stroke="black" points="1645.91,-566.5 1655.91,-563 1645.91,-559.5 1645.91,-566.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1213.23" y="-615.8" font-family="Helvetica,sans-Serif" font-size="8.00">1:1 opt</text>
|
|
||||||
</g>
|
|
||||||
<!-- veterinarian -->
|
|
||||||
<g id="node6" class="node">
|
|
||||||
<title>veterinarian</title>
|
|
||||||
<path fill="#ffe0b2" stroke="black" d="M500.96,-438.4C500.96,-438.4 455.04,-438.4 455.04,-438.4 449.04,-438.4 443.04,-432.4 443.04,-426.4 443.04,-426.4 443.04,-414.4 443.04,-414.4 443.04,-408.4 449.04,-402.4 455.04,-402.4 455.04,-402.4 500.96,-402.4 500.96,-402.4 506.96,-402.4 512.96,-408.4 512.96,-414.4 512.96,-414.4 512.96,-426.4 512.96,-426.4 512.96,-432.4 506.96,-438.4 500.96,-438.4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="478" y="-417.4" font-family="Helvetica,sans-Serif" font-size="10.00">Veterinarian</text>
|
|
||||||
</g>
|
|
||||||
<!-- auth_user->veterinarian -->
|
|
||||||
<g id="edge33" class="edge">
|
|
||||||
<title>auth_user->veterinarian</title>
|
|
||||||
<path fill="none" stroke="black" d="M914.19,-665.54C914.19,-607.58 914.19,-433 914.19,-433 914.19,-433 524.84,-433 524.84,-433"/>
|
|
||||||
<polygon fill="black" stroke="black" points="524.84,-429.5 514.84,-433 524.84,-436.5 524.84,-429.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="667.56" y="-549.8" font-family="Helvetica,sans-Serif" font-size="8.00">1:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- pet -->
|
|
||||||
<g id="node3" class="node">
|
|
||||||
<title>pet</title>
|
|
||||||
<path fill="#bbdefb" stroke="black" d="M1706,-438.4C1706,-438.4 1676,-438.4 1676,-438.4 1670,-438.4 1664,-432.4 1664,-426.4 1664,-426.4 1664,-414.4 1664,-414.4 1664,-408.4 1670,-402.4 1676,-402.4 1676,-402.4 1706,-402.4 1706,-402.4 1712,-402.4 1718,-408.4 1718,-414.4 1718,-414.4 1718,-426.4 1718,-426.4 1718,-432.4 1712,-438.4 1706,-438.4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1691" y="-417.4" font-family="Helvetica,sans-Serif" font-size="10.00">Pet</text>
|
|
||||||
</g>
|
|
||||||
<!-- petowner->pet -->
|
|
||||||
<g id="edge9" class="edge">
|
|
||||||
<title>petowner->pet</title>
|
|
||||||
<path fill="none" stroke="black" d="M1691,-533.87C1691,-533.87 1691,-450.08 1691,-450.08"/>
|
|
||||||
<polygon fill="black" stroke="black" points="1694.5,-450.08 1691,-440.08 1687.5,-450.08 1694.5,-450.08"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1696.22" y="-484" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- cart -->
|
|
||||||
<g id="node16" class="node">
|
|
||||||
<title>cart</title>
|
|
||||||
<path fill="#b2ebf2" stroke="black" d="M1111,-438.4C1111,-438.4 1081,-438.4 1081,-438.4 1075,-438.4 1069,-432.4 1069,-426.4 1069,-426.4 1069,-414.4 1069,-414.4 1069,-408.4 1075,-402.4 1081,-402.4 1081,-402.4 1111,-402.4 1111,-402.4 1117,-402.4 1123,-408.4 1123,-414.4 1123,-414.4 1123,-426.4 1123,-426.4 1123,-432.4 1117,-438.4 1111,-438.4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1096" y="-417.4" font-family="Helvetica,sans-Serif" font-size="10.00">Cart</text>
|
|
||||||
</g>
|
|
||||||
<!-- petowner->cart -->
|
|
||||||
<!-- petowner->cart -->
|
|
||||||
<g id="edge34" class="edge">
|
|
||||||
<title>petowner->cart</title>
|
|
||||||
<path fill="none" stroke="black" d="M1657.48,-541C1537.52,-541 1105,-541 1105,-541 1105,-541 1105,-450.23 1105,-450.23"/>
|
|
||||||
<polygon fill="black" stroke="black" points="1108.5,-450.23 1105,-440.23 1101.5,-450.23 1108.5,-450.23"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1233.22" y="-484" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- servicerequest -->
|
|
||||||
<g id="node20" class="node">
|
|
||||||
<title>servicerequest</title>
|
|
||||||
<path fill="#ffcdd2" stroke="black" d="M763.3,-306.6C763.3,-306.6 700.7,-306.6 700.7,-306.6 694.7,-306.6 688.7,-300.6 688.7,-294.6 688.7,-294.6 688.7,-282.6 688.7,-282.6 688.7,-276.6 694.7,-270.6 700.7,-270.6 700.7,-270.6 763.3,-270.6 763.3,-270.6 769.3,-270.6 775.3,-276.6 775.3,-282.6 775.3,-282.6 775.3,-294.6 775.3,-294.6 775.3,-300.6 769.3,-306.6 763.3,-306.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="732" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">ServiceRequest</text>
|
|
||||||
</g>
|
|
||||||
<!-- petowner->servicerequest -->
|
|
||||||
<g id="edge35" class="edge">
|
|
||||||
<title>petowner->servicerequest</title>
|
|
||||||
<path fill="none" stroke="black" d="M1657.27,-556C1493.64,-556 732,-556 732,-556 732,-556 732,-318.49 732,-318.49"/>
|
|
||||||
<polygon fill="black" stroke="black" points="735.5,-318.49 732,-308.49 728.5,-318.49 735.5,-318.49"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="782.22" y="-418" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- petvaccine -->
|
|
||||||
<g id="node4" class="node">
|
|
||||||
<title>petvaccine</title>
|
|
||||||
<path fill="#90caf9" stroke="black" d="M1712.29,-306.6C1712.29,-306.6 1669.71,-306.6 1669.71,-306.6 1663.71,-306.6 1657.71,-300.6 1657.71,-294.6 1657.71,-294.6 1657.71,-282.6 1657.71,-282.6 1657.71,-276.6 1663.71,-270.6 1669.71,-270.6 1669.71,-270.6 1712.29,-270.6 1712.29,-270.6 1718.29,-270.6 1724.29,-276.6 1724.29,-282.6 1724.29,-282.6 1724.29,-294.6 1724.29,-294.6 1724.29,-300.6 1718.29,-306.6 1712.29,-306.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1691" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">PetVaccine</text>
|
|
||||||
</g>
|
|
||||||
<!-- pet->petvaccine -->
|
|
||||||
<g id="edge10" class="edge">
|
|
||||||
<title>pet->petvaccine</title>
|
|
||||||
<path fill="none" stroke="black" d="M1691,-402.07C1691,-402.07 1691,-318.28 1691,-318.28"/>
|
|
||||||
<polygon fill="black" stroke="black" points="1694.5,-318.28 1691,-308.28 1687.5,-318.28 1694.5,-318.28"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1697.22" y="-352.2" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- petstudy -->
|
|
||||||
<g id="node5" class="node">
|
|
||||||
<title>petstudy</title>
|
|
||||||
<path fill="#90caf9" stroke="black" d="M1791.29,-306.6C1791.29,-306.6 1758.71,-306.6 1758.71,-306.6 1752.71,-306.6 1746.71,-300.6 1746.71,-294.6 1746.71,-294.6 1746.71,-282.6 1746.71,-282.6 1746.71,-276.6 1752.71,-270.6 1758.71,-270.6 1758.71,-270.6 1791.29,-270.6 1791.29,-270.6 1797.29,-270.6 1803.29,-276.6 1803.29,-282.6 1803.29,-282.6 1803.29,-294.6 1803.29,-294.6 1803.29,-300.6 1797.29,-306.6 1791.29,-306.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1775" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">PetStudy</text>
|
|
||||||
</g>
|
|
||||||
<!-- pet->petstudy -->
|
|
||||||
<g id="edge11" class="edge">
|
|
||||||
<title>pet->petstudy</title>
|
|
||||||
<path fill="none" stroke="black" d="M1718.04,-414C1742.6,-414 1775,-414 1775,-414 1775,-414 1775,-318.35 1775,-318.35"/>
|
|
||||||
<polygon fill="black" stroke="black" points="1778.5,-318.35 1775,-308.35 1771.5,-318.35 1778.5,-318.35"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1758.22" y="-352.2" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- vaccine -->
|
|
||||||
<g id="node36" class="node">
|
|
||||||
<title>vaccine</title>
|
|
||||||
<path fill="#cfd8dc" stroke="black" d="M1189,-306.6C1189,-306.6 1159,-306.6 1159,-306.6 1153,-306.6 1147,-300.6 1147,-294.6 1147,-294.6 1147,-282.6 1147,-282.6 1147,-276.6 1153,-270.6 1159,-270.6 1159,-270.6 1189,-270.6 1189,-270.6 1195,-270.6 1201,-276.6 1201,-282.6 1201,-282.6 1201,-294.6 1201,-294.6 1201,-300.6 1195,-306.6 1189,-306.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1174" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">Vaccine</text>
|
|
||||||
</g>
|
|
||||||
<!-- petvaccine->vaccine -->
|
|
||||||
<g id="edge39" class="edge">
|
|
||||||
<title>petvaccine->vaccine</title>
|
|
||||||
<path fill="none" stroke="black" d="M1691,-270.32C1691,-257.63 1691,-243 1691,-243 1691,-243 1174.1,-243 1174.1,-243 1174.1,-243 1174.1,-258.8 1174.1,-258.8"/>
|
|
||||||
<polygon fill="black" stroke="black" points="1170.6,-258.8 1174.1,-268.8 1177.6,-258.8 1170.6,-258.8"/>
|
|
||||||
</g>
|
|
||||||
<!-- study -->
|
|
||||||
<g id="node37" class="node">
|
|
||||||
<title>study</title>
|
|
||||||
<path fill="#cfd8dc" stroke="black" d="M1356,-175C1356,-175 1326,-175 1326,-175 1320,-175 1314,-169 1314,-163 1314,-163 1314,-151 1314,-151 1314,-145 1320,-139 1326,-139 1326,-139 1356,-139 1356,-139 1362,-139 1368,-145 1368,-151 1368,-151 1368,-163 1368,-163 1368,-169 1362,-175 1356,-175"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1341" y="-154" font-family="Helvetica,sans-Serif" font-size="10.00">Study</text>
|
|
||||||
</g>
|
|
||||||
<!-- petstudy->study -->
|
|
||||||
<g id="edge40" class="edge">
|
|
||||||
<title>petstudy->study</title>
|
|
||||||
<path fill="none" stroke="black" d="M1775,-270.23C1775,-244.81 1775,-202 1775,-202 1775,-202 1353.18,-202 1353.18,-202 1353.18,-202 1353.18,-186.82 1353.18,-186.82"/>
|
|
||||||
<polygon fill="black" stroke="black" points="1356.68,-186.82 1353.18,-176.82 1349.68,-186.82 1356.68,-186.82"/>
|
|
||||||
</g>
|
|
||||||
<!-- availability -->
|
|
||||||
<g id="node7" class="node">
|
|
||||||
<title>availability</title>
|
|
||||||
<path fill="#ffcc80" stroke="black" d="M67.62,-306.6C67.62,-306.6 28.38,-306.6 28.38,-306.6 22.38,-306.6 16.38,-300.6 16.38,-294.6 16.38,-294.6 16.38,-282.6 16.38,-282.6 16.38,-276.6 22.38,-270.6 28.38,-270.6 28.38,-270.6 67.62,-270.6 67.62,-270.6 73.62,-270.6 79.62,-276.6 79.62,-282.6 79.62,-282.6 79.62,-294.6 79.62,-294.6 79.62,-300.6 73.62,-306.6 67.62,-306.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="48" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">Availability</text>
|
|
||||||
</g>
|
|
||||||
<!-- veterinarian->availability -->
|
|
||||||
<g id="edge12" class="edge">
|
|
||||||
<title>veterinarian->availability</title>
|
|
||||||
<path fill="none" stroke="black" d="M442.81,-429C340.11,-429 48,-429 48,-429 48,-429 48,-318.47 48,-318.47"/>
|
|
||||||
<polygon fill="black" stroke="black" points="51.5,-318.47 48,-308.47 44.5,-318.47 51.5,-318.47"/>
|
|
||||||
</g>
|
|
||||||
<!-- unavailability -->
|
|
||||||
<g id="node8" class="node">
|
|
||||||
<title>unavailability</title>
|
|
||||||
<path fill="#ffcc80" stroke="black" d="M164.46,-306.6C164.46,-306.6 113.54,-306.6 113.54,-306.6 107.54,-306.6 101.54,-300.6 101.54,-294.6 101.54,-294.6 101.54,-282.6 101.54,-282.6 101.54,-276.6 107.54,-270.6 113.54,-270.6 113.54,-270.6 164.46,-270.6 164.46,-270.6 170.46,-270.6 176.46,-276.6 176.46,-282.6 176.46,-282.6 176.46,-294.6 176.46,-294.6 176.46,-300.6 170.46,-306.6 164.46,-306.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="139" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">Unavailability</text>
|
|
||||||
</g>
|
|
||||||
<!-- veterinarian->unavailability -->
|
|
||||||
<g id="edge13" class="edge">
|
|
||||||
<title>veterinarian->unavailability</title>
|
|
||||||
<path fill="none" stroke="black" d="M442.68,-420C355.85,-420 139,-420 139,-420 139,-420 139,-318.38 139,-318.38"/>
|
|
||||||
<polygon fill="black" stroke="black" points="142.5,-318.38 139,-308.38 135.5,-318.38 142.5,-318.38"/>
|
|
||||||
</g>
|
|
||||||
<!-- vet_specialty -->
|
|
||||||
<g id="node9" class="node">
|
|
||||||
<title>vet_specialty</title>
|
|
||||||
<path fill="#ffb74d" stroke="black" d="M250.45,-303.34C250.45,-303.34 209.84,-291.86 209.84,-291.86 204.06,-290.23 204.06,-286.97 209.84,-285.34 209.84,-285.34 250.45,-273.86 250.45,-273.86 256.23,-272.23 267.77,-272.23 273.55,-273.86 273.55,-273.86 314.16,-285.34 314.16,-285.34 319.94,-286.97 319.94,-290.23 314.16,-291.86 314.16,-291.86 273.55,-303.34 273.55,-303.34 267.77,-304.97 256.23,-304.97 250.45,-303.34"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="262" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">Vet-Specialty</text>
|
|
||||||
</g>
|
|
||||||
<!-- veterinarian->vet_specialty -->
|
|
||||||
<g id="edge14" class="edge">
|
|
||||||
<title>veterinarian->vet_specialty</title>
|
|
||||||
<path fill="none" stroke="black" d="M442.82,-411C381.61,-411 262,-411 262,-411 262,-411 262,-318.44 262,-318.44"/>
|
|
||||||
<polygon fill="black" stroke="black" points="265.5,-318.44 262,-308.44 258.5,-318.44 265.5,-318.44"/>
|
|
||||||
</g>
|
|
||||||
<!-- vet_neighborhood -->
|
|
||||||
<g id="node10" class="node">
|
|
||||||
<title>vet_neighborhood</title>
|
|
||||||
<path fill="#ffb74d" stroke="black" d="M418.27,-304.05C418.27,-304.05 359.09,-291.15 359.09,-291.15 353.23,-289.88 353.23,-287.32 359.09,-286.05 359.09,-286.05 418.27,-273.15 418.27,-273.15 424.14,-271.88 435.86,-271.88 441.73,-273.15 441.73,-273.15 500.91,-286.05 500.91,-286.05 506.77,-287.32 506.77,-289.88 500.91,-291.15 500.91,-291.15 441.73,-304.05 441.73,-304.05 435.86,-305.32 424.14,-305.32 418.27,-304.05"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="430" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">Vet-Neighborhood</text>
|
|
||||||
</g>
|
|
||||||
<!-- veterinarian->vet_neighborhood -->
|
|
||||||
<g id="edge15" class="edge">
|
|
||||||
<title>veterinarian->vet_neighborhood</title>
|
|
||||||
<path fill="none" stroke="black" d="M466.24,-402.07C466.24,-402.07 466.24,-310.63 466.24,-310.63"/>
|
|
||||||
<polygon fill="black" stroke="black" points="469.74,-310.63 466.24,-300.63 462.74,-310.63 469.74,-310.63"/>
|
|
||||||
</g>
|
|
||||||
<!-- veterinarian->servicerequest -->
|
|
||||||
<!-- specialty -->
|
|
||||||
<g id="node31" class="node">
|
|
||||||
<title>specialty</title>
|
|
||||||
<path fill="#cfd8dc" stroke="black" d="M1622.29,-306.6C1622.29,-306.6 1589.71,-306.6 1589.71,-306.6 1583.71,-306.6 1577.71,-300.6 1577.71,-294.6 1577.71,-294.6 1577.71,-282.6 1577.71,-282.6 1577.71,-276.6 1583.71,-270.6 1589.71,-270.6 1589.71,-270.6 1622.29,-270.6 1622.29,-270.6 1628.29,-270.6 1634.29,-276.6 1634.29,-282.6 1634.29,-282.6 1634.29,-294.6 1634.29,-294.6 1634.29,-300.6 1628.29,-306.6 1622.29,-306.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1606" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">Specialty</text>
|
|
||||||
</g>
|
|
||||||
<!-- vet_specialty->specialty -->
|
|
||||||
<g id="edge41" class="edge">
|
|
||||||
<title>vet_specialty->specialty</title>
|
|
||||||
<path fill="none" stroke="black" d="M262,-270.22C262,-263.27 262,-257 262,-257 262,-257 1591.73,-257 1591.73,-257 1591.73,-257 1591.73,-258.7 1591.73,-258.7"/>
|
|
||||||
<polygon fill="black" stroke="black" points="1588.23,-258.7 1591.73,-268.7 1595.23,-258.7 1588.23,-258.7"/>
|
|
||||||
</g>
|
|
||||||
<!-- neighborhood -->
|
|
||||||
<g id="node32" class="node">
|
|
||||||
<title>neighborhood</title>
|
|
||||||
<path fill="#cfd8dc" stroke="black" d="M1289.41,-306.6C1289.41,-306.6 1234.59,-306.6 1234.59,-306.6 1228.59,-306.6 1222.59,-300.6 1222.59,-294.6 1222.59,-294.6 1222.59,-282.6 1222.59,-282.6 1222.59,-276.6 1228.59,-270.6 1234.59,-270.6 1234.59,-270.6 1289.41,-270.6 1289.41,-270.6 1295.41,-270.6 1301.41,-276.6 1301.41,-282.6 1301.41,-282.6 1301.41,-294.6 1301.41,-294.6 1301.41,-300.6 1295.41,-306.6 1289.41,-306.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1262" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">Neighborhood</text>
|
|
||||||
</g>
|
|
||||||
<!-- vet_neighborhood->neighborhood -->
|
|
||||||
<g id="edge42" class="edge">
|
|
||||||
<title>vet_neighborhood->neighborhood</title>
|
|
||||||
<path fill="none" stroke="black" d="M489.44,-293.99C489.44,-307 489.44,-339 489.44,-339 489.44,-339 1262,-339 1262,-339 1262,-339 1262,-318.59 1262,-318.59"/>
|
|
||||||
<polygon fill="black" stroke="black" points="1265.5,-318.59 1262,-308.59 1258.5,-318.59 1265.5,-318.59"/>
|
|
||||||
</g>
|
|
||||||
<!-- grupo -->
|
|
||||||
<g id="node11" class="node">
|
|
||||||
<title>grupo</title>
|
|
||||||
<path fill="#e1bee7" stroke="black" d="M2316,-702C2316,-702 2286,-702 2286,-702 2280,-702 2274,-696 2274,-690 2274,-690 2274,-678 2274,-678 2274,-672 2280,-666 2286,-666 2286,-666 2316,-666 2316,-666 2322,-666 2328,-672 2328,-678 2328,-678 2328,-690 2328,-690 2328,-696 2322,-702 2316,-702"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2301" y="-681" font-family="Helvetica,sans-Serif" font-size="10.00">Group</text>
|
|
||||||
</g>
|
|
||||||
<!-- category -->
|
|
||||||
<g id="node12" class="node">
|
|
||||||
<title>category</title>
|
|
||||||
<path fill="#e1bee7" stroke="black" d="M2317.29,-570.2C2317.29,-570.2 2284.71,-570.2 2284.71,-570.2 2278.71,-570.2 2272.71,-564.2 2272.71,-558.2 2272.71,-558.2 2272.71,-546.2 2272.71,-546.2 2272.71,-540.2 2278.71,-534.2 2284.71,-534.2 2284.71,-534.2 2317.29,-534.2 2317.29,-534.2 2323.29,-534.2 2329.29,-540.2 2329.29,-546.2 2329.29,-546.2 2329.29,-558.2 2329.29,-558.2 2329.29,-564.2 2323.29,-570.2 2317.29,-570.2"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2301" y="-549.2" font-family="Helvetica,sans-Serif" font-size="10.00">Category</text>
|
|
||||||
</g>
|
|
||||||
<!-- grupo->category -->
|
|
||||||
<g id="edge16" class="edge">
|
|
||||||
<title>grupo->category</title>
|
|
||||||
<path fill="none" stroke="black" d="M2301,-665.67C2301,-665.67 2301,-581.88 2301,-581.88"/>
|
|
||||||
<polygon fill="black" stroke="black" points="2304.5,-581.88 2301,-571.88 2297.5,-581.88 2304.5,-581.88"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2307.22" y="-615.8" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- service -->
|
|
||||||
<g id="node13" class="node">
|
|
||||||
<title>service</title>
|
|
||||||
<path fill="#ce93d8" stroke="black" d="M2315,-438.4C2315,-438.4 2285,-438.4 2285,-438.4 2279,-438.4 2273,-432.4 2273,-426.4 2273,-426.4 2273,-414.4 2273,-414.4 2273,-408.4 2279,-402.4 2285,-402.4 2285,-402.4 2315,-402.4 2315,-402.4 2321,-402.4 2327,-408.4 2327,-414.4 2327,-414.4 2327,-426.4 2327,-426.4 2327,-432.4 2321,-438.4 2315,-438.4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2300" y="-417.4" font-family="Helvetica,sans-Serif" font-size="10.00">Service</text>
|
|
||||||
</g>
|
|
||||||
<!-- category->service -->
|
|
||||||
<g id="edge17" class="edge">
|
|
||||||
<title>category->service</title>
|
|
||||||
<path fill="none" stroke="black" d="M2300,-533.87C2300,-533.87 2300,-450.08 2300,-450.08"/>
|
|
||||||
<polygon fill="black" stroke="black" points="2303.5,-450.08 2300,-440.08 2296.5,-450.08 2303.5,-450.08"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2306.22" y="-484" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- prices -->
|
|
||||||
<g id="node14" class="node">
|
|
||||||
<title>prices</title>
|
|
||||||
<path fill="#ba68c8" stroke="black" d="M2315,-306.6C2315,-306.6 2285,-306.6 2285,-306.6 2279,-306.6 2273,-300.6 2273,-294.6 2273,-294.6 2273,-282.6 2273,-282.6 2273,-276.6 2279,-270.6 2285,-270.6 2285,-270.6 2315,-270.6 2315,-270.6 2321,-270.6 2327,-276.6 2327,-282.6 2327,-282.6 2327,-294.6 2327,-294.6 2327,-300.6 2321,-306.6 2315,-306.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2300" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">Prices</text>
|
|
||||||
</g>
|
|
||||||
<!-- service->prices -->
|
|
||||||
<g id="edge18" class="edge">
|
|
||||||
<title>service->prices</title>
|
|
||||||
<path fill="none" stroke="black" d="M2309,-402.07C2309,-402.07 2309,-318.28 2309,-318.28"/>
|
|
||||||
<polygon fill="black" stroke="black" points="2312.5,-318.28 2309,-308.28 2305.5,-318.28 2312.5,-318.28"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2306.22" y="-352.2" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- discounts -->
|
|
||||||
<g id="node15" class="node">
|
|
||||||
<title>discounts</title>
|
|
||||||
<path fill="#ba68c8" stroke="black" d="M2396.95,-306.6C2396.95,-306.6 2361.05,-306.6 2361.05,-306.6 2355.05,-306.6 2349.05,-300.6 2349.05,-294.6 2349.05,-294.6 2349.05,-282.6 2349.05,-282.6 2349.05,-276.6 2355.05,-270.6 2361.05,-270.6 2361.05,-270.6 2396.95,-270.6 2396.95,-270.6 2402.95,-270.6 2408.95,-276.6 2408.95,-282.6 2408.95,-282.6 2408.95,-294.6 2408.95,-294.6 2408.95,-300.6 2402.95,-306.6 2396.95,-306.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2379" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">Discounts</text>
|
|
||||||
</g>
|
|
||||||
<!-- service->discounts -->
|
|
||||||
<g id="edge19" class="edge">
|
|
||||||
<title>service->discounts</title>
|
|
||||||
<path fill="none" stroke="black" d="M2327.19,-420C2350.04,-420 2379,-420 2379,-420 2379,-420 2379,-318.38 2379,-318.38"/>
|
|
||||||
<polygon fill="black" stroke="black" points="2382.5,-318.38 2379,-308.38 2375.5,-318.38 2382.5,-318.38"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2365.22" y="-352.2" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisit -->
|
|
||||||
<g id="node25" class="node">
|
|
||||||
<title>vetvisit</title>
|
|
||||||
<path fill="#fff9c4" stroke="black" d="M2081,-306.6C2081,-306.6 2051,-306.6 2051,-306.6 2045,-306.6 2039,-300.6 2039,-294.6 2039,-294.6 2039,-282.6 2039,-282.6 2039,-276.6 2045,-270.6 2051,-270.6 2051,-270.6 2081,-270.6 2081,-270.6 2087,-270.6 2093,-276.6 2093,-282.6 2093,-282.6 2093,-294.6 2093,-294.6 2093,-300.6 2087,-306.6 2081,-306.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2066" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">VetVisit</text>
|
|
||||||
</g>
|
|
||||||
<!-- service->vetvisit -->
|
|
||||||
<!-- cartitem -->
|
|
||||||
<g id="node17" class="node">
|
|
||||||
<title>cartitem</title>
|
|
||||||
<path fill="#80deea" stroke="black" d="M891.17,-306.6C891.17,-306.6 860.83,-306.6 860.83,-306.6 854.83,-306.6 848.83,-300.6 848.83,-294.6 848.83,-294.6 848.83,-282.6 848.83,-282.6 848.83,-276.6 854.83,-270.6 860.83,-270.6 860.83,-270.6 891.17,-270.6 891.17,-270.6 897.17,-270.6 903.17,-276.6 903.17,-282.6 903.17,-282.6 903.17,-294.6 903.17,-294.6 903.17,-300.6 897.17,-306.6 891.17,-306.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="876" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">CartItem</text>
|
|
||||||
</g>
|
|
||||||
<!-- cart->cartitem -->
|
|
||||||
<g id="edge20" class="edge">
|
|
||||||
<title>cart->cartitem</title>
|
|
||||||
<path fill="none" stroke="black" d="M1068.78,-413C1011.66,-413 884.49,-413 884.49,-413 884.49,-413 884.49,-318.47 884.49,-318.47"/>
|
|
||||||
<polygon fill="black" stroke="black" points="887.99,-318.47 884.49,-308.47 880.99,-318.47 887.99,-318.47"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="924.22" y="-352.2" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- cartresumeitem -->
|
|
||||||
<g id="node18" class="node">
|
|
||||||
<title>cartresumeitem</title>
|
|
||||||
<path fill="#80deea" stroke="black" d="M1004.79,-306.6C1004.79,-306.6 937.21,-306.6 937.21,-306.6 931.21,-306.6 925.21,-300.6 925.21,-294.6 925.21,-294.6 925.21,-282.6 925.21,-282.6 925.21,-276.6 931.21,-270.6 937.21,-270.6 937.21,-270.6 1004.79,-270.6 1004.79,-270.6 1010.79,-270.6 1016.79,-276.6 1016.79,-282.6 1016.79,-282.6 1016.79,-294.6 1016.79,-294.6 1016.79,-300.6 1010.79,-306.6 1004.79,-306.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="971" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">CartResumeItem</text>
|
|
||||||
</g>
|
|
||||||
<!-- cart->cartresumeitem -->
|
|
||||||
<g id="edge21" class="edge">
|
|
||||||
<title>cart->cartresumeitem</title>
|
|
||||||
<path fill="none" stroke="black" d="M1068.5,-408C1036.87,-408 989.23,-408 989.23,-408 989.23,-408 989.23,-318.51 989.23,-318.51"/>
|
|
||||||
<polygon fill="black" stroke="black" points="992.73,-318.51 989.23,-308.51 985.73,-318.51 992.73,-318.51"/>
|
|
||||||
</g>
|
|
||||||
<!-- cartpetreason -->
|
|
||||||
<g id="node19" class="node">
|
|
||||||
<title>cartpetreason</title>
|
|
||||||
<path fill="#80deea" stroke="black" d="M1111.18,-306.6C1111.18,-306.6 1050.82,-306.6 1050.82,-306.6 1044.82,-306.6 1038.82,-300.6 1038.82,-294.6 1038.82,-294.6 1038.82,-282.6 1038.82,-282.6 1038.82,-276.6 1044.82,-270.6 1050.82,-270.6 1050.82,-270.6 1111.18,-270.6 1111.18,-270.6 1117.18,-270.6 1123.18,-276.6 1123.18,-282.6 1123.18,-282.6 1123.18,-294.6 1123.18,-294.6 1123.18,-300.6 1117.18,-306.6 1111.18,-306.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1081" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">CartPetReason</text>
|
|
||||||
</g>
|
|
||||||
<!-- cart->cartpetreason -->
|
|
||||||
<g id="edge22" class="edge">
|
|
||||||
<title>cart->cartpetreason</title>
|
|
||||||
<path fill="none" stroke="black" d="M1096,-402.07C1096,-402.07 1096,-318.28 1096,-318.28"/>
|
|
||||||
<polygon fill="black" stroke="black" points="1099.5,-318.28 1096,-308.28 1092.5,-318.28 1099.5,-318.28"/>
|
|
||||||
</g>
|
|
||||||
<!-- cart->specialty -->
|
|
||||||
<!-- cartitem->service -->
|
|
||||||
<g id="edge43" class="edge">
|
|
||||||
<title>cartitem->service</title>
|
|
||||||
<path fill="none" stroke="black" d="M866.66,-307.07C866.66,-331.09 866.66,-370 866.66,-370 866.66,-370 2291,-370 2291,-370 2291,-370 2291,-390.41 2291,-390.41"/>
|
|
||||||
<polygon fill="black" stroke="black" points="2287.5,-390.41 2291,-400.41 2294.5,-390.41 2287.5,-390.41"/>
|
|
||||||
</g>
|
|
||||||
<!-- servicerequest->cart -->
|
|
||||||
<g id="edge36" class="edge">
|
|
||||||
<title>servicerequest->cart</title>
|
|
||||||
<path fill="none" stroke="black" d="M753.65,-306.82C753.65,-344.61 753.65,-428 753.65,-428 753.65,-428 1057.05,-428 1057.05,-428"/>
|
|
||||||
<polygon fill="black" stroke="black" points="1057.05,-431.5 1067.05,-428 1057.05,-424.5 1057.05,-431.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1349.56" y="-352.2" font-family="Helvetica,sans-Serif" font-size="8.00">1:1</text>
|
|
||||||
</g>
|
|
||||||
<!-- statehistory -->
|
|
||||||
<g id="node21" class="node">
|
|
||||||
<title>statehistory</title>
|
|
||||||
<path fill="#ef9a9a" stroke="black" d="M595.23,-175C595.23,-175 548.77,-175 548.77,-175 542.77,-175 536.77,-169 536.77,-163 536.77,-163 536.77,-151 536.77,-151 536.77,-145 542.77,-139 548.77,-139 548.77,-139 595.23,-139 595.23,-139 601.23,-139 607.23,-145 607.23,-151 607.23,-151 607.23,-163 607.23,-163 607.23,-169 601.23,-175 595.23,-175"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="572" y="-154" font-family="Helvetica,sans-Serif" font-size="10.00">StateHistory</text>
|
|
||||||
</g>
|
|
||||||
<!-- servicerequest->statehistory -->
|
|
||||||
<g id="edge23" class="edge">
|
|
||||||
<title>servicerequest->statehistory</title>
|
|
||||||
<path fill="none" stroke="black" d="M688.28,-289C640.91,-289 572,-289 572,-289 572,-289 572,-186.87 572,-186.87"/>
|
|
||||||
<polygon fill="black" stroke="black" points="575.5,-186.87 572,-176.87 568.5,-186.87 575.5,-186.87"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="635.22" y="-220.4" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetasked -->
|
|
||||||
<g id="node22" class="node">
|
|
||||||
<title>vetasked</title>
|
|
||||||
<path fill="#ef9a9a" stroke="black" d="M714.86,-175C714.86,-175 641.14,-175 641.14,-175 635.14,-175 629.14,-169 629.14,-163 629.14,-163 629.14,-151 629.14,-151 629.14,-145 635.14,-139 641.14,-139 641.14,-139 714.86,-139 714.86,-139 720.86,-139 726.86,-145 726.86,-151 726.86,-151 726.86,-163 726.86,-163 726.86,-169 720.86,-175 714.86,-175"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="678" y="-154" font-family="Helvetica,sans-Serif" font-size="10.00">VeterinarianAsked</text>
|
|
||||||
</g>
|
|
||||||
<!-- servicerequest->vetasked -->
|
|
||||||
<g id="edge24" class="edge">
|
|
||||||
<title>servicerequest->vetasked</title>
|
|
||||||
<path fill="none" stroke="black" d="M707.78,-270.3C707.78,-270.3 707.78,-186.66 707.78,-186.66"/>
|
|
||||||
<polygon fill="black" stroke="black" points="711.28,-186.66 707.78,-176.66 704.28,-186.66 711.28,-186.66"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="707.22" y="-220.4" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- payreminder -->
|
|
||||||
<g id="node24" class="node">
|
|
||||||
<title>payreminder</title>
|
|
||||||
<path fill="#e57373" stroke="black" d="M813.29,-175C813.29,-175 760.71,-175 760.71,-175 754.71,-175 748.71,-169 748.71,-163 748.71,-163 748.71,-151 748.71,-151 748.71,-145 754.71,-139 760.71,-139 760.71,-139 813.29,-139 813.29,-139 819.29,-139 825.29,-145 825.29,-151 825.29,-151 825.29,-163 825.29,-163 825.29,-169 819.29,-175 813.29,-175"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="787" y="-154" font-family="Helvetica,sans-Serif" font-size="10.00">PayReminder</text>
|
|
||||||
</g>
|
|
||||||
<!-- servicerequest->payreminder -->
|
|
||||||
<g id="edge25" class="edge">
|
|
||||||
<title>servicerequest->payreminder</title>
|
|
||||||
<path fill="none" stroke="black" d="M757.57,-270.3C757.57,-270.3 757.57,-186.66 757.57,-186.66"/>
|
|
||||||
<polygon fill="black" stroke="black" points="761.07,-186.66 757.57,-176.66 754.07,-186.66 761.07,-186.66"/>
|
|
||||||
</g>
|
|
||||||
<!-- campaign -->
|
|
||||||
<g id="node38" class="node">
|
|
||||||
<title>campaign</title>
|
|
||||||
<path fill="#cfd8dc" stroke="black" d="M1196.79,-175C1196.79,-175 1159.21,-175 1159.21,-175 1153.21,-175 1147.21,-169 1147.21,-163 1147.21,-163 1147.21,-151 1147.21,-151 1147.21,-145 1153.21,-139 1159.21,-139 1159.21,-139 1196.79,-139 1196.79,-139 1202.79,-139 1208.79,-145 1208.79,-151 1208.79,-151 1208.79,-163 1208.79,-163 1208.79,-169 1202.79,-175 1196.79,-175"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1178" y="-154" font-family="Helvetica,sans-Serif" font-size="10.00">Campaign</text>
|
|
||||||
</g>
|
|
||||||
<!-- servicerequest->campaign -->
|
|
||||||
<!-- vetasked->veterinarian -->
|
|
||||||
<g id="edge37" class="edge">
|
|
||||||
<title>vetasked->veterinarian</title>
|
|
||||||
<path fill="none" stroke="black" d="M658.92,-175.35C658.92,-234.76 658.92,-418 658.92,-418 658.92,-418 524.83,-418 524.83,-418"/>
|
|
||||||
<polygon fill="black" stroke="black" points="524.83,-414.5 514.83,-418 524.83,-421.5 524.83,-414.5"/>
|
|
||||||
</g>
|
|
||||||
<!-- vetreminder -->
|
|
||||||
<g id="node23" class="node">
|
|
||||||
<title>vetreminder</title>
|
|
||||||
<path fill="#e57373" stroke="black" d="M703.18,-52C703.18,-52 652.82,-52 652.82,-52 646.82,-52 640.82,-46 640.82,-40 640.82,-40 640.82,-28 640.82,-28 640.82,-22 646.82,-16 652.82,-16 652.82,-16 703.18,-16 703.18,-16 709.18,-16 715.18,-22 715.18,-28 715.18,-28 715.18,-40 715.18,-40 715.18,-46 709.18,-52 703.18,-52"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="678" y="-31" font-family="Helvetica,sans-Serif" font-size="10.00">VetReminder</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetasked->vetreminder -->
|
|
||||||
<g id="edge26" class="edge">
|
|
||||||
<title>vetasked->vetreminder</title>
|
|
||||||
<path fill="none" stroke="black" d="M678,-138.59C678,-138.59 678,-63.65 678,-63.65"/>
|
|
||||||
<polygon fill="black" stroke="black" points="681.5,-63.65 678,-53.65 674.5,-63.65 681.5,-63.65"/>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisit->servicerequest -->
|
|
||||||
<g id="edge38" class="edge">
|
|
||||||
<title>vetvisit->servicerequest</title>
|
|
||||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M2042.05,-270.38C2042.05,-253.18 2042.05,-230 2042.05,-230 2042.05,-230 766.43,-230 766.43,-230 766.43,-230 766.43,-258.87 766.43,-258.87"/>
|
|
||||||
<polygon fill="black" stroke="black" points="762.93,-258.87 766.43,-268.87 769.93,-258.87 762.93,-258.87"/>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisitreport -->
|
|
||||||
<g id="node26" class="node">
|
|
||||||
<title>vetvisitreport</title>
|
|
||||||
<path fill="#fff59d" stroke="black" d="M1932.96,-175C1932.96,-175 1877.04,-175 1877.04,-175 1871.04,-175 1865.04,-169 1865.04,-163 1865.04,-163 1865.04,-151 1865.04,-151 1865.04,-145 1871.04,-139 1877.04,-139 1877.04,-139 1932.96,-139 1932.96,-139 1938.96,-139 1944.96,-145 1944.96,-151 1944.96,-151 1944.96,-163 1944.96,-163 1944.96,-169 1938.96,-175 1932.96,-175"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1905" y="-154" font-family="Helvetica,sans-Serif" font-size="10.00">VetVisitReport</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisit->vetvisitreport -->
|
|
||||||
<g id="edge28" class="edge">
|
|
||||||
<title>vetvisit->vetvisitreport</title>
|
|
||||||
<path fill="none" stroke="black" d="M2038.69,-289C1992.75,-289 1905,-289 1905,-289 1905,-289 1905,-186.87 1905,-186.87"/>
|
|
||||||
<polygon fill="black" stroke="black" points="1908.5,-186.87 1905,-176.87 1901.5,-186.87 1908.5,-186.87"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1981.22" y="-220.4" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisitpetreason -->
|
|
||||||
<g id="node28" class="node">
|
|
||||||
<title>vetvisitpetreason</title>
|
|
||||||
<path fill="#fff176" stroke="black" d="M2039.18,-175C2039.18,-175 1978.82,-175 1978.82,-175 1972.82,-175 1966.82,-169 1966.82,-163 1966.82,-163 1966.82,-151 1966.82,-151 1966.82,-145 1972.82,-139 1978.82,-139 1978.82,-139 2039.18,-139 2039.18,-139 2045.18,-139 2051.18,-145 2051.18,-151 2051.18,-151 2051.18,-163 2051.18,-163 2051.18,-169 2045.18,-175 2039.18,-175"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2009" y="-154" font-family="Helvetica,sans-Serif" font-size="10.00">VisitPetReason</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisit->vetvisitpetreason -->
|
|
||||||
<g id="edge29" class="edge">
|
|
||||||
<title>vetvisit->vetvisitpetreason</title>
|
|
||||||
<path fill="none" stroke="black" d="M2048.14,-270.3C2048.14,-270.3 2048.14,-186.66 2048.14,-186.66"/>
|
|
||||||
<polygon fill="black" stroke="black" points="2051.64,-186.66 2048.14,-176.66 2044.64,-186.66 2051.64,-186.66"/>
|
|
||||||
</g>
|
|
||||||
<!-- visit_pets -->
|
|
||||||
<g id="node29" class="node">
|
|
||||||
<title>visit_pets</title>
|
|
||||||
<path fill="#ffee58" stroke="black" d="M2111.72,-170.9C2111.72,-170.9 2084.76,-161.1 2084.76,-161.1 2079.12,-159.05 2079.12,-154.95 2084.76,-152.9 2084.76,-152.9 2111.72,-143.1 2111.72,-143.1 2117.36,-141.05 2128.64,-141.05 2134.28,-143.1 2134.28,-143.1 2161.24,-152.9 2161.24,-152.9 2166.88,-154.95 2166.88,-159.05 2161.24,-161.1 2161.24,-161.1 2134.28,-170.9 2134.28,-170.9 2128.64,-172.95 2117.36,-172.95 2111.72,-170.9"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2123" y="-154" font-family="Helvetica,sans-Serif" font-size="10.00">Visit-Pets</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisit->visit_pets -->
|
|
||||||
<g id="edge27" class="edge">
|
|
||||||
<title>vetvisit->visit_pets</title>
|
|
||||||
<path fill="none" stroke="black" d="M2083.24,-270.3C2083.24,-270.3 2083.24,-172.32 2083.24,-172.32"/>
|
|
||||||
<polygon fill="black" stroke="black" points="2086.74,-172.32 2083.24,-162.32 2079.74,-172.32 2086.74,-172.32"/>
|
|
||||||
</g>
|
|
||||||
<!-- receipt -->
|
|
||||||
<g id="node30" class="node">
|
|
||||||
<title>receipt</title>
|
|
||||||
<path fill="#f8bbd9" stroke="black" d="M2237,-175C2237,-175 2207,-175 2207,-175 2201,-175 2195,-169 2195,-163 2195,-163 2195,-151 2195,-151 2195,-145 2201,-139 2207,-139 2207,-139 2237,-139 2237,-139 2243,-139 2249,-145 2249,-151 2249,-151 2249,-163 2249,-163 2249,-169 2243,-175 2237,-175"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2222" y="-160" font-family="Helvetica,sans-Serif" font-size="10.00">Receipt</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="2222" y="-148" font-family="Helvetica,sans-Serif" font-size="10.00">(AFIP)</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisit->receipt -->
|
|
||||||
<g id="edge31" class="edge">
|
|
||||||
<title>vetvisit->receipt</title>
|
|
||||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M2093.17,-289C2137.87,-289 2222,-289 2222,-289 2222,-289 2222,-186.87 2222,-186.87"/>
|
|
||||||
<polygon fill="black" stroke="black" points="2225.5,-186.87 2222,-176.87 2218.5,-186.87 2225.5,-186.87"/>
|
|
||||||
</g>
|
|
||||||
<!-- turnfeegroup -->
|
|
||||||
<g id="node41" class="node">
|
|
||||||
<title>turnfeegroup</title>
|
|
||||||
<path fill="#cfd8dc" stroke="black" d="M1621.79,-175C1621.79,-175 1564.21,-175 1564.21,-175 1558.21,-175 1552.21,-169 1552.21,-163 1552.21,-163 1552.21,-151 1552.21,-151 1552.21,-145 1558.21,-139 1564.21,-139 1564.21,-139 1621.79,-139 1621.79,-139 1627.79,-139 1633.79,-145 1633.79,-151 1633.79,-151 1633.79,-163 1633.79,-163 1633.79,-169 1627.79,-175 1621.79,-175"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1593" y="-154" font-family="Helvetica,sans-Serif" font-size="10.00">TurnFeeGroup</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisit->turnfeegroup -->
|
|
||||||
<!-- vetvisitfollowup -->
|
|
||||||
<g id="node27" class="node">
|
|
||||||
<title>vetvisitfollowup</title>
|
|
||||||
<path fill="#fff176" stroke="black" d="M1921.84,-52C1921.84,-52 1888.16,-52 1888.16,-52 1882.16,-52 1876.16,-46 1876.16,-40 1876.16,-40 1876.16,-28 1876.16,-28 1876.16,-22 1882.16,-16 1888.16,-16 1888.16,-16 1921.84,-16 1921.84,-16 1927.84,-16 1933.84,-22 1933.84,-28 1933.84,-28 1933.84,-40 1933.84,-40 1933.84,-46 1927.84,-52 1921.84,-52"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1905" y="-31" font-family="Helvetica,sans-Serif" font-size="10.00">FollowUp</text>
|
|
||||||
</g>
|
|
||||||
<!-- vetvisitreport->vetvisitfollowup -->
|
|
||||||
<g id="edge30" class="edge">
|
|
||||||
<title>vetvisitreport->vetvisitfollowup</title>
|
|
||||||
<path fill="none" stroke="black" d="M1905,-138.59C1905,-138.59 1905,-63.65 1905,-63.65"/>
|
|
||||||
<polygon fill="black" stroke="black" points="1908.5,-63.65 1905,-53.65 1901.5,-63.65 1908.5,-63.65"/>
|
|
||||||
</g>
|
|
||||||
<!-- specialty->study -->
|
|
||||||
<!-- province -->
|
|
||||||
<g id="node33" class="node">
|
|
||||||
<title>province</title>
|
|
||||||
<path fill="#cfd8dc" stroke="black" d="M1366.45,-306.6C1366.45,-306.6 1335.55,-306.6 1335.55,-306.6 1329.55,-306.6 1323.55,-300.6 1323.55,-294.6 1323.55,-294.6 1323.55,-282.6 1323.55,-282.6 1323.55,-276.6 1329.55,-270.6 1335.55,-270.6 1335.55,-270.6 1366.45,-270.6 1366.45,-270.6 1372.45,-270.6 1378.45,-276.6 1378.45,-282.6 1378.45,-282.6 1378.45,-294.6 1378.45,-294.6 1378.45,-300.6 1372.45,-306.6 1366.45,-306.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1351" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">Province</text>
|
|
||||||
</g>
|
|
||||||
<!-- locality -->
|
|
||||||
<g id="node34" class="node">
|
|
||||||
<title>locality</title>
|
|
||||||
<path fill="#cfd8dc" stroke="black" d="M1464,-306.6C1464,-306.6 1434,-306.6 1434,-306.6 1428,-306.6 1422,-300.6 1422,-294.6 1422,-294.6 1422,-282.6 1422,-282.6 1422,-276.6 1428,-270.6 1434,-270.6 1434,-270.6 1464,-270.6 1464,-270.6 1470,-270.6 1476,-276.6 1476,-282.6 1476,-282.6 1476,-294.6 1476,-294.6 1476,-300.6 1470,-306.6 1464,-306.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1449" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">Locality</text>
|
|
||||||
</g>
|
|
||||||
<!-- province->locality -->
|
|
||||||
<g id="edge2" class="edge">
|
|
||||||
<title>province->locality</title>
|
|
||||||
<path fill="none" stroke="black" d="M1378.82,-289C1378.82,-289 1410.27,-289 1410.27,-289"/>
|
|
||||||
<polygon fill="black" stroke="black" points="1410.27,-292.5 1420.27,-289 1410.27,-285.5 1410.27,-292.5"/>
|
|
||||||
</g>
|
|
||||||
<!-- petbreed -->
|
|
||||||
<g id="node35" class="node">
|
|
||||||
<title>petbreed</title>
|
|
||||||
<path fill="#cfd8dc" stroke="black" d="M1543.85,-306.6C1543.85,-306.6 1510.15,-306.6 1510.15,-306.6 1504.15,-306.6 1498.15,-300.6 1498.15,-294.6 1498.15,-294.6 1498.15,-282.6 1498.15,-282.6 1498.15,-276.6 1504.15,-270.6 1510.15,-270.6 1510.15,-270.6 1543.85,-270.6 1543.85,-270.6 1549.85,-270.6 1555.85,-276.6 1555.85,-282.6 1555.85,-282.6 1555.85,-294.6 1555.85,-294.6 1555.85,-300.6 1549.85,-306.6 1543.85,-306.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1527" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">PetBreed</text>
|
|
||||||
</g>
|
|
||||||
<!-- tag -->
|
|
||||||
<g id="node39" class="node">
|
|
||||||
<title>tag</title>
|
|
||||||
<path fill="#cfd8dc" stroke="black" d="M1432,-175C1432,-175 1402,-175 1402,-175 1396,-175 1390,-169 1390,-163 1390,-163 1390,-151 1390,-151 1390,-145 1396,-139 1402,-139 1402,-139 1432,-139 1432,-139 1438,-139 1444,-145 1444,-151 1444,-151 1444,-163 1444,-163 1444,-169 1438,-175 1432,-175"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1417" y="-154" font-family="Helvetica,sans-Serif" font-size="10.00">Tag</text>
|
|
||||||
</g>
|
|
||||||
<!-- medication -->
|
|
||||||
<g id="node40" class="node">
|
|
||||||
<title>medication</title>
|
|
||||||
<path fill="#cfd8dc" stroke="black" d="M1518.18,-175C1518.18,-175 1477.82,-175 1477.82,-175 1471.82,-175 1465.82,-169 1465.82,-163 1465.82,-163 1465.82,-151 1465.82,-151 1465.82,-145 1471.82,-139 1477.82,-139 1477.82,-139 1518.18,-139 1518.18,-139 1524.18,-139 1530.18,-145 1530.18,-151 1530.18,-151 1530.18,-163 1530.18,-163 1530.18,-169 1524.18,-175 1518.18,-175"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1498" y="-154" font-family="Helvetica,sans-Serif" font-size="10.00">Medication</text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 43 KiB |
@@ -1,120 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Graph Viewer - AMAR Mascotas</title>
|
|
||||||
<link rel="stylesheet" href="styles.css">
|
|
||||||
</head>
|
|
||||||
<body class="graph-viewer">
|
|
||||||
<header class="graph-header">
|
|
||||||
<a href="index.html" class="back-link">← Index</a>
|
|
||||||
<div class="nav-controls">
|
|
||||||
<button onclick="navigate(-1)" id="btn-prev" title="Previous (←)">◀</button>
|
|
||||||
<span id="nav-position">1 / 4</span>
|
|
||||||
<button onclick="navigate(1)" id="btn-next" title="Next (→)">▶</button>
|
|
||||||
</div>
|
|
||||||
<h1 id="graph-title">Loading...</h1>
|
|
||||||
<div class="graph-controls">
|
|
||||||
<button onclick="setMode('fit')">Fit</button>
|
|
||||||
<button onclick="setMode('fit-width')">Width</button>
|
|
||||||
<button onclick="setMode('fit-height')">Height</button>
|
|
||||||
<button onclick="setMode('actual-size')">100%</button>
|
|
||||||
<button onclick="downloadSvg()">↓ SVG</button>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="graph-container" id="graph-container">
|
|
||||||
<img id="graph-img" src="" alt="Graph">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const graphOrder = [
|
|
||||||
'04-data-model-simple',
|
|
||||||
'01-backend-architecture',
|
|
||||||
'02-frontend-architecture',
|
|
||||||
'03-data-model'
|
|
||||||
];
|
|
||||||
|
|
||||||
const graphs = {
|
|
||||||
'04-data-model-simple': {
|
|
||||||
title: 'Data Model Overview',
|
|
||||||
file: '04-data-model-simple.svg'
|
|
||||||
},
|
|
||||||
'01-backend-architecture': {
|
|
||||||
title: 'Backend Architecture',
|
|
||||||
file: '01-backend-architecture.svg'
|
|
||||||
},
|
|
||||||
'02-frontend-architecture': {
|
|
||||||
title: 'Frontend Architecture',
|
|
||||||
file: '02-frontend-architecture.svg'
|
|
||||||
},
|
|
||||||
'03-data-model': {
|
|
||||||
title: 'Detailed Data Model',
|
|
||||||
file: '03-data-model.svg'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const params = new URLSearchParams(window.location.search);
|
|
||||||
let graphKey = params.get('g') || '04-data-model-simple';
|
|
||||||
let currentIndex = graphOrder.indexOf(graphKey);
|
|
||||||
if (currentIndex === -1) currentIndex = 0;
|
|
||||||
|
|
||||||
function loadGraph(key) {
|
|
||||||
const graph = graphs[key];
|
|
||||||
document.getElementById('graph-title').textContent = graph.title;
|
|
||||||
document.getElementById('graph-img').src = graph.file;
|
|
||||||
document.title = graph.title + ' - AMAR Mascotas';
|
|
||||||
history.replaceState(null, '', '?g=' + key);
|
|
||||||
graphKey = key;
|
|
||||||
updateNavHints();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateNavHints() {
|
|
||||||
const idx = graphOrder.indexOf(graphKey);
|
|
||||||
const prevBtn = document.getElementById('btn-prev');
|
|
||||||
const nextBtn = document.getElementById('btn-next');
|
|
||||||
prevBtn.disabled = idx === 0;
|
|
||||||
nextBtn.disabled = idx === graphOrder.length - 1;
|
|
||||||
document.getElementById('nav-position').textContent = (idx + 1) + ' / ' + graphOrder.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
function navigate(direction) {
|
|
||||||
const idx = graphOrder.indexOf(graphKey);
|
|
||||||
const newIdx = idx + direction;
|
|
||||||
if (newIdx >= 0 && newIdx < graphOrder.length) {
|
|
||||||
currentIndex = newIdx;
|
|
||||||
loadGraph(graphOrder[newIdx]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setMode(mode) {
|
|
||||||
const container = document.getElementById('graph-container');
|
|
||||||
container.className = 'graph-container ' + mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
function downloadSvg() {
|
|
||||||
const graph = graphs[graphKey];
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = graph.file;
|
|
||||||
link.download = graph.file;
|
|
||||||
link.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keyboard navigation
|
|
||||||
document.addEventListener('keydown', (e) => {
|
|
||||||
if (e.key === 'ArrowLeft') {
|
|
||||||
navigate(-1);
|
|
||||||
} else if (e.key === 'ArrowRight') {
|
|
||||||
navigate(1);
|
|
||||||
} else if (e.key === 'Escape') {
|
|
||||||
window.location.href = 'index.html';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initialize
|
|
||||||
loadGraph(graphOrder[currentIndex]);
|
|
||||||
setMode('fit');
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,245 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>AMAR Mascotas - Architecture & Data Models</title>
|
|
||||||
<link rel="stylesheet" href="styles.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<h1>AMAR Mascotas</h1>
|
|
||||||
<p class="subtitle">Architecture & Data Model Documentation</p>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<!-- Graph Sections -->
|
|
||||||
<section class="graph-section" id="data-model-simple">
|
|
||||||
<div class="graph-header-row">
|
|
||||||
<h2>Data Model Overview</h2>
|
|
||||||
<a href="graph.html?g=04-data-model-simple" class="view-btn">View Full</a>
|
|
||||||
</div>
|
|
||||||
<a href="graph.html?g=04-data-model-simple" class="graph-preview">
|
|
||||||
<img src="04-data-model-simple.svg" alt="Data Model Overview">
|
|
||||||
</a>
|
|
||||||
<div class="graph-details">
|
|
||||||
<p>High-level entity relationships without field details. Shows the main actors, workflow, and data flow.</p>
|
|
||||||
<h4>Clusters</h4>
|
|
||||||
<ul>
|
|
||||||
<li><strong>Users & Auth</strong>: Django auth.User as central identity</li>
|
|
||||||
<li><strong>Pet Owners & Pets</strong>: Clients, their pets, vaccines, studies</li>
|
|
||||||
<li><strong>Veterinarians</strong>: Vets with availability, specialties, coverage areas</li>
|
|
||||||
<li><strong>Services & Pricing</strong>: Service catalog with dynamic pricing</li>
|
|
||||||
<li><strong>Cart & Checkout</strong>: Shopping cart workflow</li>
|
|
||||||
<li><strong>Service Requests</strong>: Order lifecycle with state machine</li>
|
|
||||||
<li><strong>Veterinary Visits</strong>: Scheduled visits, reports, AFIP invoicing</li>
|
|
||||||
<li><strong>Reference Data</strong>: Lookups (specialties, neighborhoods, vaccines, etc.)</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="graph-section" id="backend">
|
|
||||||
<div class="graph-header-row">
|
|
||||||
<h2>Backend Architecture</h2>
|
|
||||||
<a href="graph.html?g=01-backend-architecture" class="view-btn">View Full</a>
|
|
||||||
</div>
|
|
||||||
<a href="graph.html?g=01-backend-architecture" class="graph-preview">
|
|
||||||
<img src="01-backend-architecture.svg" alt="Backend Architecture">
|
|
||||||
</a>
|
|
||||||
<div class="graph-details">
|
|
||||||
<p>Django apps structure: mascotas, productos, solicitudes, common, payments, and external integrations.</p>
|
|
||||||
|
|
||||||
<h4>Celery Tasks</h4>
|
|
||||||
<table class="details-table">
|
|
||||||
<thead>
|
|
||||||
<tr><th>Task</th><th>App</th><th>Purpose</th></tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td><code>send_veterinarian_followup</code></td>
|
|
||||||
<td>solicitudes</td>
|
|
||||||
<td>Re-sends availability request to vet if still pending</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>run_payment_reminder_cron</code></td>
|
|
||||||
<td>solicitudes</td>
|
|
||||||
<td>Cron job to send payment reminders</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>create_vetvisit_in_sheet</code></td>
|
|
||||||
<td>mascotas</td>
|
|
||||||
<td>Creates row in Google Sheets for visit</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>update_vetvisit_in_sheet</code></td>
|
|
||||||
<td>mascotas</td>
|
|
||||||
<td>Updates Google Sheets row</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>create_event_calendar_vetvisit</code></td>
|
|
||||||
<td>mascotas</td>
|
|
||||||
<td>Creates Google Calendar event</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>update_event_calendar_vetvisit</code></td>
|
|
||||||
<td>mascotas</td>
|
|
||||||
<td>Updates Google Calendar event</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>create_user_owner</code></td>
|
|
||||||
<td>mascotas</td>
|
|
||||||
<td>Creates Django user for PetOwner + welcome email</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>generate_vetvisit_invoice</code></td>
|
|
||||||
<td>mascotas</td>
|
|
||||||
<td>Generates AFIP invoice and PDF</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>fetch_mp_notification_details</code></td>
|
|
||||||
<td>payments</td>
|
|
||||||
<td>Fetches MercadoPago webhook details</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<p class="note">Celery handles async operations: external APIs (Google, MercadoPago, AFIP), scheduled reminders, and heavy processing (invoices, emails).</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="graph-section" id="frontend">
|
|
||||||
<div class="graph-header-row">
|
|
||||||
<h2>Frontend Architecture</h2>
|
|
||||||
<a href="graph.html?g=02-frontend-architecture" class="view-btn">View Full</a>
|
|
||||||
</div>
|
|
||||||
<a href="graph.html?g=02-frontend-architecture" class="graph-preview">
|
|
||||||
<img src="02-frontend-architecture.svg" alt="Frontend Architecture">
|
|
||||||
</a>
|
|
||||||
<div class="graph-details">
|
|
||||||
<p>Next.js 13+ App Router structure with public pages, backoffice, shared components, and services layer.</p>
|
|
||||||
<h4>Key Areas</h4>
|
|
||||||
<ul>
|
|
||||||
<li><strong>Public Pages</strong>: Landing, service catalog, booking flow</li>
|
|
||||||
<li><strong>Backoffice</strong>: Role-based dashboards (admin, vet, petowner)</li>
|
|
||||||
<li><strong>Services Layer</strong>: API clients for backend communication</li>
|
|
||||||
<li><strong>State Management</strong>: Redux store for cart, auth, UI state</li>
|
|
||||||
<li><strong>Shared Components</strong>: Forms, tables, modals, navigation</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="graph-section" id="data-model-detailed">
|
|
||||||
<div class="graph-header-row">
|
|
||||||
<h2>Detailed Data Model</h2>
|
|
||||||
<a href="graph.html?g=03-data-model" class="view-btn">View Full</a>
|
|
||||||
</div>
|
|
||||||
<a href="graph.html?g=03-data-model" class="graph-preview">
|
|
||||||
<img src="03-data-model.svg" alt="Detailed Data Model">
|
|
||||||
</a>
|
|
||||||
<div class="graph-details">
|
|
||||||
<p>Complete entity-relationship diagram with all fields, types, and relationships.</p>
|
|
||||||
<h4>Data Patterns</h4>
|
|
||||||
<ul>
|
|
||||||
<li><strong>Soft delete</strong>: <code>deleted</code> flag on most models</li>
|
|
||||||
<li><strong>Audit trail</strong>: <code>StateHistory</code> for service requests</li>
|
|
||||||
<li><strong>Geographic</strong>: PostGIS polygons for coverage areas</li>
|
|
||||||
<li><strong>Versioned pricing</strong>: Date ranges on <code>Prices</code></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Findings Section -->
|
|
||||||
<section class="findings-section">
|
|
||||||
<h2>Key Findings</h2>
|
|
||||||
|
|
||||||
<div class="findings-grid">
|
|
||||||
<article class="finding-card">
|
|
||||||
<h3>User Types</h3>
|
|
||||||
<p>All users connect to <code>auth.User</code>:</p>
|
|
||||||
<ul>
|
|
||||||
<li><strong>PetOwner</strong>: Optional 1:1 link (created lazily)</li>
|
|
||||||
<li><strong>Veterinarian</strong>: Required 1:1 link to User</li>
|
|
||||||
<li><strong>Staff</strong>: Direct Django users with <code>is_staff=True</code></li>
|
|
||||||
</ul>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<article class="finding-card">
|
|
||||||
<h3>Core Workflow</h3>
|
|
||||||
<div class="workflow-diagram">
|
|
||||||
<code>PetOwner → Cart → ServiceRequest → VeterinarianAsked → VetVisit → Report</code>
|
|
||||||
</div>
|
|
||||||
<p><strong>State Machine:</strong></p>
|
|
||||||
<div class="state-flow">
|
|
||||||
pending → vet_asked → vet_accepted → coordinated → payed → Confirmado
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<article class="finding-card">
|
|
||||||
<h3>Pricing Logic</h3>
|
|
||||||
<ul>
|
|
||||||
<li>Base price × <code>neighborhood.distance_coefficient</code></li>
|
|
||||||
<li>Optional vet-specific pricing via <code>Prices.veterinarian_id</code></li>
|
|
||||||
<li>Turn fee surcharge via <code>IndividualTurnFeeGroup</code></li>
|
|
||||||
<li>Time-based discounts via <code>Discounts</code> model</li>
|
|
||||||
</ul>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<article class="finding-card">
|
|
||||||
<h3>External Integrations</h3>
|
|
||||||
<ul>
|
|
||||||
<li><strong>MercadoPago</strong>: Payment processing</li>
|
|
||||||
<li><strong>Google Calendar</strong>: Visit synchronization</li>
|
|
||||||
<li><strong>Google Sheets</strong>: Visit tracking spreadsheet</li>
|
|
||||||
<li><strong>Mercately</strong>: WhatsApp notifications</li>
|
|
||||||
<li><strong>AFIP</strong>: Argentine tax invoicing</li>
|
|
||||||
</ul>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Tech Stack Section -->
|
|
||||||
<section class="tech-section">
|
|
||||||
<h2>Technology Stack</h2>
|
|
||||||
<div class="tech-grid">
|
|
||||||
<div class="tech-column">
|
|
||||||
<h3>Backend</h3>
|
|
||||||
<ul>
|
|
||||||
<li>Django 4.x</li>
|
|
||||||
<li>Django REST Framework</li>
|
|
||||||
<li>PostgreSQL + PostGIS</li>
|
|
||||||
<li>Celery (Redis)</li>
|
|
||||||
<li>JWT Authentication</li>
|
|
||||||
<li>django-afip</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="tech-column">
|
|
||||||
<h3>Frontend</h3>
|
|
||||||
<ul>
|
|
||||||
<li>Next.js 13+ (App Router)</li>
|
|
||||||
<li>React 18+</li>
|
|
||||||
<li>TypeScript</li>
|
|
||||||
<li>Redux</li>
|
|
||||||
<li>Axios</li>
|
|
||||||
<li>Tailwind CSS</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="tech-column">
|
|
||||||
<h3>Infrastructure</h3>
|
|
||||||
<ul>
|
|
||||||
<li>Docker</li>
|
|
||||||
<li>Nginx</li>
|
|
||||||
<li>AWS S3 (storage)</li>
|
|
||||||
<li>MercadoPago API</li>
|
|
||||||
<li>Google APIs</li>
|
|
||||||
<li>WhatsApp Business</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
<p>AMAR Mascotas Architecture Documentation</p>
|
|
||||||
<p class="date">Generated: <time datetime="2024-12-14">December 2024</time></p>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,565 +0,0 @@
|
|||||||
/* Reset and base */
|
|
||||||
*, *::before, *::after {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--color-bg: #0f0f0f;
|
|
||||||
--color-surface: #1a1a1a;
|
|
||||||
--color-surface-hover: #252525;
|
|
||||||
--color-border: #333;
|
|
||||||
--color-text: #e0e0e0;
|
|
||||||
--color-text-muted: #888;
|
|
||||||
--color-accent: #4A90D9;
|
|
||||||
--color-accent-light: #6BA3E0;
|
|
||||||
--radius: 8px;
|
|
||||||
--shadow: 0 2px 8px rgba(0,0,0,0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, sans-serif;
|
|
||||||
background: var(--color-bg);
|
|
||||||
color: var(--color-text);
|
|
||||||
line-height: 1.6;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Header */
|
|
||||||
header {
|
|
||||||
background: var(--color-surface);
|
|
||||||
border-bottom: 1px solid var(--color-border);
|
|
||||||
padding: 2rem;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
header h1 {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 2rem;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--color-text);
|
|
||||||
}
|
|
||||||
|
|
||||||
header .subtitle {
|
|
||||||
margin: 0.5rem 0 0;
|
|
||||||
color: var(--color-text-muted);
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Main content */
|
|
||||||
main {
|
|
||||||
max-width: 1400px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
section {
|
|
||||||
margin-bottom: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
section h2 {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
font-weight: 600;
|
|
||||||
margin: 0 0 1.5rem;
|
|
||||||
padding-bottom: 0.5rem;
|
|
||||||
border-bottom: 2px solid var(--color-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Card Grid - max 3 columns */
|
|
||||||
.card-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
||||||
gap: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1000px) {
|
|
||||||
.card-grid {
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 700px) and (max-width: 999px) {
|
|
||||||
.card-grid {
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Graph Cards */
|
|
||||||
.card {
|
|
||||||
display: block;
|
|
||||||
background: var(--color-surface);
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
border-radius: var(--radius);
|
|
||||||
overflow: hidden;
|
|
||||||
text-decoration: none;
|
|
||||||
color: inherit;
|
|
||||||
transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: var(--shadow);
|
|
||||||
border-color: var(--color-accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-preview {
|
|
||||||
aspect-ratio: 4/3;
|
|
||||||
background: #fff;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
overflow: hidden;
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-preview img {
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 100%;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-content {
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-content h3 {
|
|
||||||
margin: 0 0 0.5rem;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--color-accent-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-content p {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
color: var(--color-text-muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Findings Grid */
|
|
||||||
.findings-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
||||||
gap: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1000px) {
|
|
||||||
.findings-grid {
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.finding-card {
|
|
||||||
background: var(--color-surface);
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
border-radius: var(--radius);
|
|
||||||
padding: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.finding-card h3 {
|
|
||||||
margin: 0 0 0.75rem;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--color-accent-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.finding-card p,
|
|
||||||
.finding-card ul {
|
|
||||||
margin: 0 0 0.75rem;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.finding-card ul {
|
|
||||||
padding-left: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.finding-card li {
|
|
||||||
margin-bottom: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.finding-card code {
|
|
||||||
background: #2a2a2a;
|
|
||||||
padding: 0.1em 0.4em;
|
|
||||||
border-radius: 3px;
|
|
||||||
font-size: 0.85em;
|
|
||||||
color: #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.workflow-diagram {
|
|
||||||
background: #2a2a2a;
|
|
||||||
padding: 0.75rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 0.75rem;
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.workflow-diagram code {
|
|
||||||
background: none;
|
|
||||||
padding: 0;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.state-flow {
|
|
||||||
font-family: monospace;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
color: var(--color-text-muted);
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats-table {
|
|
||||||
width: 100%;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats-table td {
|
|
||||||
padding: 0.35rem 0;
|
|
||||||
border-bottom: 1px solid var(--color-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats-table td:first-child {
|
|
||||||
font-family: monospace;
|
|
||||||
color: var(--color-accent-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats-table td:last-child {
|
|
||||||
color: var(--color-text-muted);
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tech Stack Grid */
|
|
||||||
.tech-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
||||||
gap: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 700px) {
|
|
||||||
.tech-grid {
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tech-column {
|
|
||||||
background: var(--color-surface);
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
border-radius: var(--radius);
|
|
||||||
padding: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tech-column h3 {
|
|
||||||
margin: 0 0 0.75rem;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--color-accent-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tech-column ul {
|
|
||||||
margin: 0;
|
|
||||||
padding-left: 1.25rem;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tech-column li {
|
|
||||||
margin-bottom: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Footer */
|
|
||||||
footer {
|
|
||||||
background: var(--color-surface);
|
|
||||||
border-top: 1px solid var(--color-border);
|
|
||||||
padding: 1.5rem 2rem;
|
|
||||||
text-align: center;
|
|
||||||
color: var(--color-text-muted);
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer p {
|
|
||||||
margin: 0.25rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Graph Viewer Page */
|
|
||||||
.graph-viewer {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-header {
|
|
||||||
background: var(--color-surface);
|
|
||||||
border-bottom: 1px solid var(--color-border);
|
|
||||||
padding: 1rem 2rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 1rem;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-header .back-link {
|
|
||||||
color: var(--color-accent);
|
|
||||||
text-decoration: none;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-header .back-link:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-controls {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-controls button {
|
|
||||||
background: var(--color-surface-hover);
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
color: var(--color-text);
|
|
||||||
padding: 0.4rem 0.7rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-controls button:hover:not(:disabled) {
|
|
||||||
background: var(--color-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-controls button:disabled {
|
|
||||||
opacity: 0.4;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-controls span {
|
|
||||||
font-size: 0.85rem;
|
|
||||||
color: var(--color-text-muted);
|
|
||||||
min-width: 3rem;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-header h1 {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 1.25rem;
|
|
||||||
font-weight: 600;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-controls {
|
|
||||||
display: flex;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-controls button {
|
|
||||||
background: var(--color-surface-hover);
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
color: var(--color-text);
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-controls button:hover {
|
|
||||||
background: var(--color-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-container {
|
|
||||||
flex: 1;
|
|
||||||
overflow: auto;
|
|
||||||
background: #fff;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-container img {
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 100%;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-container.fit-width img {
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
max-height: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-container.fit-height img {
|
|
||||||
height: calc(100vh - 80px);
|
|
||||||
width: auto;
|
|
||||||
max-width: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-container.actual-size img {
|
|
||||||
max-width: none;
|
|
||||||
max-height: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Graph Sections (index page) */
|
|
||||||
.graph-section {
|
|
||||||
background: var(--color-surface);
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
border-radius: var(--radius);
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-header-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 1rem 1.5rem;
|
|
||||||
border-bottom: 1px solid var(--color-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-header-row h2 {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: none;
|
|
||||||
font-size: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-btn {
|
|
||||||
background: var(--color-accent);
|
|
||||||
color: #fff;
|
|
||||||
text-decoration: none;
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-btn:hover {
|
|
||||||
background: var(--color-accent-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-section .graph-preview {
|
|
||||||
display: block;
|
|
||||||
background: #fff;
|
|
||||||
max-height: 400px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-section .graph-preview img {
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
object-fit: contain;
|
|
||||||
object-position: top left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-details {
|
|
||||||
padding: 1.5rem;
|
|
||||||
border-top: 1px solid var(--color-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-details p {
|
|
||||||
margin: 0 0 1rem;
|
|
||||||
color: var(--color-text-muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-details h4 {
|
|
||||||
margin: 1.5rem 0 0.75rem;
|
|
||||||
font-size: 0.95rem;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--color-accent-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-details h4:first-child {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-details ul {
|
|
||||||
margin: 0;
|
|
||||||
padding-left: 1.25rem;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-details li {
|
|
||||||
margin-bottom: 0.35rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-details code {
|
|
||||||
background: #2a2a2a;
|
|
||||||
padding: 0.1em 0.4em;
|
|
||||||
border-radius: 3px;
|
|
||||||
font-size: 0.85em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Details Table */
|
|
||||||
.details-table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
margin: 0.5rem 0 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.details-table th {
|
|
||||||
text-align: left;
|
|
||||||
padding: 0.5rem;
|
|
||||||
background: #2a2a2a;
|
|
||||||
border-bottom: 1px solid var(--color-border);
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--color-text);
|
|
||||||
}
|
|
||||||
|
|
||||||
.details-table td {
|
|
||||||
padding: 0.5rem;
|
|
||||||
border-bottom: 1px solid var(--color-border);
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.details-table code {
|
|
||||||
background: #2a2a2a;
|
|
||||||
padding: 0.15em 0.4em;
|
|
||||||
border-radius: 3px;
|
|
||||||
font-size: 0.85em;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note {
|
|
||||||
font-size: 0.85rem;
|
|
||||||
color: var(--color-text-muted);
|
|
||||||
font-style: italic;
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile adjustments */
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
main {
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
padding: 1.5rem 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
header h1 {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-grid,
|
|
||||||
.findings-grid {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-header {
|
|
||||||
flex-wrap: wrap;
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-header h1 {
|
|
||||||
order: -1;
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-controls {
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,645 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
|
||||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<!-- Generated by graphviz version 2.42.4 (0)
|
|
||||||
-->
|
|
||||||
<!-- Title: MediaAnalyzer Pages: 1 -->
|
|
||||||
<svg width="1979pt" height="2160pt"
|
|
||||||
viewBox="0.00 0.00 1979.34 2160.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
||||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 2165.46)">
|
|
||||||
<title>MediaAnalyzer</title>
|
|
||||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-2165.46 1984,-2165.46 1984,4 -4,4"/>
|
|
||||||
<g id="clust1" class="cluster">
|
|
||||||
<title>cluster_legend</title>
|
|
||||||
<polygon fill="lightgray" stroke="lightgray" points="8.47,-1739.35 8.47,-2138.17 249.75,-2138.17 249.75,-1739.35 8.47,-1739.35"/>
|
|
||||||
<text text-anchor="middle" x="129.11" y="-2124.57" font-family="Arial" font-size="12.00">Legend</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust2" class="cluster">
|
|
||||||
<title>cluster_sources</title>
|
|
||||||
<polygon fill="#1976d2" stroke="#1976d2" points="258.21,-1925.66 258.21,-2138.17 522.78,-2138.17 522.78,-1925.66 258.21,-1925.66"/>
|
|
||||||
<text text-anchor="middle" x="390.5" y="-2124.57" font-family="Arial" font-size="12.00">Video Sources</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust3" class="cluster">
|
|
||||||
<title>cluster_adapters</title>
|
|
||||||
<polygon fill="#8e24aa" stroke="#8e24aa" points="531.25,-1739.35 531.25,-2138.17 837.08,-2138.17 837.08,-1739.35 531.25,-1739.35"/>
|
|
||||||
<text text-anchor="middle" x="684.16" y="-2124.57" font-family="Arial" font-size="12.00">Source Adapters Pattern</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust4" class="cluster">
|
|
||||||
<title>cluster_core</title>
|
|
||||||
<polygon fill="#1976d2" stroke="#1976d2" points="641.3,-1158.6 641.3,-1689.87 846.61,-1689.87 846.61,-1158.6 641.3,-1158.6"/>
|
|
||||||
<text text-anchor="middle" x="743.96" y="-1676.27" font-family="Arial" font-size="12.00">Core Platform</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust5" class="cluster">
|
|
||||||
<title>cluster_execution</title>
|
|
||||||
<polygon fill="#8e24aa" stroke="#8e24aa" points="1665.7,-570.57 1665.7,-1052.35 1971.53,-1052.35 1971.53,-570.57 1665.7,-570.57"/>
|
|
||||||
<text text-anchor="middle" x="1818.62" y="-1038.75" font-family="Arial" font-size="12.00">Execution Strategies Pattern</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust6" class="cluster">
|
|
||||||
<title>cluster_event_sources</title>
|
|
||||||
<polygon fill="#8e24aa" stroke="#8e24aa" points="1200.06,-839.84 1200.06,-1371.11 1657.23,-1371.11 1657.23,-839.84 1200.06,-839.84"/>
|
|
||||||
<text text-anchor="middle" x="1428.65" y="-1357.51" font-family="Arial" font-size="12.00">Event Source Pattern</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust7" class="cluster">
|
|
||||||
<title>cluster_events</title>
|
|
||||||
<polygon fill="#1976d2" stroke="#1976d2" points="1164.08,-23.29 1164.08,-783.07 1321.76,-783.07 1321.76,-23.29 1164.08,-23.29"/>
|
|
||||||
<text text-anchor="middle" x="1242.92" y="-769.47" font-family="Arial" font-size="12.00">Event-Driven Processing</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust8" class="cluster">
|
|
||||||
<title>cluster_workers</title>
|
|
||||||
<polygon fill="#1976d2" stroke="#1976d2" points="386.26,-832.56 386.26,-1059.62 846.61,-1059.62 846.61,-832.56 386.26,-832.56"/>
|
|
||||||
<text text-anchor="middle" x="616.44" y="-1046.02" font-family="Arial" font-size="12.00">Celery Workers</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust9" class="cluster">
|
|
||||||
<title>cluster_ai_adapters</title>
|
|
||||||
<polygon fill="#8e24aa" stroke="#8e24aa" points="1330.23,-334.77 1330.23,-783.07 1657.23,-783.07 1657.23,-334.77 1330.23,-334.77"/>
|
|
||||||
<text text-anchor="middle" x="1493.73" y="-769.47" font-family="Arial" font-size="12.00">Analysis Adapters Pattern</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust10" class="cluster">
|
|
||||||
<title>cluster_storage</title>
|
|
||||||
<polygon fill="#1976d2" stroke="#1976d2" points="988.41,-1158.6 988.41,-1371.11 1191.6,-1371.11 1191.6,-1158.6 988.41,-1158.6"/>
|
|
||||||
<text text-anchor="middle" x="1090.01" y="-1357.51" font-family="Arial" font-size="12.00">Media Storage</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust11" class="cluster">
|
|
||||||
<title>cluster_frontend</title>
|
|
||||||
<polygon fill="#1976d2" stroke="#1976d2" points="855.07,-839.84 855.07,-1378.38 979.95,-1378.38 979.95,-839.84 855.07,-839.84"/>
|
|
||||||
<text text-anchor="middle" x="917.51" y="-1364.78" font-family="Arial" font-size="12.00">Frontend</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust12" class="cluster">
|
|
||||||
<title>cluster_cloud</title>
|
|
||||||
<polygon fill="#1976d2" stroke="#1976d2" points="1569.4,-23.29 1569.4,-235.8 1813.85,-235.8 1813.85,-23.29 1569.4,-23.29"/>
|
|
||||||
<text text-anchor="middle" x="1691.62" y="-222.2" font-family="Arial" font-size="12.00">GCP Services</text>
|
|
||||||
</g>
|
|
||||||
<!-- implemented -->
|
|
||||||
<g id="node1" class="node">
|
|
||||||
<title>implemented</title>
|
|
||||||
<polygon fill="#28a745" stroke="transparent" points="239.18,-2019.29 150.18,-2019.29 150.18,-1983.29 239.18,-1983.29 239.18,-2019.29"/>
|
|
||||||
<text text-anchor="middle" x="194.68" y="-1998.79" font-family="Arial" font-size="10.00" fill="#ffffff">✅ Implemented</text>
|
|
||||||
</g>
|
|
||||||
<!-- planned -->
|
|
||||||
<g id="node2" class="node">
|
|
||||||
<title>planned</title>
|
|
||||||
<polygon fill="#ffc107" stroke="transparent" points="239.68,-1833.29 171.68,-1833.29 171.68,-1797.29 239.68,-1797.29 239.68,-1833.29"/>
|
|
||||||
<text text-anchor="middle" x="205.68" y="-1812.79" font-family="Arial" font-size="10.00" fill="#212529">⏳ Planned</text>
|
|
||||||
</g>
|
|
||||||
<!-- implemented->planned -->
|
|
||||||
<!-- code_pattern -->
|
|
||||||
<g id="node3" class="node">
|
|
||||||
<title>code_pattern</title>
|
|
||||||
<polygon fill="#8e24aa" stroke="transparent" points="121.68,-2019.29 31.68,-2019.29 31.68,-1983.29 121.68,-1983.29 121.68,-2019.29"/>
|
|
||||||
<text text-anchor="middle" x="76.68" y="-1998.79" font-family="Arial" font-size="10.00" fill="#ffffff">📐 Code Pattern</text>
|
|
||||||
</g>
|
|
||||||
<!-- service_arch -->
|
|
||||||
<g id="node4" class="node">
|
|
||||||
<title>service_arch</title>
|
|
||||||
<polygon fill="#1976d2" stroke="transparent" points="142.18,-1833.29 21.18,-1833.29 21.18,-1797.29 142.18,-1797.29 142.18,-1833.29"/>
|
|
||||||
<text text-anchor="middle" x="81.68" y="-1812.79" font-family="Arial" font-size="10.00" fill="#ffffff">⚡ Service/Architecture</text>
|
|
||||||
</g>
|
|
||||||
<!-- code_pattern->service_arch -->
|
|
||||||
<!-- webcam -->
|
|
||||||
<g id="node5" class="node">
|
|
||||||
<title>webcam</title>
|
|
||||||
<polygon fill="#28a745" stroke="black" points="324.18,-2019.29 269.18,-2019.29 269.18,-1983.29 324.18,-1983.29 324.18,-2019.29"/>
|
|
||||||
<text text-anchor="middle" x="296.68" y="-1998.79" font-family="Arial" font-size="10.00" fill="#ffffff">Webcam</text>
|
|
||||||
</g>
|
|
||||||
<!-- webcam_adapter -->
|
|
||||||
<g id="node9" class="node">
|
|
||||||
<title>webcam_adapter</title>
|
|
||||||
<polygon fill="#28a745" stroke="black" points="631.18,-1833.29 542.18,-1833.29 542.18,-1797.29 631.18,-1797.29 631.18,-1833.29"/>
|
|
||||||
<text text-anchor="middle" x="586.68" y="-1812.79" font-family="Arial" font-size="10.00" fill="#ffffff">WebcamAdapter</text>
|
|
||||||
</g>
|
|
||||||
<!-- webcam->webcam_adapter -->
|
|
||||||
<g id="edge3" class="edge">
|
|
||||||
<title>webcam->webcam_adapter</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M318.21,-1983.19C344.52,-1962.61 390.72,-1927.49 432.68,-1900.79 469.09,-1877.62 512.31,-1854.24 543.69,-1837.97"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="545.33,-1841.06 552.61,-1833.36 542.12,-1834.83 545.33,-1841.06"/>
|
|
||||||
</g>
|
|
||||||
<!-- rtmp -->
|
|
||||||
<g id="node6" class="node">
|
|
||||||
<title>rtmp</title>
|
|
||||||
<polygon fill="#28a745" stroke="black" points="512.68,-2019.29 444.68,-2019.29 444.68,-1983.29 512.68,-1983.29 512.68,-2019.29"/>
|
|
||||||
<text text-anchor="middle" x="478.68" y="-1998.79" font-family="Arial" font-size="10.00" fill="#ffffff">RTMP/OBS</text>
|
|
||||||
</g>
|
|
||||||
<!-- rtmp_adapter -->
|
|
||||||
<g id="node10" class="node">
|
|
||||||
<title>rtmp_adapter</title>
|
|
||||||
<polygon fill="#28a745" stroke="black" points="826.68,-1833.29 752.68,-1833.29 752.68,-1797.29 826.68,-1797.29 826.68,-1833.29"/>
|
|
||||||
<text text-anchor="middle" x="789.68" y="-1812.79" font-family="Arial" font-size="10.00" fill="#ffffff">RtmpAdapter</text>
|
|
||||||
</g>
|
|
||||||
<!-- rtmp->rtmp_adapter -->
|
|
||||||
<g id="edge4" class="edge">
|
|
||||||
<title>rtmp->rtmp_adapter</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M507.66,-1983.14C564.14,-1949.73 688.93,-1875.9 751.84,-1838.67"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="753.84,-1841.56 760.66,-1833.46 750.27,-1835.54 753.84,-1841.56"/>
|
|
||||||
</g>
|
|
||||||
<!-- files -->
|
|
||||||
<g id="node7" class="node">
|
|
||||||
<title>files</title>
|
|
||||||
<polygon fill="#ffc107" stroke="black" points="416.68,-2019.29 350.68,-2019.29 350.68,-1983.29 416.68,-1983.29 416.68,-2019.29"/>
|
|
||||||
<text text-anchor="middle" x="383.68" y="-1998.79" font-family="Arial" font-size="10.00" fill="#212529">File Upload</text>
|
|
||||||
</g>
|
|
||||||
<!-- file_adapter -->
|
|
||||||
<g id="node11" class="node">
|
|
||||||
<title>file_adapter</title>
|
|
||||||
<polygon fill="#ffc107" stroke="black" points="724.68,-1833.29 658.68,-1833.29 658.68,-1797.29 724.68,-1797.29 724.68,-1833.29"/>
|
|
||||||
<text text-anchor="middle" x="691.68" y="-1812.79" font-family="Arial" font-size="10.00" fill="#212529">FileAdapter</text>
|
|
||||||
</g>
|
|
||||||
<!-- files->file_adapter -->
|
|
||||||
<g id="edge28" class="edge">
|
|
||||||
<title>files->file_adapter</title>
|
|
||||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M406.89,-1983.22C435.24,-1962.66 484.94,-1927.58 529.68,-1900.79 569.19,-1877.13 615.98,-1853.13 649.1,-1836.79"/>
|
|
||||||
<polygon fill="#f57f17" stroke="#f57f17" points="651.05,-1839.73 658.48,-1832.18 647.96,-1833.44 651.05,-1839.73"/>
|
|
||||||
</g>
|
|
||||||
<!-- base_adapter -->
|
|
||||||
<g id="node8" class="node">
|
|
||||||
<title>base_adapter</title>
|
|
||||||
<polygon fill="#ce93d8" stroke="black" points="743.68,-2019.29 639.68,-2019.29 639.68,-1983.29 743.68,-1983.29 743.68,-2019.29"/>
|
|
||||||
<text text-anchor="middle" x="691.68" y="-2004.29" font-family="Arial" font-size="10.00">BaseSourceAdapter</text>
|
|
||||||
<text text-anchor="middle" x="691.68" y="-1993.29" font-family="Arial" font-size="10.00">(Abstract)</text>
|
|
||||||
</g>
|
|
||||||
<!-- base_adapter->webcam_adapter -->
|
|
||||||
<g id="edge38" class="edge">
|
|
||||||
<title>base_adapter->webcam_adapter</title>
|
|
||||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M681.8,-1982.98C663.31,-1950.57 623.36,-1880.56 601.58,-1842.39"/>
|
|
||||||
<polygon fill="gray" stroke="gray" points="604.45,-1840.37 596.46,-1833.42 598.37,-1843.84 604.45,-1840.37"/>
|
|
||||||
</g>
|
|
||||||
<!-- base_adapter->rtmp_adapter -->
|
|
||||||
<g id="edge39" class="edge">
|
|
||||||
<title>base_adapter->rtmp_adapter</title>
|
|
||||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M700.9,-1982.98C718.16,-1950.57 755.45,-1880.56 775.78,-1842.39"/>
|
|
||||||
<polygon fill="gray" stroke="gray" points="778.95,-1843.89 780.56,-1833.42 772.77,-1840.6 778.95,-1843.89"/>
|
|
||||||
</g>
|
|
||||||
<!-- base_adapter->file_adapter -->
|
|
||||||
<g id="edge40" class="edge">
|
|
||||||
<title>base_adapter->file_adapter</title>
|
|
||||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M691.68,-1982.98C691.68,-1950.98 691.68,-1882.33 691.68,-1843.86"/>
|
|
||||||
<polygon fill="gray" stroke="gray" points="695.18,-1843.42 691.68,-1833.42 688.18,-1843.42 695.18,-1843.42"/>
|
|
||||||
</g>
|
|
||||||
<!-- django -->
|
|
||||||
<g id="node12" class="node">
|
|
||||||
<title>django</title>
|
|
||||||
<polygon fill="#28a745" stroke="black" points="725.18,-1565.79 658.18,-1565.79 658.18,-1524.79 725.18,-1524.79 725.18,-1565.79"/>
|
|
||||||
<text text-anchor="middle" x="691.68" y="-1553.79" font-family="Arial" font-size="10.00" fill="#ffffff">Django API</text>
|
|
||||||
<text text-anchor="middle" x="691.68" y="-1542.79" font-family="Arial" font-size="10.00" fill="#ffffff">+ Channels</text>
|
|
||||||
<text text-anchor="middle" x="691.68" y="-1531.79" font-family="Arial" font-size="10.00" fill="#ffffff">:8000</text>
|
|
||||||
</g>
|
|
||||||
<!-- webcam_adapter->django -->
|
|
||||||
<g id="edge5" class="edge">
|
|
||||||
<title>webcam_adapter->django</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M593.41,-1797.12C611,-1752.21 658.51,-1630.94 680.38,-1575.12"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="683.64,-1576.4 684.03,-1565.81 677.13,-1573.84 683.64,-1576.4"/>
|
|
||||||
</g>
|
|
||||||
<!-- rtmp_adapter->django -->
|
|
||||||
<g id="edge6" class="edge">
|
|
||||||
<title>rtmp_adapter->django</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M783.4,-1797.12C767.02,-1752.31 722.82,-1631.45 702.35,-1575.47"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="705.54,-1574 698.82,-1565.81 698.97,-1576.4 705.54,-1574"/>
|
|
||||||
</g>
|
|
||||||
<!-- file_adapter->django -->
|
|
||||||
<g id="edge29" class="edge">
|
|
||||||
<title>file_adapter->django</title>
|
|
||||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M691.68,-1797.12C691.68,-1752.4 691.68,-1631.95 691.68,-1575.82"/>
|
|
||||||
<polygon fill="#f57f17" stroke="#f57f17" points="695.18,-1575.81 691.68,-1565.81 688.18,-1575.81 695.18,-1575.81"/>
|
|
||||||
</g>
|
|
||||||
<!-- postgres -->
|
|
||||||
<g id="node13" class="node">
|
|
||||||
<title>postgres</title>
|
|
||||||
<polygon fill="#28a745" stroke="black" points="835.68,-1252.29 765.68,-1252.29 765.68,-1216.29 835.68,-1216.29 835.68,-1252.29"/>
|
|
||||||
<text text-anchor="middle" x="800.68" y="-1237.29" font-family="Arial" font-size="10.00" fill="#ffffff">PostgreSQL</text>
|
|
||||||
<text text-anchor="middle" x="800.68" y="-1226.29" font-family="Arial" font-size="10.00" fill="#ffffff">Database</text>
|
|
||||||
</g>
|
|
||||||
<!-- django->postgres -->
|
|
||||||
<g id="edge7" class="edge">
|
|
||||||
<title>django->postgres</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M698.67,-1524.49C717.54,-1470.97 769.61,-1323.37 791.15,-1262.29"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="794.55,-1263.19 794.58,-1252.6 787.95,-1260.86 794.55,-1263.19"/>
|
|
||||||
</g>
|
|
||||||
<!-- redis -->
|
|
||||||
<g id="node14" class="node">
|
|
||||||
<title>redis</title>
|
|
||||||
<polygon fill="#28a745" stroke="black" points="738.68,-1252.29 652.68,-1252.29 652.68,-1216.29 738.68,-1216.29 738.68,-1252.29"/>
|
|
||||||
<text text-anchor="middle" x="695.68" y="-1237.29" font-family="Arial" font-size="10.00" fill="#ffffff">Redis</text>
|
|
||||||
<text text-anchor="middle" x="695.68" y="-1226.29" font-family="Arial" font-size="10.00" fill="#ffffff">Cache & Broker</text>
|
|
||||||
</g>
|
|
||||||
<!-- django->redis -->
|
|
||||||
<g id="edge8" class="edge">
|
|
||||||
<title>django->redis</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M691.94,-1524.49C692.63,-1471.08 694.53,-1323.98 695.33,-1262.67"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="698.83,-1262.64 695.46,-1252.6 691.83,-1262.55 698.83,-1262.64"/>
|
|
||||||
</g>
|
|
||||||
<!-- local_storage -->
|
|
||||||
<g id="node35" class="node">
|
|
||||||
<title>local_storage</title>
|
|
||||||
<polygon fill="#28a745" stroke="black" points="1076.68,-1252.29 998.68,-1252.29 998.68,-1216.29 1076.68,-1216.29 1076.68,-1252.29"/>
|
|
||||||
<text text-anchor="middle" x="1037.68" y="-1237.29" font-family="Arial" font-size="10.00" fill="#ffffff">Local Files</text>
|
|
||||||
<text text-anchor="middle" x="1037.68" y="-1226.29" font-family="Arial" font-size="10.00" fill="#ffffff">(nginx-served)</text>
|
|
||||||
</g>
|
|
||||||
<!-- django->local_storage -->
|
|
||||||
<g id="edge9" class="edge">
|
|
||||||
<title>django->local_storage</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M716.06,-1524.54C742.04,-1503.33 784.24,-1468.48 819.68,-1437.29 890.85,-1374.64 972.16,-1297.86 1012.4,-1259.51"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1015.06,-1261.8 1019.88,-1252.36 1010.23,-1256.74 1015.06,-1261.8"/>
|
|
||||||
</g>
|
|
||||||
<!-- gcs_storage -->
|
|
||||||
<g id="node36" class="node">
|
|
||||||
<title>gcs_storage</title>
|
|
||||||
<polygon fill="#ffc107" stroke="black" points="1180.68,-1252.29 1104.68,-1252.29 1104.68,-1216.29 1180.68,-1216.29 1180.68,-1252.29"/>
|
|
||||||
<text text-anchor="middle" x="1142.68" y="-1237.29" font-family="Arial" font-size="10.00" fill="#212529">Google Cloud</text>
|
|
||||||
<text text-anchor="middle" x="1142.68" y="-1226.29" font-family="Arial" font-size="10.00" fill="#212529">Storage</text>
|
|
||||||
</g>
|
|
||||||
<!-- django->gcs_storage -->
|
|
||||||
<g id="edge10" class="edge">
|
|
||||||
<title>django->gcs_storage</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M724.82,-1524.68C794.21,-1483.15 959.01,-1382.07 1087.68,-1283.79 1097.54,-1276.26 1107.86,-1267.43 1116.87,-1259.39"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1119.33,-1261.88 1124.4,-1252.57 1114.64,-1256.69 1119.33,-1261.88"/>
|
|
||||||
</g>
|
|
||||||
<!-- angular -->
|
|
||||||
<g id="node37" class="node">
|
|
||||||
<title>angular</title>
|
|
||||||
<polygon fill="#28a745" stroke="black" points="951.68,-1254.79 865.68,-1254.79 865.68,-1213.79 951.68,-1213.79 951.68,-1254.79"/>
|
|
||||||
<text text-anchor="middle" x="908.68" y="-1242.79" font-family="Arial" font-size="10.00" fill="#ffffff">Angular 17 SPA</text>
|
|
||||||
<text text-anchor="middle" x="908.68" y="-1231.79" font-family="Arial" font-size="10.00" fill="#ffffff">+ WebSocket</text>
|
|
||||||
<text text-anchor="middle" x="908.68" y="-1220.79" font-family="Arial" font-size="10.00" fill="#ffffff">:4200</text>
|
|
||||||
</g>
|
|
||||||
<!-- django->angular -->
|
|
||||||
<g id="edge25" class="edge">
|
|
||||||
<title>django->angular</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M705.59,-1524.49C742.94,-1471.29 845.59,-1325.13 888.94,-1263.4"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="892.01,-1265.12 894.89,-1254.92 886.28,-1261.1 892.01,-1265.12"/>
|
|
||||||
<text text-anchor="middle" x="794.18" y="-1430.89" font-family="Arial" font-size="8.00">WebSocket</text>
|
|
||||||
<text text-anchor="middle" x="794.18" y="-1421.89" font-family="Arial" font-size="8.00">API</text>
|
|
||||||
</g>
|
|
||||||
<!-- logo_worker -->
|
|
||||||
<g id="node27" class="node">
|
|
||||||
<title>logo_worker</title>
|
|
||||||
<polygon fill="#28a745" stroke="black" points="480.18,-935.79 397.18,-935.79 397.18,-894.79 480.18,-894.79 480.18,-935.79"/>
|
|
||||||
<text text-anchor="middle" x="438.68" y="-923.79" font-family="Arial" font-size="10.00" fill="#ffffff">Logo Detection</text>
|
|
||||||
<text text-anchor="middle" x="438.68" y="-912.79" font-family="Arial" font-size="10.00" fill="#ffffff">Worker</text>
|
|
||||||
<text text-anchor="middle" x="438.68" y="-901.79" font-family="Arial" font-size="10.00" fill="#ffffff">(logo_queue)</text>
|
|
||||||
</g>
|
|
||||||
<!-- redis->logo_worker -->
|
|
||||||
<g id="edge19" class="edge">
|
|
||||||
<title>redis->logo_worker</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M681.86,-1216.25C639.76,-1164.31 512.77,-1007.67 461.01,-943.83"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="463.52,-941.37 454.5,-935.8 458.08,-945.78 463.52,-941.37"/>
|
|
||||||
</g>
|
|
||||||
<!-- visual_worker -->
|
|
||||||
<g id="node28" class="node">
|
|
||||||
<title>visual_worker</title>
|
|
||||||
<polygon fill="#ffc107" stroke="black" points="600.18,-935.79 509.18,-935.79 509.18,-894.79 600.18,-894.79 600.18,-935.79"/>
|
|
||||||
<text text-anchor="middle" x="554.68" y="-923.79" font-family="Arial" font-size="10.00" fill="#212529">Visual Properties</text>
|
|
||||||
<text text-anchor="middle" x="554.68" y="-912.79" font-family="Arial" font-size="10.00" fill="#212529">Worker</text>
|
|
||||||
<text text-anchor="middle" x="554.68" y="-901.79" font-family="Arial" font-size="10.00" fill="#212529">(visual_queue)</text>
|
|
||||||
</g>
|
|
||||||
<!-- redis->visual_worker -->
|
|
||||||
<g id="edge30" class="edge">
|
|
||||||
<title>redis->visual_worker</title>
|
|
||||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M688.1,-1216.25C665.14,-1164.63 596.18,-1009.58 567.46,-945.01"/>
|
|
||||||
<polygon fill="#f57f17" stroke="#f57f17" points="570.62,-943.52 563.36,-935.8 564.23,-946.36 570.62,-943.52"/>
|
|
||||||
</g>
|
|
||||||
<!-- audio_worker -->
|
|
||||||
<g id="node29" class="node">
|
|
||||||
<title>audio_worker</title>
|
|
||||||
<polygon fill="#ffc107" stroke="black" points="835.18,-935.79 748.18,-935.79 748.18,-894.79 835.18,-894.79 835.18,-935.79"/>
|
|
||||||
<text text-anchor="middle" x="791.68" y="-923.79" font-family="Arial" font-size="10.00" fill="#212529">Audio Transcript</text>
|
|
||||||
<text text-anchor="middle" x="791.68" y="-912.79" font-family="Arial" font-size="10.00" fill="#212529">Worker</text>
|
|
||||||
<text text-anchor="middle" x="791.68" y="-901.79" font-family="Arial" font-size="10.00" fill="#212529">(audio_queue)</text>
|
|
||||||
</g>
|
|
||||||
<!-- redis->audio_worker -->
|
|
||||||
<g id="edge31" class="edge">
|
|
||||||
<title>redis->audio_worker</title>
|
|
||||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M700.84,-1216.25C716.44,-1164.73 763.23,-1010.22 782.86,-945.41"/>
|
|
||||||
<polygon fill="#f57f17" stroke="#f57f17" points="786.22,-946.39 785.77,-935.8 779.52,-944.36 786.22,-946.39"/>
|
|
||||||
</g>
|
|
||||||
<!-- text_worker -->
|
|
||||||
<g id="node30" class="node">
|
|
||||||
<title>text_worker</title>
|
|
||||||
<polygon fill="#ffc107" stroke="black" points="718.68,-935.79 628.68,-935.79 628.68,-894.79 718.68,-894.79 718.68,-935.79"/>
|
|
||||||
<text text-anchor="middle" x="673.68" y="-923.79" font-family="Arial" font-size="10.00" fill="#212529">Text Recognition</text>
|
|
||||||
<text text-anchor="middle" x="673.68" y="-912.79" font-family="Arial" font-size="10.00" fill="#212529">Worker</text>
|
|
||||||
<text text-anchor="middle" x="673.68" y="-901.79" font-family="Arial" font-size="10.00" fill="#212529">(text_queue)</text>
|
|
||||||
</g>
|
|
||||||
<!-- redis->text_worker -->
|
|
||||||
<g id="edge32" class="edge">
|
|
||||||
<title>redis->text_worker</title>
|
|
||||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M694.5,-1216.25C690.93,-1164.84 680.24,-1010.86 675.73,-945.81"/>
|
|
||||||
<polygon fill="#f57f17" stroke="#f57f17" points="679.22,-945.54 675.04,-935.8 672.24,-946.02 679.22,-945.54"/>
|
|
||||||
</g>
|
|
||||||
<!-- nginx -->
|
|
||||||
<g id="node15" class="node">
|
|
||||||
<title>nginx</title>
|
|
||||||
<polygon fill="#28a745" stroke="black" points="835.68,-1565.79 753.68,-1565.79 753.68,-1524.79 835.68,-1524.79 835.68,-1565.79"/>
|
|
||||||
<text text-anchor="middle" x="794.68" y="-1553.79" font-family="Arial" font-size="10.00" fill="#ffffff">NGINX</text>
|
|
||||||
<text text-anchor="middle" x="794.68" y="-1542.79" font-family="Arial" font-size="10.00" fill="#ffffff">Reverse Proxy</text>
|
|
||||||
<text text-anchor="middle" x="794.68" y="-1531.79" font-family="Arial" font-size="10.00" fill="#ffffff">:80</text>
|
|
||||||
</g>
|
|
||||||
<!-- nginx->angular -->
|
|
||||||
<g id="edge27" class="edge">
|
|
||||||
<title>nginx->angular</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M801.99,-1524.49C821.49,-1471.62 874.87,-1326.93 897.89,-1264.55"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="901.26,-1265.52 901.44,-1254.92 894.69,-1263.09 901.26,-1265.52"/>
|
|
||||||
</g>
|
|
||||||
<!-- base_strategy -->
|
|
||||||
<g id="node16" class="node">
|
|
||||||
<title>base_strategy</title>
|
|
||||||
<polygon fill="#ce93d8" stroke="black" points="1877.18,-933.29 1758.18,-933.29 1758.18,-897.29 1877.18,-897.29 1877.18,-933.29"/>
|
|
||||||
<text text-anchor="middle" x="1817.68" y="-918.29" font-family="Arial" font-size="10.00">BaseExecutionStrategy</text>
|
|
||||||
<text text-anchor="middle" x="1817.68" y="-907.29" font-family="Arial" font-size="10.00">(Abstract)</text>
|
|
||||||
</g>
|
|
||||||
<!-- local_strategy -->
|
|
||||||
<g id="node17" class="node">
|
|
||||||
<title>local_strategy</title>
|
|
||||||
<polygon fill="#28a745" stroke="black" points="1754.18,-664.29 1677.18,-664.29 1677.18,-628.29 1754.18,-628.29 1754.18,-664.29"/>
|
|
||||||
<text text-anchor="middle" x="1715.68" y="-643.79" font-family="Arial" font-size="10.00" fill="#ffffff">LocalStrategy</text>
|
|
||||||
</g>
|
|
||||||
<!-- base_strategy->local_strategy -->
|
|
||||||
<g id="edge41" class="edge">
|
|
||||||
<title>base_strategy->local_strategy</title>
|
|
||||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1811.15,-897.18C1793.84,-851.89 1746.75,-728.61 1725.87,-673.97"/>
|
|
||||||
<polygon fill="gray" stroke="gray" points="1729.13,-672.69 1722.29,-664.6 1722.59,-675.19 1729.13,-672.69"/>
|
|
||||||
</g>
|
|
||||||
<!-- lan_strategy -->
|
|
||||||
<g id="node18" class="node">
|
|
||||||
<title>lan_strategy</title>
|
|
||||||
<polygon fill="#ffc107" stroke="black" points="1854.18,-664.29 1781.18,-664.29 1781.18,-628.29 1854.18,-628.29 1854.18,-664.29"/>
|
|
||||||
<text text-anchor="middle" x="1817.68" y="-643.79" font-family="Arial" font-size="10.00" fill="#212529">LANStrategy</text>
|
|
||||||
</g>
|
|
||||||
<!-- base_strategy->lan_strategy -->
|
|
||||||
<g id="edge42" class="edge">
|
|
||||||
<title>base_strategy->lan_strategy</title>
|
|
||||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1817.68,-897.18C1817.68,-852.08 1817.68,-729.63 1817.68,-674.65"/>
|
|
||||||
<polygon fill="gray" stroke="gray" points="1821.18,-674.6 1817.68,-664.6 1814.18,-674.6 1821.18,-674.6"/>
|
|
||||||
</g>
|
|
||||||
<!-- cloud_strategy -->
|
|
||||||
<g id="node19" class="node">
|
|
||||||
<title>cloud_strategy</title>
|
|
||||||
<polygon fill="#ffc107" stroke="black" points="1961.18,-664.29 1882.18,-664.29 1882.18,-628.29 1961.18,-628.29 1961.18,-664.29"/>
|
|
||||||
<text text-anchor="middle" x="1921.68" y="-643.79" font-family="Arial" font-size="10.00" fill="#212529">CloudStrategy</text>
|
|
||||||
</g>
|
|
||||||
<!-- base_strategy->cloud_strategy -->
|
|
||||||
<g id="edge43" class="edge">
|
|
||||||
<title>base_strategy->cloud_strategy</title>
|
|
||||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1824.35,-897.18C1841.99,-851.89 1890.01,-728.61 1911.29,-673.97"/>
|
|
||||||
<polygon fill="gray" stroke="gray" points="1914.57,-675.19 1914.94,-664.6 1908.05,-672.65 1914.57,-675.19"/>
|
|
||||||
</g>
|
|
||||||
<!-- clip_adapter -->
|
|
||||||
<g id="node32" class="node">
|
|
||||||
<title>clip_adapter</title>
|
|
||||||
<polygon fill="#28a745" stroke="black" points="1412.68,-428.29 1340.68,-428.29 1340.68,-392.29 1412.68,-392.29 1412.68,-428.29"/>
|
|
||||||
<text text-anchor="middle" x="1376.68" y="-413.29" font-family="Arial" font-size="10.00" fill="#ffffff">CLIPAdapter</text>
|
|
||||||
<text text-anchor="middle" x="1376.68" y="-402.29" font-family="Arial" font-size="10.00" fill="#ffffff">(Local)</text>
|
|
||||||
</g>
|
|
||||||
<!-- local_strategy->clip_adapter -->
|
|
||||||
<g id="edge21" class="edge">
|
|
||||||
<title>local_strategy->clip_adapter</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M1691.03,-628.27C1631.12,-586.92 1477.85,-481.12 1409.93,-434.24"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1411.92,-431.36 1401.7,-428.56 1407.94,-437.12 1411.92,-431.36"/>
|
|
||||||
</g>
|
|
||||||
<!-- gcp_vision -->
|
|
||||||
<g id="node33" class="node">
|
|
||||||
<title>gcp_vision</title>
|
|
||||||
<polygon fill="#ffc107" stroke="black" points="1540.18,-428.29 1441.18,-428.29 1441.18,-392.29 1540.18,-392.29 1540.18,-428.29"/>
|
|
||||||
<text text-anchor="middle" x="1490.68" y="-413.29" font-family="Arial" font-size="10.00" fill="#212529">GCPVisionAdapter</text>
|
|
||||||
<text text-anchor="middle" x="1490.68" y="-402.29" font-family="Arial" font-size="10.00" fill="#212529">(Cloud)</text>
|
|
||||||
</g>
|
|
||||||
<!-- local_strategy->gcp_vision -->
|
|
||||||
<g id="edge22" class="edge">
|
|
||||||
<title>local_strategy->gcp_vision</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M1702.78,-628.22C1682.83,-602.22 1642.81,-551.56 1604.68,-512.29 1577.5,-484.29 1543.76,-455 1520.07,-435.22"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1522.07,-432.33 1512.13,-428.64 1517.6,-437.71 1522.07,-432.33"/>
|
|
||||||
</g>
|
|
||||||
<!-- yolo_adapter -->
|
|
||||||
<g id="node34" class="node">
|
|
||||||
<title>yolo_adapter</title>
|
|
||||||
<polygon fill="#ffc107" stroke="black" points="1646.18,-428.29 1569.18,-428.29 1569.18,-392.29 1646.18,-392.29 1646.18,-428.29"/>
|
|
||||||
<text text-anchor="middle" x="1607.68" y="-413.29" font-family="Arial" font-size="10.00" fill="#212529">YOLOAdapter</text>
|
|
||||||
<text text-anchor="middle" x="1607.68" y="-402.29" font-family="Arial" font-size="10.00" fill="#212529">(Planned)</text>
|
|
||||||
</g>
|
|
||||||
<!-- lan_strategy->yolo_adapter -->
|
|
||||||
<g id="edge37" class="edge">
|
|
||||||
<title>lan_strategy->yolo_adapter</title>
|
|
||||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M1802.24,-628.08C1765.53,-587.17 1672.91,-483.97 1630.06,-436.23"/>
|
|
||||||
<polygon fill="#f57f17" stroke="#f57f17" points="1632.43,-433.62 1623.15,-428.52 1627.22,-438.3 1632.43,-433.62"/>
|
|
||||||
</g>
|
|
||||||
<!-- speech_api -->
|
|
||||||
<g id="node40" class="node">
|
|
||||||
<title>speech_api</title>
|
|
||||||
<polygon fill="#ffc107" stroke="black" points="1802.18,-117.29 1701.18,-117.29 1701.18,-81.29 1802.18,-81.29 1802.18,-117.29"/>
|
|
||||||
<text text-anchor="middle" x="1751.68" y="-102.29" font-family="Arial" font-size="10.00" fill="#212529">Speech-to-Text API</text>
|
|
||||||
<text text-anchor="middle" x="1751.68" y="-91.29" font-family="Arial" font-size="10.00" fill="#212529">(Audio Transcript)</text>
|
|
||||||
</g>
|
|
||||||
<!-- cloud_strategy->speech_api -->
|
|
||||||
<g id="edge36" class="edge">
|
|
||||||
<title>cloud_strategy->speech_api</title>
|
|
||||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M1916.31,-628.08C1891.75,-549.34 1790.02,-223.21 1760.04,-127.07"/>
|
|
||||||
<polygon fill="#f57f17" stroke="#f57f17" points="1763.37,-126.01 1757.05,-117.5 1756.69,-128.09 1763.37,-126.01"/>
|
|
||||||
</g>
|
|
||||||
<!-- base_event_source -->
|
|
||||||
<g id="node20" class="node">
|
|
||||||
<title>base_event_source</title>
|
|
||||||
<polygon fill="#ce93d8" stroke="black" points="1490.68,-1252.29 1378.68,-1252.29 1378.68,-1216.29 1490.68,-1216.29 1490.68,-1252.29"/>
|
|
||||||
<text text-anchor="middle" x="1434.68" y="-1237.29" font-family="Arial" font-size="10.00">SegmentEventSource</text>
|
|
||||||
<text text-anchor="middle" x="1434.68" y="-1226.29" font-family="Arial" font-size="10.00">(Abstract)</text>
|
|
||||||
</g>
|
|
||||||
<!-- file_watcher_source -->
|
|
||||||
<g id="node21" class="node">
|
|
||||||
<title>file_watcher_source</title>
|
|
||||||
<polygon fill="#28a745" stroke="black" points="1338.18,-933.29 1213.18,-933.29 1213.18,-897.29 1338.18,-897.29 1338.18,-933.29"/>
|
|
||||||
<text text-anchor="middle" x="1275.68" y="-918.29" font-family="Arial" font-size="10.00" fill="#ffffff">FileWatcherEventSource</text>
|
|
||||||
<text text-anchor="middle" x="1275.68" y="-907.29" font-family="Arial" font-size="10.00" fill="#ffffff">(Local/LAN)</text>
|
|
||||||
</g>
|
|
||||||
<!-- base_event_source->file_watcher_source -->
|
|
||||||
<g id="edge47" class="edge">
|
|
||||||
<title>base_event_source->file_watcher_source</title>
|
|
||||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1426.13,-1216.25C1399.93,-1164 1320.58,-1005.81 1288.93,-942.7"/>
|
|
||||||
<polygon fill="gray" stroke="gray" points="1291.88,-940.79 1284.27,-933.42 1285.63,-943.92 1291.88,-940.79"/>
|
|
||||||
</g>
|
|
||||||
<!-- cloud_storage_source -->
|
|
||||||
<g id="node22" class="node">
|
|
||||||
<title>cloud_storage_source</title>
|
|
||||||
<polygon fill="#ffc107" stroke="black" points="1500.68,-933.29 1368.68,-933.29 1368.68,-897.29 1500.68,-897.29 1500.68,-933.29"/>
|
|
||||||
<text text-anchor="middle" x="1434.68" y="-918.29" font-family="Arial" font-size="10.00" fill="#212529">CloudStorageEventSource</text>
|
|
||||||
<text text-anchor="middle" x="1434.68" y="-907.29" font-family="Arial" font-size="10.00" fill="#212529">(GCS Pub/Sub)</text>
|
|
||||||
</g>
|
|
||||||
<!-- base_event_source->cloud_storage_source -->
|
|
||||||
<g id="edge48" class="edge">
|
|
||||||
<title>base_event_source->cloud_storage_source</title>
|
|
||||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1434.68,-1216.25C1434.68,-1164.21 1434.68,-1007.11 1434.68,-943.48"/>
|
|
||||||
<polygon fill="gray" stroke="gray" points="1438.18,-943.42 1434.68,-933.42 1431.18,-943.42 1438.18,-943.42"/>
|
|
||||||
</g>
|
|
||||||
<!-- webhook_source -->
|
|
||||||
<g id="node23" class="node">
|
|
||||||
<title>webhook_source</title>
|
|
||||||
<polygon fill="#ffc107" stroke="black" points="1645.18,-933.29 1532.18,-933.29 1532.18,-897.29 1645.18,-897.29 1645.18,-933.29"/>
|
|
||||||
<text text-anchor="middle" x="1588.68" y="-918.29" font-family="Arial" font-size="10.00" fill="#212529">WebhookEventSource</text>
|
|
||||||
<text text-anchor="middle" x="1588.68" y="-907.29" font-family="Arial" font-size="10.00" fill="#212529">(External)</text>
|
|
||||||
</g>
|
|
||||||
<!-- base_event_source->webhook_source -->
|
|
||||||
<g id="edge49" class="edge">
|
|
||||||
<title>base_event_source->webhook_source</title>
|
|
||||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1442.96,-1216.25C1468.34,-1164 1545.19,-1005.81 1575.85,-942.7"/>
|
|
||||||
<polygon fill="gray" stroke="gray" points="1579.14,-943.94 1580.36,-933.42 1572.84,-940.88 1579.14,-943.94"/>
|
|
||||||
</g>
|
|
||||||
<!-- event_source_manager -->
|
|
||||||
<g id="node24" class="node">
|
|
||||||
<title>event_source_manager</title>
|
|
||||||
<polygon fill="#28a745" stroke="black" points="1309.68,-664.29 1199.68,-664.29 1199.68,-628.29 1309.68,-628.29 1309.68,-664.29"/>
|
|
||||||
<text text-anchor="middle" x="1254.68" y="-649.29" font-family="Arial" font-size="10.00" fill="#ffffff">EventSourceManager</text>
|
|
||||||
<text text-anchor="middle" x="1254.68" y="-638.29" font-family="Arial" font-size="10.00" fill="#ffffff">(Environment-based)</text>
|
|
||||||
</g>
|
|
||||||
<!-- file_watcher_source->event_source_manager -->
|
|
||||||
<g id="edge13" class="edge">
|
|
||||||
<title>file_watcher_source->event_source_manager</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M1274.34,-897.18C1270.79,-852.08 1261.16,-729.63 1256.83,-674.65"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1260.32,-674.29 1256.04,-664.6 1253.34,-674.84 1260.32,-674.29"/>
|
|
||||||
</g>
|
|
||||||
<!-- cloud_storage_source->event_source_manager -->
|
|
||||||
<g id="edge14" class="edge">
|
|
||||||
<title>cloud_storage_source->event_source_manager</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M1423.15,-897.18C1392.42,-851.61 1308.47,-727.07 1271.99,-672.96"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1274.84,-670.93 1266.35,-664.6 1269.04,-674.85 1274.84,-670.93"/>
|
|
||||||
</g>
|
|
||||||
<!-- webhook_source->event_source_manager -->
|
|
||||||
<g id="edge15" class="edge">
|
|
||||||
<title>webhook_source->event_source_manager</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M1567.28,-897.18C1509.57,-851.04 1350.61,-723.97 1284.32,-670.98"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1286.33,-668.11 1276.34,-664.6 1281.96,-673.58 1286.33,-668.11"/>
|
|
||||||
</g>
|
|
||||||
<!-- redis_events -->
|
|
||||||
<g id="node25" class="node">
|
|
||||||
<title>redis_events</title>
|
|
||||||
<polygon fill="#28a745" stroke="black" points="1299.68,-428.29 1197.68,-428.29 1197.68,-392.29 1299.68,-392.29 1299.68,-428.29"/>
|
|
||||||
<text text-anchor="middle" x="1248.68" y="-413.29" font-family="Arial" font-size="10.00" fill="#ffffff">Redis Event Queue</text>
|
|
||||||
<text text-anchor="middle" x="1248.68" y="-402.29" font-family="Arial" font-size="10.00" fill="#ffffff">(segment_events)</text>
|
|
||||||
</g>
|
|
||||||
<!-- event_source_manager->redis_events -->
|
|
||||||
<g id="edge16" class="edge">
|
|
||||||
<title>event_source_manager->redis_events</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M1254.24,-628.08C1253.21,-587.85 1250.63,-487.37 1249.38,-438.64"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1252.88,-438.43 1249.12,-428.52 1245.88,-438.61 1252.88,-438.43"/>
|
|
||||||
<text text-anchor="middle" x="1268.68" y="-523.89" font-family="Arial" font-size="8.00">publishes</text>
|
|
||||||
<text text-anchor="middle" x="1268.68" y="-514.89" font-family="Arial" font-size="8.00">events</text>
|
|
||||||
</g>
|
|
||||||
<!-- event_processor -->
|
|
||||||
<g id="node26" class="node">
|
|
||||||
<title>event_processor</title>
|
|
||||||
<polygon fill="#28a745" stroke="black" points="1282.68,-117.29 1188.68,-117.29 1188.68,-81.29 1282.68,-81.29 1282.68,-117.29"/>
|
|
||||||
<text text-anchor="middle" x="1235.68" y="-102.29" font-family="Arial" font-size="10.00" fill="#ffffff">Event Processor</text>
|
|
||||||
<text text-anchor="middle" x="1235.68" y="-91.29" font-family="Arial" font-size="10.00" fill="#ffffff">(triggers analysis)</text>
|
|
||||||
</g>
|
|
||||||
<!-- redis_events->event_processor -->
|
|
||||||
<g id="edge17" class="edge">
|
|
||||||
<title>redis_events->event_processor</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M1247.96,-392.24C1245.82,-341.27 1239.45,-189.79 1236.83,-127.5"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1240.31,-127.13 1236.4,-117.29 1233.32,-127.43 1240.31,-127.13"/>
|
|
||||||
<text text-anchor="middle" x="1262.18" y="-287.89" font-family="Arial" font-size="8.00">consumes</text>
|
|
||||||
<text text-anchor="middle" x="1262.18" y="-278.89" font-family="Arial" font-size="8.00">events</text>
|
|
||||||
</g>
|
|
||||||
<!-- event_processor->logo_worker -->
|
|
||||||
<g id="edge18" class="edge">
|
|
||||||
<title>event_processor->logo_worker</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M1219.03,-117.29C1118.31,-220.17 590.46,-759.27 465.31,-887.09"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="462.48,-884.98 457.99,-894.57 467.48,-889.87 462.48,-884.98"/>
|
|
||||||
<text text-anchor="middle" x="844.68" y="-523.89" font-family="Arial" font-size="8.00">triggers</text>
|
|
||||||
<text text-anchor="middle" x="844.68" y="-514.89" font-family="Arial" font-size="8.00">analysis</text>
|
|
||||||
</g>
|
|
||||||
<!-- logo_worker->django -->
|
|
||||||
<g id="edge24" class="edge">
|
|
||||||
<title>logo_worker->django</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M446.55,-935.81C483.06,-1026.45 636.1,-1406.32 679.91,-1515.08"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="676.71,-1516.49 683.69,-1524.46 683.2,-1513.87 676.71,-1516.49"/>
|
|
||||||
<text text-anchor="middle" x="599.68" y="-1236.89" font-family="Arial" font-size="8.00">analysis</text>
|
|
||||||
<text text-anchor="middle" x="599.68" y="-1227.89" font-family="Arial" font-size="8.00">results</text>
|
|
||||||
</g>
|
|
||||||
<!-- logo_worker->local_strategy -->
|
|
||||||
<g id="edge20" class="edge">
|
|
||||||
<title>logo_worker->local_strategy</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M476.74,-894.69C483.88,-891.64 491.4,-888.82 498.68,-886.79 1003.74,-745.56 1195.76,-932.06 1662.68,-693.29 1673.48,-687.77 1683.89,-679.59 1692.59,-671.65"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1695.24,-673.95 1700.05,-664.51 1690.41,-668.89 1695.24,-673.95"/>
|
|
||||||
</g>
|
|
||||||
<!-- visual_worker->lan_strategy -->
|
|
||||||
<g id="edge33" class="edge">
|
|
||||||
<title>visual_worker->lan_strategy</title>
|
|
||||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M594.34,-894.72C601.93,-891.62 609.94,-888.79 617.68,-886.79 975.57,-794.36 1084.97,-889.51 1445.68,-808.79 1593.23,-775.77 1635.63,-770.42 1765.68,-693.29 1775.84,-687.26 1785.84,-679.11 1794.32,-671.34"/>
|
|
||||||
<polygon fill="#f57f17" stroke="#f57f17" points="1796.8,-673.81 1801.63,-664.39 1791.97,-668.74 1796.8,-673.81"/>
|
|
||||||
</g>
|
|
||||||
<!-- audio_worker->cloud_strategy -->
|
|
||||||
<g id="edge34" class="edge">
|
|
||||||
<title>audio_worker->cloud_strategy</title>
|
|
||||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M828.42,-894.66C835.33,-891.61 842.61,-888.81 849.68,-886.79 1144.76,-802.58 1236.56,-877.23 1535.68,-808.79 1687.16,-774.13 1730.23,-769.44 1865.68,-693.29 1876.54,-687.18 1887.37,-678.92 1896.58,-671.08"/>
|
|
||||||
<polygon fill="#f57f17" stroke="#f57f17" points="1899.01,-673.6 1904.21,-664.37 1894.39,-668.35 1899.01,-673.6"/>
|
|
||||||
</g>
|
|
||||||
<!-- text_worker->cloud_strategy -->
|
|
||||||
<g id="edge35" class="edge">
|
|
||||||
<title>text_worker->cloud_strategy</title>
|
|
||||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M714,-894.79C721.72,-891.68 729.84,-888.83 737.68,-886.79 1065.47,-801.63 1163.94,-876.92 1495.68,-808.79 1664.43,-774.13 1714.1,-775.14 1865.68,-693.29 1876.64,-687.37 1887.5,-679.15 1896.7,-671.29"/>
|
|
||||||
<polygon fill="#f57f17" stroke="#f57f17" points="1899.14,-673.8 1904.31,-664.55 1894.5,-668.56 1899.14,-673.8"/>
|
|
||||||
</g>
|
|
||||||
<!-- base_ai -->
|
|
||||||
<g id="node31" class="node">
|
|
||||||
<title>base_ai</title>
|
|
||||||
<polygon fill="#ce93d8" stroke="black" points="1536.68,-664.29 1444.68,-664.29 1444.68,-628.29 1536.68,-628.29 1536.68,-664.29"/>
|
|
||||||
<text text-anchor="middle" x="1490.68" y="-649.29" font-family="Arial" font-size="10.00">DetectionAdapter</text>
|
|
||||||
<text text-anchor="middle" x="1490.68" y="-638.29" font-family="Arial" font-size="10.00">(Abstract)</text>
|
|
||||||
</g>
|
|
||||||
<!-- base_ai->clip_adapter -->
|
|
||||||
<g id="edge44" class="edge">
|
|
||||||
<title>base_ai->clip_adapter</title>
|
|
||||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1482.3,-628.08C1462.58,-587.6 1413.13,-486.1 1389.56,-437.72"/>
|
|
||||||
<polygon fill="gray" stroke="gray" points="1392.6,-435.98 1385.08,-428.52 1386.31,-439.04 1392.6,-435.98"/>
|
|
||||||
</g>
|
|
||||||
<!-- base_ai->gcp_vision -->
|
|
||||||
<g id="edge45" class="edge">
|
|
||||||
<title>base_ai->gcp_vision</title>
|
|
||||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1490.68,-628.08C1490.68,-587.85 1490.68,-487.37 1490.68,-438.64"/>
|
|
||||||
<polygon fill="gray" stroke="gray" points="1494.18,-438.52 1490.68,-428.52 1487.18,-438.52 1494.18,-438.52"/>
|
|
||||||
</g>
|
|
||||||
<!-- base_ai->yolo_adapter -->
|
|
||||||
<g id="edge46" class="edge">
|
|
||||||
<title>base_ai->yolo_adapter</title>
|
|
||||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1499.28,-628.08C1519.53,-587.6 1570.28,-486.1 1594.46,-437.72"/>
|
|
||||||
<polygon fill="gray" stroke="gray" points="1597.72,-439.03 1599.07,-428.52 1591.46,-435.9 1597.72,-439.03"/>
|
|
||||||
</g>
|
|
||||||
<!-- vision_api -->
|
|
||||||
<g id="node39" class="node">
|
|
||||||
<title>vision_api</title>
|
|
||||||
<polygon fill="#ffc107" stroke="black" points="1672.18,-117.29 1581.18,-117.29 1581.18,-81.29 1672.18,-81.29 1672.18,-117.29"/>
|
|
||||||
<text text-anchor="middle" x="1626.68" y="-102.29" font-family="Arial" font-size="10.00" fill="#212529">Cloud Vision API</text>
|
|
||||||
<text text-anchor="middle" x="1626.68" y="-91.29" font-family="Arial" font-size="10.00" fill="#212529">(Logo Detection)</text>
|
|
||||||
</g>
|
|
||||||
<!-- gcp_vision->vision_api -->
|
|
||||||
<g id="edge23" class="edge">
|
|
||||||
<title>gcp_vision->vision_api</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M1498.19,-392.24C1520.71,-341.06 1587.84,-188.54 1615.04,-126.73"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1618.37,-127.85 1619.2,-117.29 1611.97,-125.03 1618.37,-127.85"/>
|
|
||||||
</g>
|
|
||||||
<!-- local_storage->file_watcher_source -->
|
|
||||||
<g id="edge11" class="edge">
|
|
||||||
<title>local_storage->file_watcher_source</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M1045.51,-1216.18C1057.76,-1190.12 1082.96,-1139.4 1110.68,-1100.29 1154.5,-1038.45 1216.41,-974.35 1250.58,-940.54"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1253.16,-942.92 1257.83,-933.41 1248.25,-937.92 1253.16,-942.92"/>
|
|
||||||
<text text-anchor="middle" x="1137.18" y="-1111.89" font-family="Arial" font-size="8.00">monitors</text>
|
|
||||||
<text text-anchor="middle" x="1137.18" y="-1102.89" font-family="Arial" font-size="8.00">HLS segments</text>
|
|
||||||
</g>
|
|
||||||
<!-- gcs_storage->cloud_storage_source -->
|
|
||||||
<g id="edge12" class="edge">
|
|
||||||
<title>gcs_storage->cloud_storage_source</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M1158.38,-1216.25C1207,-1163.46 1355.23,-1002.54 1412.12,-940.78"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1414.7,-943.14 1418.9,-933.42 1409.56,-938.4 1414.7,-943.14"/>
|
|
||||||
<text text-anchor="middle" x="1275.68" y="-1111.89" font-family="Arial" font-size="8.00">storage</text>
|
|
||||||
<text text-anchor="middle" x="1275.68" y="-1102.89" font-family="Arial" font-size="8.00">events</text>
|
|
||||||
</g>
|
|
||||||
<!-- hls_player -->
|
|
||||||
<g id="node38" class="node">
|
|
||||||
<title>hls_player</title>
|
|
||||||
<polygon fill="#28a745" stroke="black" points="968.18,-933.29 867.18,-933.29 867.18,-897.29 968.18,-897.29 968.18,-933.29"/>
|
|
||||||
<text text-anchor="middle" x="917.68" y="-918.29" font-family="Arial" font-size="10.00" fill="#ffffff">HLS.js Player</text>
|
|
||||||
<text text-anchor="middle" x="917.68" y="-907.29" font-family="Arial" font-size="10.00" fill="#ffffff">+ Canvas Overlays</text>
|
|
||||||
</g>
|
|
||||||
<!-- angular->hls_player -->
|
|
||||||
<g id="edge26" class="edge">
|
|
||||||
<title>angular->hls_player</title>
|
|
||||||
<path fill="none" stroke="#2e7d32" d="M909.24,-1213.46C910.79,-1158.86 915.13,-1006.23 916.91,-943.62"/>
|
|
||||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="920.41,-943.45 917.2,-933.35 913.41,-943.25 920.41,-943.45"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 40 KiB |
@@ -1,153 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
+-------+
|
|
||||||
| OBS | RTMP stream
|
|
||||||
+---+---+ --------------+
|
|
||||||
| |
|
|
||||||
v |
|
|
||||||
+-----+------+ |
|
|
||||||
| nginx-rtmp |-- HLS ───+ |
|
|
||||||
| (RTMP/HLS) | | |
|
|
||||||
+-----+------+ | |
|
|
||||||
| | |
|
|
||||||
HLS on /media | |
|
|
||||||
| | |
|
|
||||||
v | |
|
|
||||||
+----------+-----------+ | |
|
|
||||||
| Host “media/” dir |<----+ |
|
|
||||||
+----------+-----------+ |
|
|
||||||
| File watcher |
|
|
||||||
v |
|
|
||||||
+----------+-----------+ |
|
|
||||||
| Django Backend | |
|
|
||||||
| (REST + ASGI + WS) +<--WebSocket--+
|
|
||||||
+---+------------------+ |
|
|
||||||
| | |
|
|
||||||
| +-------+ HTTP/API |
|
|
||||||
| v |
|
|
||||||
| +-------+------+ |
|
|
||||||
| | PostgreSQL | |
|
|
||||||
| +-------+------+ |
|
|
||||||
| |
|
|
||||||
| Celery tasks |
|
|
||||||
v |
|
|
||||||
+------+--------+ queue +------+--------+
|
|
||||||
| celery-logo |<-----------| celery-default |
|
|
||||||
+---------------+ +---------------+
|
|
||||||
| ^
|
|
||||||
| |
|
|
||||||
+----> Redis (broker) <------+
|
|
||||||
|
|
||||||
Frontend:
|
|
||||||
+-----------+
|
|
||||||
| Angular |
|
|
||||||
| (4200) |
|
|
||||||
+-----+-----+
|
|
||||||
|
|
|
||||||
| HTTP/API (/api/…)
|
|
||||||
|
|
|
||||||
+-----v-----+
|
|
||||||
| Nginx |
|
|
||||||
| (SSP) |
|
|
||||||
+-----------+
|
|
||||||
|
|
||||||
Legend:
|
|
||||||
|
|
||||||
• OBS pushes RTMP into nginx-rtmp, which writes HLS (.m3u8/.ts) into the shared media/ folder.
|
|
||||||
• Django’s file-watcher polls media/ for new segments and enqueues Celery tasks.
|
|
||||||
• Django Backend serves REST API, WebSockets (via Channels → Redis), and hosts analysis logic.
|
|
||||||
• Celery Workers consume tasks from Redis:
|
|
||||||
– celery-logo for logo detection
|
|
||||||
– celery-default (or others) for visual analysis, etc.
|
|
||||||
• PostgreSQL holds application & analysis metadata.
|
|
||||||
• Angular (dev or static via Nginx) calls the Django API and listens on WebSockets.
|
|
||||||
• Nginx (frontend) proxies /api and /ws to Django and serves the Angular SPA.
|
|
||||||
|
|
||||||
This layout keeps streaming, AI-processing, data storage, and UI layers cleanly separated.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
codex
|
|
||||||
Below is an updated architecture sketch—with the local webcam path and AI execution‐strategy layer added. Each box is a service or component, and arrows show the main flows:
|
|
||||||
|
|
||||||
+--------+
|
|
||||||
| OBS | RTMP stream
|
|
||||||
+---+----+ --------+
|
|
||||||
| |
|
|
||||||
v |
|
|
||||||
+-------+------+ |
|
|
||||||
| nginx-rtmp |-- HLS →| (writes .m3u8/.ts to host media/)
|
|
||||||
| (RTMP/HLS) | |
|
|
||||||
+-------+------+ |
|
|
||||||
| |
|
|
||||||
+------------+---------------+-------------+
|
|
||||||
| HLS |
|
|
||||||
v |
|
|
||||||
+------+-------+ |
|
|
||||||
| Host “media/”|<-----------------------------------+
|
|
||||||
| directory | (mount in backend & nginx-rtmp)
|
|
||||||
+------+-------+
|
|
||||||
|
|
|
||||||
| (poll .ts files)
|
|
||||||
v
|
|
||||||
+------+-------+
|
|
||||||
| File Watcher | → enqueues Celery tasks
|
|
||||||
+------+-------+
|
|
||||||
|
|
|
||||||
v
|
|
||||||
+------+-------+
|
|
||||||
| Django | REST API & WebSocket (ASGI)
|
|
||||||
| Backend |
|
|
||||||
| - RTMP/Webcam|
|
|
||||||
| Adapters |
|
|
||||||
+------+-------+
|
|
||||||
| +--------------------------------+
|
|
||||||
| WebSocket | |
|
|
||||||
+---------->+ Frontend Nginx (SPA + proxy) |
|
|
||||||
| | – Serves Angular app on 80 |
|
|
||||||
| | – Proxies /api → Django |
|
|
||||||
v | – Proxies /ws → Django |
|
|
||||||
+------+-------+ | – Proxies /streaming → nginx-rtmp
|
|
||||||
| PostgreSQL | +--------------------------------+
|
|
||||||
+------+-------+
|
|
||||||
|
|
|
||||||
v
|
|
||||||
+------+-------+ +------------------------+
|
|
||||||
| Redis |<-----------+ Celery Workers |
|
|
||||||
| (broker) | tasks | – Worker queues: |
|
|
||||||
+------+-------+ | logo_detection, |
|
|
||||||
| | visual_analysis, |
|
|
||||||
| | default… |
|
|
||||||
+------------------→ | – Uses AnalysisEngine |
|
|
||||||
| with Execution |
|
|
||||||
| Strategies: |
|
|
||||||
| • local (in-worker) |
|
|
||||||
| • remote LAN (via |
|
|
||||||
| worker_host) |
|
|
||||||
| • cloud (via API) |
|
|
||||||
+------------------------+
|
|
||||||
+--------+
|
|
||||||
| Webcam | local device
|
|
||||||
+---+----+
|
|
||||||
|
|
|
||||||
| via Django’s Webcam
|
|
||||||
|SourceAdapter (ffmpeg)
|
|
||||||
v
|
|
||||||
[ Django Backend ]
|
|
||||||
|
|
|
||||||
v
|
|
||||||
+------+-------+
|
|
||||||
| Host “media/”|
|
|
||||||
+--------------+
|
|
||||||
|
|
||||||
Key points:
|
|
||||||
|
|
||||||
• OBS → nginx-rtmp → host “media/” → Django file-watcher → Celery tasks
|
|
||||||
• Local Webcam → Django backend (WebcamSourceAdapter with ffmpeg) → host “media/” → same pipeline
|
|
||||||
• Django Backend (REST + ASGI/WebSocket) ↔ Angular (served by Frontend Nginx)
|
|
||||||
• Celery Workers pick up tasks from Redis, run AnalysisEngine → can execute locally, offload to remote LAN workers, or call cloud APIs
|
|
||||||
• PostgreSQL stores streams, queue, and analysis results
|
|
||||||
|
|
||||||
This should give a clear bird’s-eye of how data and control flow through your streaming+AI stack.
|
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
|
|
||||||
> generate a diagram of the architecture to include in readme, just the diagram output in /def. ask question if needed, suggest options which technology to use to generate them I need a graphviz like
|
|
||||||
diagram of how services connect and some of design decisions on the code, like adapter for sources, and strategy for execution (local, lan, cloud), how the │
|
|
||||||
│ queue is organized in different analysis task, media storage, that could be media_root server by nginx or some cloud storage
|
|
||||||
|
|
||||||
● I'll create an architecture diagram showing the service connections and design patterns. Let me ask a few questions to make it accurate:
|
|
||||||
|
|
||||||
Questions:
|
|
||||||
1. Current media storage: Are you currently using local nginx-served files or cloud storage?
|
|
||||||
2. Queue organization: Do you have separate queues like logo_queue, visual_queue, etc., or task routing by queue names?
|
|
||||||
3. GCP services: Which specific GCP services are you integrating (Cloud Vision, Cloud Storage, others)?
|
|
||||||
|
|
||||||
Diagram tool options:
|
|
||||||
1. Graphviz/DOT - Clean, professional, version-controllable
|
|
||||||
2. PlantUML - Good for architectural diagrams, supports multiple output formats
|
|
||||||
3. Mermaid - GitHub-friendly, can render directly in markdown
|
|
||||||
4. Python + matplotlib/networkx - Programmatic, customizable
|
|
||||||
|
|
||||||
My recommendation: Graphviz/DOT because it:
|
|
||||||
- Creates clean, professional diagrams
|
|
||||||
- Is text-based (version control friendly)
|
|
||||||
- Excellent for showing service connections and data flow
|
|
||||||
- Industry standard for technical architecture
|
|
||||||
|
|
||||||
Should I proceed with Graphviz showing:
|
|
||||||
- Service containers (Django, Celery workers, Redis, PostgreSQL, nginx)
|
|
||||||
- Design patterns (Source Adapters, Execution Strategies)
|
|
||||||
- Queue segregation by analysis type
|
|
||||||
- Media storage options (local vs cloud)
|
|
||||||
- WebSocket connections for real-time updates
|
|
||||||
|
|
||||||
What's your preference for the tool and any specific details to include/exclude?
|
|
||||||
|
|
||||||
> even if not ready the idea is to include planed features, maybe use a different style or color to signal this, also maybe use something like svg so easily updetable for when they are done, 2. same thing
|
|
||||||
now is only logo detection, need default as base,3, GCS and Vision for now, extra.. about tool options can you give links for demos that are reprensetative of each to check the general feel. But I think
|
|
||||||
graphviz is the de facto, for super flexible and programable, not at all the usual WYSIWYG generic look which I'd like to avoid
|
|
||||||
|
|
||||||
● Perfect! Here are representative demos to see the visual style:
|
|
||||||
|
|
||||||
Tool Options with Demos:
|
|
||||||
|
|
||||||
1. Graphviz/DOT - https://graphviz.org/gallery/
|
|
||||||
- Clean, programmatic, no WYSIWYG look
|
|
||||||
- Example: https://graphviz.org/Gallery/directed/cluster.html
|
|
||||||
2. PlantUML - https://real-world-plantuml.com/
|
|
||||||
- Architecture: https://plantuml.com/c4-plantuml
|
|
||||||
3. Mermaid - https://mermaid.live/
|
|
||||||
- Architecture: https://mermaid.live/edit#pako:eNptkk1PwzAMhv-K5QtIq6vCx9ZlqiAmEEKIwwSInBqaOG2mxqlWM9Gf6nO6hB3dN0_8vI8f2ccEJqFABKNQzFklUMsK3CrRkrOQF2h8Lk2nPXRt67DpeQHe90QKFNYzb8e4qFXt-Rc
|
|
||||||
4. Python + networkx - https://networkx.org/documentation/stable/auto_examples/index.html
|
|
||||||
|
|
||||||
Graphviz is definitely the best choice for your needs - professional, flexible, no generic look, SVG output, version-controllable.
|
|
||||||
|
|
||||||
For the diagram, I'll include:
|
|
||||||
|
|
||||||
✅ Current (solid lines/colors):
|
|
||||||
- Django API, Celery logo workers, Redis, PostgreSQL
|
|
||||||
- Webcam/RTMP source adapters
|
|
||||||
- Local execution strategy
|
|
||||||
- GCS + Vision API integration
|
|
||||||
|
|
||||||
🔄 Planned (dashed lines/lighter colors):
|
|
||||||
- Additional analysis workers (visual, audio, text)
|
|
||||||
- LAN/Cloud execution strategies
|
|
||||||
- Advanced queue routing
|
|
||||||
- GPU effects pipeline
|
|
||||||
|
|
||||||
Media storage: I'll show both nginx-served local and GCS options with a switch.
|
|
||||||
|
|
||||||
Should I create the Graphviz DOT file now? It'll generate a clean SVG that you can easily update as features are implemented.
|
|
||||||
|
|
||||||
> yes if you need to generate many files, use a folder inside /def
|
|
||||||
|
|
||||||
● I'll create the architecture diagram using Graphviz. Let me create a folder and generate the files:
|
|
||||||
|
|
||||||
● Bash(mkdir -p /home/mariano/wdir/snk/def/architecture)
|
|
||||||
⎿ (No content)
|
|
||||||
|
|
||||||
● Write(~/wdir/snk/def/architecture/system_architecture.dot)
|
|
||||||
⎿ Wrote 175 lines to ../def/architecture/system_architecture.dot
|
|
||||||
digraph MediaAnalyzer {
|
|
||||||
// Graph settings
|
|
||||||
rankdir=TB;
|
|
||||||
bgcolor="white";
|
|
||||||
fontname="Arial";
|
|
||||||
fontsize=12;
|
|
||||||
// Node defaults
|
|
||||||
node [fontname="Arial", fontsize=10, shape=box, style=filled];
|
|
||||||
edge [fontname="Arial", fontsize=8];
|
|
||||||
… +165 lines (ctrl+r to expand)
|
|
||||||
|
|
||||||
● Bash(cd /home/mariano/wdir/snk/def/architecture && dot -Tsvg system_architecture.dot -o architecture_diagram.svg)
|
|
||||||
⎿ (No content)
|
|
||||||
|
|
||||||
● Write(README.md)
|
|
||||||
⎿ User rejected write to README.md
|
|
||||||
1 + # Architecture Diagram Generation
|
|
||||||
2 +
|
|
||||||
3 + ## Files
|
|
||||||
4 + - `system_architecture.dot` - Graphviz source file
|
|
||||||
5 + - `architecture_diagram.svg` - Generated SVG diagram
|
|
||||||
6 +
|
|
||||||
7 + ## To Update Diagram
|
|
||||||
8 +
|
|
||||||
9 + ```bash
|
|
||||||
10 + cd def/architecture
|
|
||||||
11 + dot -Tsvg system_architecture.dot -o architecture_diagram.svg
|
|
||||||
12 + ```
|
|
||||||
13 +
|
|
||||||
14 + ## Design Patterns Shown
|
|
||||||
15 +
|
|
||||||
16 + 1. **Source Adapters** - Abstract different video input sources
|
|
||||||
17 + 2. **Execution Strategies** - Local vs distributed processing
|
|
||||||
18 + 3. **Queue Segregation** - Separate workers for different analysis types
|
|
||||||
19 + 4. **Analysis Adapters** - Pluggable AI models
|
|
||||||
20 +
|
|
||||||
21 + ## Color Coding
|
|
||||||
22 +
|
|
||||||
23 + - **Green (✓)** - Currently implemented
|
|
||||||
24 + - **Yellow (○)** - Planned features
|
|
||||||
25 + - **Dotted lines** - Inheritance/abstraction
|
|
||||||
26 + - **Dashed lines** - Planned connections
|
|
||||||
27 +
|
|
||||||
28 + ## Update Process
|
|
||||||
29 +
|
|
||||||
30 + When implementing new features:
|
|
||||||
31 + 1. Change color from `#fff3cd` (yellow/planned) to `#d4edda` (green/implemented)
|
|
||||||
32 + 2. Change edge style from `dashed` to `solid`
|
|
||||||
33 + 3. Regenerate SVG
|
|
||||||
34 \ No newline at end of file
|
|
||||||
|
|
||||||
> dont step on the actual README !! in any case name it something like diagram md
|
|
||||||
|
|
||||||
● Write(diagram.md)
|
|
||||||
⎿ Wrote 33 lines to diagram.md
|
|
||||||
# Architecture Diagram Generation
|
|
||||||
## Files
|
|
||||||
- `system_architecture.dot` - Graphviz source file
|
|
||||||
- `architecture_diagram.svg` - Generated SVG diagram
|
|
||||||
## To Update Diagram
|
|
||||||
```bash
|
|
||||||
cd def/architecture
|
|
||||||
… +23 lines (ctrl+r to expand)
|
|
||||||
|
|
||||||
● Perfect! The architecture diagram is now generated at:
|
|
||||||
|
|
||||||
- Source: /def/architecture/system_architecture.dot
|
|
||||||
- Generated SVG: /def/architecture/architecture_diagram.svg
|
|
||||||
- Instructions: /def/architecture/diagram.md
|
|
||||||
|
|
||||||
The diagram shows:
|
|
||||||
✅ Current implementation (green, solid lines) - Logo detection with CLIP/GCP Vision🔄 Planned features (yellow, dashed lines) - Additional analysis types, LAN/Cloud strategies📐 Design patterns - Source
|
|
||||||
adapters, execution strategies, queue segregation
|
|
||||||
|
|
||||||
Now you can update the README to reference def/architecture/architecture_diagram.svg for the architecture diagram!
|
|
||||||
|
|
||||||
> can we make it less wide without making square smallers, so it looks better in the main github
|
|
||||||
page, I think that the workers could be aligned verticaly to make a clearer separation with the
|
|
||||||
"core", media and frontend, also a bit more contrast in the colors, not much, especialy grouping
|
|
||||||
background squares
|
|
||||||
|
|
||||||
● Update(system_architecture.dot)
|
|
||||||
⎿ Updated system_architecture.dot with 2 additions
|
|
||||||
4 bgcolor="white";
|
|
||||||
5 fontname="Arial";
|
|
||||||
6 fontsize=12;
|
|
||||||
7 + ratio=fill;
|
|
||||||
8 + size="12,16!";
|
|
||||||
9
|
|
||||||
10 // Node defaults
|
|
||||||
11 node [fontname="Arial", fontsize=10, shape=box, style=filled];
|
|
||||||
⎿ 5-hour limit reached ∙ resets 1pm
|
|
||||||
/upgrade to increase your usage limit.
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
# Architecture Diagram Generation
|
|
||||||
|
|
||||||
## Files
|
|
||||||
- `system_architecture.dot` - Graphviz source file
|
|
||||||
- `architecture_diagram.svg` - Generated SVG diagram
|
|
||||||
|
|
||||||
## To Update Diagram
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd def/architecture
|
|
||||||
dot -Tsvg system_architecture.dot -o architecture_diagram.svg
|
|
||||||
```
|
|
||||||
|
|
||||||
## Design Patterns Shown
|
|
||||||
|
|
||||||
1. **Source Adapters** - Abstract different video input sources
|
|
||||||
2. **Execution Strategies** - Local vs distributed processing
|
|
||||||
3. **Queue Segregation** - Separate workers for different analysis types
|
|
||||||
4. **Analysis Adapters** - Pluggable AI models
|
|
||||||
|
|
||||||
## Color Coding
|
|
||||||
|
|
||||||
- **Green (✓)** - Currently implemented
|
|
||||||
- **Yellow (○)** - Planned features
|
|
||||||
- **Dotted lines** - Inheritance/abstraction
|
|
||||||
- **Dashed lines** - Planned connections
|
|
||||||
|
|
||||||
## Update Process
|
|
||||||
|
|
||||||
When implementing new features:
|
|
||||||
1. Change color from `#fff3cd` (yellow/planned) to `#d4edda` (green/implemented)
|
|
||||||
2. Change edge style from `dashed` to `solid`
|
|
||||||
3. Regenerate SVG
|
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
digraph MediaAnalyzer {
|
|
||||||
// Graph settings
|
|
||||||
rankdir=TB;
|
|
||||||
bgcolor="white";
|
|
||||||
fontname="Arial";
|
|
||||||
fontsize=12;
|
|
||||||
ratio=fill;
|
|
||||||
size="27.5,30!";
|
|
||||||
ranksep=0.3;
|
|
||||||
nodesep=0.3;
|
|
||||||
|
|
||||||
// Node defaults
|
|
||||||
node [fontname="Arial", fontsize=10, shape=box, style=filled];
|
|
||||||
edge [fontname="Arial", fontsize=8];
|
|
||||||
|
|
||||||
// Color scheme
|
|
||||||
subgraph cluster_legend {
|
|
||||||
label="Legend";
|
|
||||||
style=filled;
|
|
||||||
color=lightgray;
|
|
||||||
node [shape=plaintext, style=filled];
|
|
||||||
|
|
||||||
implemented [label="✅ Implemented", fillcolor="#28a745", color="#ffffff", fontcolor="#ffffff"];
|
|
||||||
planned [label="⏳ Planned", fillcolor="#ffc107", color="#212529", fontcolor="#212529"];
|
|
||||||
code_pattern [label="📐 Code Pattern", fillcolor="#8e24aa", color="#ffffff", fontcolor="#ffffff"];
|
|
||||||
service_arch [label="⚡ Service/Architecture", fillcolor="#1976d2", color="#ffffff", fontcolor="#ffffff"];
|
|
||||||
|
|
||||||
implemented -> planned [style=invis];
|
|
||||||
code_pattern -> service_arch [style=invis];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Input Sources
|
|
||||||
subgraph cluster_sources {
|
|
||||||
label="Video Sources";
|
|
||||||
style=filled;
|
|
||||||
color="#1976d2";
|
|
||||||
|
|
||||||
webcam [label="Webcam", fillcolor="#28a745", fontcolor="#ffffff"];
|
|
||||||
rtmp [label="RTMP/OBS", fillcolor="#28a745", fontcolor="#ffffff"];
|
|
||||||
files [label="File Upload", fillcolor="#ffc107", fontcolor="#212529"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Source Adapters (Design Pattern)
|
|
||||||
subgraph cluster_adapters {
|
|
||||||
label="Source Adapters Pattern";
|
|
||||||
style=filled;
|
|
||||||
color="#8e24aa";
|
|
||||||
|
|
||||||
base_adapter [label="BaseSourceAdapter\n(Abstract)", fillcolor="#ce93d8"];
|
|
||||||
webcam_adapter [label="WebcamAdapter", fillcolor="#28a745", fontcolor="#ffffff"];
|
|
||||||
rtmp_adapter [label="RtmpAdapter", fillcolor="#28a745", fontcolor="#ffffff"];
|
|
||||||
file_adapter [label="FileAdapter", fillcolor="#ffc107", fontcolor="#212529"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Core Services
|
|
||||||
subgraph cluster_core {
|
|
||||||
label="Core Platform";
|
|
||||||
style=filled;
|
|
||||||
color="#1976d2";
|
|
||||||
|
|
||||||
django [label="Django API\n+ Channels\n:8000", fillcolor="#28a745", fontcolor="#ffffff"];
|
|
||||||
postgres [label="PostgreSQL\nDatabase", fillcolor="#28a745", fontcolor="#ffffff"];
|
|
||||||
redis [label="Redis\nCache & Broker", fillcolor="#28a745", fontcolor="#ffffff"];
|
|
||||||
nginx [label="NGINX\nReverse Proxy\n:80", fillcolor="#28a745", fontcolor="#ffffff"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execution Strategies (Design Pattern)
|
|
||||||
subgraph cluster_execution {
|
|
||||||
label="Execution Strategies Pattern";
|
|
||||||
style=filled;
|
|
||||||
color="#8e24aa";
|
|
||||||
|
|
||||||
base_strategy [label="BaseExecutionStrategy\n(Abstract)", fillcolor="#ce93d8"];
|
|
||||||
local_strategy [label="LocalStrategy", fillcolor="#28a745", fontcolor="#ffffff"];
|
|
||||||
lan_strategy [label="LANStrategy", fillcolor="#ffc107", fontcolor="#212529"];
|
|
||||||
cloud_strategy [label="CloudStrategy", fillcolor="#ffc107", fontcolor="#212529"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event Source Pattern (NEW)
|
|
||||||
subgraph cluster_event_sources {
|
|
||||||
label="Event Source Pattern";
|
|
||||||
style=filled;
|
|
||||||
color="#8e24aa";
|
|
||||||
|
|
||||||
base_event_source [label="SegmentEventSource\n(Abstract)", fillcolor="#ce93d8"];
|
|
||||||
file_watcher_source [label="FileWatcherEventSource\n(Local/LAN)", fillcolor="#28a745", fontcolor="#ffffff"];
|
|
||||||
cloud_storage_source [label="CloudStorageEventSource\n(GCS Pub/Sub)", fillcolor="#ffc107", fontcolor="#212529"];
|
|
||||||
webhook_source [label="WebhookEventSource\n(External)", fillcolor="#ffc107", fontcolor="#212529"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event-Driven Processing Pipeline
|
|
||||||
subgraph cluster_events {
|
|
||||||
label="Event-Driven Processing";
|
|
||||||
style=filled;
|
|
||||||
color="#1976d2";
|
|
||||||
|
|
||||||
event_source_manager [label="EventSourceManager\n(Environment-based)", fillcolor="#28a745", fontcolor="#ffffff"];
|
|
||||||
redis_events [label="Redis Event Queue\n(segment_events)", fillcolor="#28a745", fontcolor="#ffffff"];
|
|
||||||
event_processor [label="Event Processor\n(triggers analysis)", fillcolor="#28a745", fontcolor="#ffffff"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Analysis Workers
|
|
||||||
subgraph cluster_workers {
|
|
||||||
label="Celery Workers";
|
|
||||||
style=filled;
|
|
||||||
color="#1976d2";
|
|
||||||
|
|
||||||
logo_worker [label="Logo Detection\nWorker\n(logo_queue)", fillcolor="#28a745", fontcolor="#ffffff"];
|
|
||||||
visual_worker [label="Visual Properties\nWorker\n(visual_queue)", fillcolor="#ffc107", fontcolor="#212529"];
|
|
||||||
audio_worker [label="Audio Transcript\nWorker\n(audio_queue)", fillcolor="#ffc107", fontcolor="#212529"];
|
|
||||||
text_worker [label="Text Recognition\nWorker\n(text_queue)", fillcolor="#ffc107", fontcolor="#212529"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// AI Adapters (Design Pattern)
|
|
||||||
subgraph cluster_ai_adapters {
|
|
||||||
label="Analysis Adapters Pattern";
|
|
||||||
style=filled;
|
|
||||||
color="#8e24aa";
|
|
||||||
|
|
||||||
base_ai [label="DetectionAdapter\n(Abstract)", fillcolor="#ce93d8"];
|
|
||||||
clip_adapter [label="CLIPAdapter\n(Local)", fillcolor="#28a745", fontcolor="#ffffff"];
|
|
||||||
gcp_vision [label="GCPVisionAdapter\n(Cloud)", fillcolor="#ffc107", fontcolor="#212529"];
|
|
||||||
yolo_adapter [label="YOLOAdapter\n(Planned)", fillcolor="#ffc107", fontcolor="#212529"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Storage Options
|
|
||||||
subgraph cluster_storage {
|
|
||||||
label="Media Storage";
|
|
||||||
style=filled;
|
|
||||||
color="#1976d2";
|
|
||||||
|
|
||||||
local_storage [label="Local Files\n(nginx-served)", fillcolor="#28a745", fontcolor="#ffffff"];
|
|
||||||
gcs_storage [label="Google Cloud\nStorage", fillcolor="#ffc107", fontcolor="#212529"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Frontend
|
|
||||||
subgraph cluster_frontend {
|
|
||||||
label="Frontend";
|
|
||||||
style=filled;
|
|
||||||
color="#1976d2";
|
|
||||||
|
|
||||||
angular [label="Angular 17 SPA\n+ WebSocket\n:4200", fillcolor="#28a745", fontcolor="#ffffff"];
|
|
||||||
hls_player [label="HLS.js Player\n+ Canvas Overlays", fillcolor="#28a745", fontcolor="#ffffff"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cloud Services
|
|
||||||
subgraph cluster_cloud {
|
|
||||||
label="GCP Services";
|
|
||||||
style=filled;
|
|
||||||
color="#1976d2";
|
|
||||||
|
|
||||||
vision_api [label="Cloud Vision API\n(Logo Detection)", fillcolor="#ffc107", fontcolor="#212529"];
|
|
||||||
speech_api [label="Speech-to-Text API\n(Audio Transcript)", fillcolor="#ffc107", fontcolor="#212529"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connections - Current Implementation (solid)
|
|
||||||
webcam -> webcam_adapter [color="#2e7d32"];
|
|
||||||
rtmp -> rtmp_adapter [color="#2e7d32"];
|
|
||||||
|
|
||||||
webcam_adapter -> django [color="#2e7d32"];
|
|
||||||
rtmp_adapter -> django [color="#2e7d32"];
|
|
||||||
|
|
||||||
django -> postgres [color="#2e7d32"];
|
|
||||||
django -> redis [color="#2e7d32"];
|
|
||||||
django -> local_storage [color="#2e7d32"];
|
|
||||||
django -> gcs_storage [color="#2e7d32"];
|
|
||||||
|
|
||||||
// Event source pattern connections
|
|
||||||
local_storage -> file_watcher_source [label="monitors\nHLS segments", color="#2e7d32"];
|
|
||||||
gcs_storage -> cloud_storage_source [label="storage\nevents", color="#2e7d32"];
|
|
||||||
file_watcher_source -> event_source_manager [color="#2e7d32"];
|
|
||||||
cloud_storage_source -> event_source_manager [color="#2e7d32"];
|
|
||||||
webhook_source -> event_source_manager [color="#2e7d32"];
|
|
||||||
|
|
||||||
// Event-driven processing flow
|
|
||||||
event_source_manager -> redis_events [label="publishes\nevents", color="#2e7d32"];
|
|
||||||
redis_events -> event_processor [label="consumes\nevents", color="#2e7d32"];
|
|
||||||
event_processor -> logo_worker [label="triggers\nanalysis", color="#2e7d32"];
|
|
||||||
|
|
||||||
redis -> logo_worker [color="#2e7d32"];
|
|
||||||
logo_worker -> local_strategy [color="#2e7d32"];
|
|
||||||
local_strategy -> clip_adapter [color="#2e7d32"];
|
|
||||||
local_strategy -> gcp_vision [color="#2e7d32"];
|
|
||||||
gcp_vision -> vision_api [color="#2e7d32"];
|
|
||||||
|
|
||||||
// WebSocket real-time updates
|
|
||||||
logo_worker -> django [label="analysis\nresults", color="#2e7d32"];
|
|
||||||
django -> angular [label="WebSocket\nAPI", color="#2e7d32"];
|
|
||||||
angular -> hls_player [color="#2e7d32"];
|
|
||||||
nginx -> angular [color="#2e7d32"];
|
|
||||||
|
|
||||||
// Planned Connections (dashed)
|
|
||||||
files -> file_adapter [color="#f57f17", style=dashed];
|
|
||||||
file_adapter -> django [color="#f57f17", style=dashed];
|
|
||||||
|
|
||||||
redis -> visual_worker [color="#f57f17", style=dashed];
|
|
||||||
redis -> audio_worker [color="#f57f17", style=dashed];
|
|
||||||
redis -> text_worker [color="#f57f17", style=dashed];
|
|
||||||
|
|
||||||
visual_worker -> lan_strategy [color="#f57f17", style=dashed];
|
|
||||||
audio_worker -> cloud_strategy [color="#f57f17", style=dashed];
|
|
||||||
text_worker -> cloud_strategy [color="#f57f17", style=dashed];
|
|
||||||
|
|
||||||
cloud_strategy -> speech_api [color="#f57f17", style=dashed];
|
|
||||||
lan_strategy -> yolo_adapter [color="#f57f17", style=dashed];
|
|
||||||
|
|
||||||
// Inheritance relationships (dotted)
|
|
||||||
base_adapter -> webcam_adapter [style=dotted, color=gray];
|
|
||||||
base_adapter -> rtmp_adapter [style=dotted, color=gray];
|
|
||||||
base_adapter -> file_adapter [style=dotted, color=gray];
|
|
||||||
|
|
||||||
base_strategy -> local_strategy [style=dotted, color=gray];
|
|
||||||
base_strategy -> lan_strategy [style=dotted, color=gray];
|
|
||||||
base_strategy -> cloud_strategy [style=dotted, color=gray];
|
|
||||||
|
|
||||||
base_ai -> clip_adapter [style=dotted, color=gray];
|
|
||||||
base_ai -> gcp_vision [style=dotted, color=gray];
|
|
||||||
base_ai -> yolo_adapter [style=dotted, color=gray];
|
|
||||||
|
|
||||||
// Event source inheritance (NEW)
|
|
||||||
base_event_source -> file_watcher_source [style=dotted, color=gray];
|
|
||||||
base_event_source -> cloud_storage_source [style=dotted, color=gray];
|
|
||||||
base_event_source -> webhook_source [style=dotted, color=gray];
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
digraph SystemOverview {
|
|
||||||
// Graph settings
|
|
||||||
rankdir=TB;
|
|
||||||
compound=true;
|
|
||||||
fontname="Helvetica";
|
|
||||||
node [fontname="Helvetica", fontsize=11];
|
|
||||||
edge [fontname="Helvetica", fontsize=10];
|
|
||||||
|
|
||||||
// Title
|
|
||||||
labelloc="t";
|
|
||||||
label="System Monitoring Platform - Architecture Overview";
|
|
||||||
fontsize=16;
|
|
||||||
|
|
||||||
// Styling
|
|
||||||
node [shape=box, style="rounded,filled"];
|
|
||||||
|
|
||||||
// External
|
|
||||||
subgraph cluster_external {
|
|
||||||
label="External";
|
|
||||||
style=dashed;
|
|
||||||
color=gray;
|
|
||||||
|
|
||||||
browser [label="Browser\n(Dashboard)", fillcolor="#E3F2FD"];
|
|
||||||
machines [label="Monitored\nMachines", fillcolor="#FFF3E0", shape=box3d];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Core Services
|
|
||||||
subgraph cluster_services {
|
|
||||||
label="Application Services";
|
|
||||||
style=filled;
|
|
||||||
color="#E8F5E9";
|
|
||||||
fillcolor="#E8F5E9";
|
|
||||||
|
|
||||||
gateway [label="Gateway\n(FastAPI)", fillcolor="#C8E6C9"];
|
|
||||||
aggregator [label="Aggregator\n(gRPC Server)", fillcolor="#C8E6C9"];
|
|
||||||
alerts [label="Alerts\nService", fillcolor="#C8E6C9"];
|
|
||||||
collector [label="Collector\n(gRPC Client)", fillcolor="#DCEDC8"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Data Layer
|
|
||||||
subgraph cluster_data {
|
|
||||||
label="Data Layer";
|
|
||||||
style=filled;
|
|
||||||
color="#FFF8E1";
|
|
||||||
fillcolor="#FFF8E1";
|
|
||||||
|
|
||||||
redis [label="Redis\n(Pub/Sub + State)", fillcolor="#FFECB3", shape=cylinder];
|
|
||||||
timescale [label="TimescaleDB\n(Time-series)", fillcolor="#FFECB3", shape=cylinder];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event Stream
|
|
||||||
subgraph cluster_events {
|
|
||||||
label="Event Stream";
|
|
||||||
style=filled;
|
|
||||||
color="#F3E5F5";
|
|
||||||
fillcolor="#F3E5F5";
|
|
||||||
|
|
||||||
events [label="Redis Pub/Sub\n(Events)", fillcolor="#E1BEE7", shape=hexagon];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connections
|
|
||||||
browser -> gateway [label="WebSocket\nREST", color="#1976D2"];
|
|
||||||
gateway -> aggregator [label="gRPC", color="#388E3C"];
|
|
||||||
gateway -> redis [label="State\nQuery", style=dashed];
|
|
||||||
gateway -> timescale [label="Historical\nQuery", style=dashed];
|
|
||||||
|
|
||||||
machines -> collector [label="psutil", color="#F57C00", style=dotted];
|
|
||||||
collector -> aggregator [label="gRPC\nStream", color="#388E3C"];
|
|
||||||
|
|
||||||
aggregator -> redis [label="Current\nState", color="#FFA000"];
|
|
||||||
aggregator -> timescale [label="Store\nMetrics", color="#FFA000"];
|
|
||||||
aggregator -> events [label="Publish", color="#7B1FA2"];
|
|
||||||
|
|
||||||
events -> alerts [label="Subscribe", color="#7B1FA2"];
|
|
||||||
events -> gateway [label="Subscribe", color="#7B1FA2"];
|
|
||||||
|
|
||||||
alerts -> timescale [label="Store\nAlerts", style=dashed];
|
|
||||||
}
|
|
||||||
@@ -1,193 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
|
||||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<!-- Generated by graphviz version 14.1.1 (0)
|
|
||||||
-->
|
|
||||||
<!-- Title: SystemOverview Pages: 1 -->
|
|
||||||
<svg width="444pt" height="508pt"
|
|
||||||
viewBox="0.00 0.00 444.00 508.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
||||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 503.78)">
|
|
||||||
<title>SystemOverview</title>
|
|
||||||
<polygon fill="white" stroke="none" points="-4,4 -4,-503.78 440,-503.78 440,4 -4,4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="218" y="-480.58" font-family="Helvetica,sans-Serif" font-size="16.00">System Monitoring Platform - Architecture Overview</text>
|
|
||||||
<g id="clust1" class="cluster">
|
|
||||||
<title>cluster_external</title>
|
|
||||||
<polygon fill="none" stroke="gray" stroke-dasharray="5,2" points="45.5,-374.2 45.5,-453.7 235.5,-453.7 235.5,-374.2 45.5,-374.2"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="140.5" y="-434.5" font-family="Helvetica,sans-Serif" font-size="16.00">External</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust2" class="cluster">
|
|
||||||
<title>cluster_services</title>
|
|
||||||
<polygon fill="#e8f5e9" stroke="#e8f5e9" points="101.5,-143.12 101.5,-320.12 363.5,-320.12 363.5,-143.12 101.5,-143.12"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="232.5" y="-300.93" font-family="Helvetica,sans-Serif" font-size="16.00">Application Services</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust3" class="cluster">
|
|
||||||
<title>cluster_data</title>
|
|
||||||
<polygon fill="#fff8e1" stroke="#fff8e1" points="22.5,-8 22.5,-99.62 260.5,-99.62 260.5,-8 22.5,-8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="141.5" y="-80.42" font-family="Helvetica,sans-Serif" font-size="16.00">Data Layer</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust4" class="cluster">
|
|
||||||
<title>cluster_events</title>
|
|
||||||
<polygon fill="#f3e5f5" stroke="#f3e5f5" points="243.5,-363.62 243.5,-464.28 413.5,-464.28 413.5,-363.62 243.5,-363.62"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="328.5" y="-445.08" font-family="Helvetica,sans-Serif" font-size="16.00">Event Stream</text>
|
|
||||||
</g>
|
|
||||||
<!-- browser -->
|
|
||||||
<g id="node1" class="node">
|
|
||||||
<title>browser</title>
|
|
||||||
<path fill="#e3f2fd" stroke="black" d="M125.62,-418.2C125.62,-418.2 65.38,-418.2 65.38,-418.2 59.38,-418.2 53.38,-412.2 53.38,-406.2 53.38,-406.2 53.38,-394.2 53.38,-394.2 53.38,-388.2 59.38,-382.2 65.38,-382.2 65.38,-382.2 125.62,-382.2 125.62,-382.2 131.62,-382.2 137.62,-388.2 137.62,-394.2 137.62,-394.2 137.62,-406.2 137.62,-406.2 137.62,-412.2 131.62,-418.2 125.62,-418.2"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="95.5" y="-403.25" font-family="Helvetica,sans-Serif" font-size="11.00">Browser</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="95.5" y="-389.75" font-family="Helvetica,sans-Serif" font-size="11.00">(Dashboard)</text>
|
|
||||||
</g>
|
|
||||||
<!-- gateway -->
|
|
||||||
<g id="node3" class="node">
|
|
||||||
<title>gateway</title>
|
|
||||||
<path fill="#c8e6c9" stroke="black" d="M161.88,-284.62C161.88,-284.62 121.12,-284.62 121.12,-284.62 115.12,-284.62 109.12,-278.62 109.12,-272.62 109.12,-272.62 109.12,-260.62 109.12,-260.62 109.12,-254.62 115.12,-248.62 121.12,-248.62 121.12,-248.62 161.88,-248.62 161.88,-248.62 167.88,-248.62 173.88,-254.62 173.88,-260.62 173.88,-260.62 173.88,-272.62 173.88,-272.62 173.88,-278.62 167.88,-284.62 161.88,-284.62"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="141.5" y="-269.68" font-family="Helvetica,sans-Serif" font-size="11.00">Gateway</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="141.5" y="-256.18" font-family="Helvetica,sans-Serif" font-size="11.00">(FastAPI)</text>
|
|
||||||
</g>
|
|
||||||
<!-- browser->gateway -->
|
|
||||||
<g id="edge1" class="edge">
|
|
||||||
<title>browser->gateway</title>
|
|
||||||
<path fill="none" stroke="#1976d2" d="M92.73,-381.75C91.08,-367.05 90.32,-345.66 96.25,-328.12 100.5,-315.57 108.45,-303.5 116.51,-293.49"/>
|
|
||||||
<polygon fill="#1976d2" stroke="#1976d2" points="119.02,-295.94 122.86,-286.06 113.7,-291.39 119.02,-295.94"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="122.88" y="-344.12" font-family="Helvetica,sans-Serif" font-size="10.00">WebSocket</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="122.88" y="-331.38" font-family="Helvetica,sans-Serif" font-size="10.00">REST</text>
|
|
||||||
</g>
|
|
||||||
<!-- machines -->
|
|
||||||
<g id="node2" class="node">
|
|
||||||
<title>machines</title>
|
|
||||||
<polygon fill="#fff3e0" stroke="black" points="227.25,-418.2 159.75,-418.2 155.75,-414.2 155.75,-382.2 223.25,-382.2 227.25,-386.2 227.25,-418.2"/>
|
|
||||||
<polyline fill="none" stroke="black" points="223.25,-414.2 155.75,-414.2"/>
|
|
||||||
<polyline fill="none" stroke="black" points="223.25,-414.2 223.25,-382.2"/>
|
|
||||||
<polyline fill="none" stroke="black" points="223.25,-414.2 227.25,-418.2"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="191.5" y="-403.25" font-family="Helvetica,sans-Serif" font-size="11.00">Monitored</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="191.5" y="-389.75" font-family="Helvetica,sans-Serif" font-size="11.00">Machines</text>
|
|
||||||
</g>
|
|
||||||
<!-- collector -->
|
|
||||||
<g id="node6" class="node">
|
|
||||||
<title>collector</title>
|
|
||||||
<path fill="#dcedc8" stroke="black" d="M343.88,-284.62C343.88,-284.62 279.12,-284.62 279.12,-284.62 273.12,-284.62 267.12,-278.62 267.12,-272.62 267.12,-272.62 267.12,-260.62 267.12,-260.62 267.12,-254.62 273.12,-248.62 279.12,-248.62 279.12,-248.62 343.88,-248.62 343.88,-248.62 349.88,-248.62 355.88,-254.62 355.88,-260.62 355.88,-260.62 355.88,-272.62 355.88,-272.62 355.88,-278.62 349.88,-284.62 343.88,-284.62"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="311.5" y="-269.68" font-family="Helvetica,sans-Serif" font-size="11.00">Collector</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="311.5" y="-256.18" font-family="Helvetica,sans-Serif" font-size="11.00">(gRPC Client)</text>
|
|
||||||
</g>
|
|
||||||
<!-- machines->collector -->
|
|
||||||
<g id="edge5" class="edge">
|
|
||||||
<title>machines->collector</title>
|
|
||||||
<path fill="none" stroke="#f57c00" stroke-dasharray="1,5" d="M210.81,-381.83C219.12,-375.21 229.26,-368.17 239.5,-363.62 260.21,-354.43 273.06,-369.22 289.5,-353.62 304.98,-338.94 310.15,-314.98 311.64,-296.08"/>
|
|
||||||
<polygon fill="#f57c00" stroke="#f57c00" points="315.12,-296.47 312.08,-286.32 308.13,-296.15 315.12,-296.47"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="318.1" y="-337.75" font-family="Helvetica,sans-Serif" font-size="10.00">psutil</text>
|
|
||||||
</g>
|
|
||||||
<!-- aggregator -->
|
|
||||||
<g id="node4" class="node">
|
|
||||||
<title>aggregator</title>
|
|
||||||
<path fill="#c8e6c9" stroke="black" d="M343.12,-187.12C343.12,-187.12 273.88,-187.12 273.88,-187.12 267.88,-187.12 261.88,-181.12 261.88,-175.12 261.88,-175.12 261.88,-163.12 261.88,-163.12 261.88,-157.12 267.88,-151.12 273.88,-151.12 273.88,-151.12 343.12,-151.12 343.12,-151.12 349.12,-151.12 355.12,-157.12 355.12,-163.12 355.12,-163.12 355.12,-175.12 355.12,-175.12 355.12,-181.12 349.12,-187.12 343.12,-187.12"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="308.5" y="-172.18" font-family="Helvetica,sans-Serif" font-size="11.00">Aggregator</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="308.5" y="-158.68" font-family="Helvetica,sans-Serif" font-size="11.00">(gRPC Server)</text>
|
|
||||||
</g>
|
|
||||||
<!-- gateway->aggregator -->
|
|
||||||
<g id="edge2" class="edge">
|
|
||||||
<title>gateway->aggregator</title>
|
|
||||||
<path fill="none" stroke="#388e3c" d="M171.74,-248.33C198.77,-232.88 238.56,-210.12 268.26,-193.13"/>
|
|
||||||
<polygon fill="#388e3c" stroke="#388e3c" points="269.66,-196.37 276.6,-188.36 266.19,-190.29 269.66,-196.37"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="257.62" y="-214.75" font-family="Helvetica,sans-Serif" font-size="10.00">gRPC</text>
|
|
||||||
</g>
|
|
||||||
<!-- redis -->
|
|
||||||
<g id="node7" class="node">
|
|
||||||
<title>redis</title>
|
|
||||||
<path fill="#ffecb3" stroke="black" d="M146,-59.75C146,-62.16 120.23,-64.12 88.5,-64.12 56.77,-64.12 31,-62.16 31,-59.75 31,-59.75 31,-20.38 31,-20.38 31,-17.96 56.77,-16 88.5,-16 120.23,-16 146,-17.96 146,-20.38 146,-20.38 146,-59.75 146,-59.75"/>
|
|
||||||
<path fill="none" stroke="black" d="M146,-59.75C146,-57.34 120.23,-55.38 88.5,-55.38 56.77,-55.38 31,-57.34 31,-59.75"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="88.5" y="-43.11" font-family="Helvetica,sans-Serif" font-size="11.00">Redis</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="88.5" y="-29.61" font-family="Helvetica,sans-Serif" font-size="11.00">(Pub/Sub + State)</text>
|
|
||||||
</g>
|
|
||||||
<!-- gateway->redis -->
|
|
||||||
<g id="edge3" class="edge">
|
|
||||||
<title>gateway->redis</title>
|
|
||||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M122.74,-248.35C108.28,-233.68 89.42,-211.2 81.25,-187.12 68.86,-150.62 73.72,-106.03 79.72,-75.79"/>
|
|
||||||
<polygon fill="black" stroke="black" points="83.14,-76.56 81.82,-66.04 76.29,-75.08 83.14,-76.56"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="95.88" y="-172.38" font-family="Helvetica,sans-Serif" font-size="10.00">State</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="95.88" y="-159.62" font-family="Helvetica,sans-Serif" font-size="10.00">Query</text>
|
|
||||||
</g>
|
|
||||||
<!-- timescale -->
|
|
||||||
<g id="node8" class="node">
|
|
||||||
<title>timescale</title>
|
|
||||||
<path fill="#ffecb3" stroke="black" d="M252.88,-59.75C252.88,-62.16 232.99,-64.12 208.5,-64.12 184.01,-64.12 164.12,-62.16 164.12,-59.75 164.12,-59.75 164.12,-20.38 164.12,-20.38 164.12,-17.96 184.01,-16 208.5,-16 232.99,-16 252.88,-17.96 252.88,-20.38 252.88,-20.38 252.88,-59.75 252.88,-59.75"/>
|
|
||||||
<path fill="none" stroke="black" d="M252.88,-59.75C252.88,-57.34 232.99,-55.38 208.5,-55.38 184.01,-55.38 164.12,-57.34 164.12,-59.75"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="208.5" y="-43.11" font-family="Helvetica,sans-Serif" font-size="11.00">TimescaleDB</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="208.5" y="-29.61" font-family="Helvetica,sans-Serif" font-size="11.00">(Time-series)</text>
|
|
||||||
</g>
|
|
||||||
<!-- gateway->timescale -->
|
|
||||||
<g id="edge4" class="edge">
|
|
||||||
<title>gateway->timescale</title>
|
|
||||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M143.41,-248.29C146.34,-224.28 152.82,-179.73 164,-143.12 171.19,-119.57 182.25,-94.18 191.54,-74.62"/>
|
|
||||||
<polygon fill="black" stroke="black" points="194.62,-76.29 195.83,-65.76 188.32,-73.24 194.62,-76.29"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="187.25" y="-172.38" font-family="Helvetica,sans-Serif" font-size="10.00">Historical</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="187.25" y="-159.62" font-family="Helvetica,sans-Serif" font-size="10.00">Query</text>
|
|
||||||
</g>
|
|
||||||
<!-- aggregator->redis -->
|
|
||||||
<g id="edge7" class="edge">
|
|
||||||
<title>aggregator->redis</title>
|
|
||||||
<path fill="none" stroke="#ffa000" d="M267.27,-150.69C261,-148.11 254.59,-145.52 248.5,-143.12 236.59,-138.44 233.22,-138.25 221.5,-133.12 191.36,-119.95 182.76,-118.04 155.5,-99.62 143.6,-91.59 131.5,-81.66 120.93,-72.28"/>
|
|
||||||
<polygon fill="#ffa000" stroke="#ffa000" points="123.32,-69.73 113.56,-65.6 118.62,-74.91 123.32,-69.73"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="239.5" y="-123.62" font-family="Helvetica,sans-Serif" font-size="10.00">Current</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="239.5" y="-110.88" font-family="Helvetica,sans-Serif" font-size="10.00">State</text>
|
|
||||||
</g>
|
|
||||||
<!-- aggregator->timescale -->
|
|
||||||
<g id="edge8" class="edge">
|
|
||||||
<title>aggregator->timescale</title>
|
|
||||||
<path fill="none" stroke="#ffa000" d="M294.81,-150.72C279.15,-130.84 253.2,-97.86 233.84,-73.25"/>
|
|
||||||
<polygon fill="#ffa000" stroke="#ffa000" points="236.64,-71.16 227.71,-65.47 231.14,-75.49 236.64,-71.16"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="296.95" y="-123.62" font-family="Helvetica,sans-Serif" font-size="10.00">Store</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="296.95" y="-110.88" font-family="Helvetica,sans-Serif" font-size="10.00">Metrics</text>
|
|
||||||
</g>
|
|
||||||
<!-- events -->
|
|
||||||
<g id="node9" class="node">
|
|
||||||
<title>events</title>
|
|
||||||
<path fill="#e1bee7" stroke="black" d="M395.63,-407.37C395.63,-407.37 376.5,-421.61 376.5,-421.61 371.69,-425.2 360.88,-428.78 354.88,-428.78 354.88,-428.78 302.12,-428.78 302.12,-428.78 296.12,-428.78 285.31,-425.2 280.5,-421.61 280.5,-421.61 261.37,-407.37 261.37,-407.37 256.56,-403.79 256.56,-396.62 261.37,-393.04 261.37,-393.04 280.5,-378.79 280.5,-378.79 285.31,-375.21 296.12,-371.62 302.12,-371.62 302.12,-371.62 354.88,-371.62 354.88,-371.62 360.88,-371.62 371.69,-375.21 376.5,-378.79 376.5,-378.79 395.63,-393.04 395.63,-393.04 400.44,-396.62 400.44,-403.79 395.63,-407.37"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="328.5" y="-403.25" font-family="Helvetica,sans-Serif" font-size="11.00">Redis Pub/Sub</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="328.5" y="-389.75" font-family="Helvetica,sans-Serif" font-size="11.00">(Events)</text>
|
|
||||||
</g>
|
|
||||||
<!-- aggregator->events -->
|
|
||||||
<g id="edge9" class="edge">
|
|
||||||
<title>aggregator->events</title>
|
|
||||||
<path fill="none" stroke="#7b1fa2" d="M333.16,-187.49C339.14,-192.63 345.07,-198.63 349.5,-205.12 361.02,-222.03 361.12,-228.46 364.5,-248.62 369.75,-279.97 371.24,-289.07 364.5,-320.12 361.48,-334.06 355.78,-348.49 349.79,-361.14"/>
|
|
||||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="346.73,-359.44 345.42,-369.95 353,-362.55 346.73,-359.44"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="386.64" y="-263.5" font-family="Helvetica,sans-Serif" font-size="10.00">Publish</text>
|
|
||||||
</g>
|
|
||||||
<!-- alerts -->
|
|
||||||
<g id="node5" class="node">
|
|
||||||
<title>alerts</title>
|
|
||||||
<path fill="#c8e6c9" stroke="black" d="M236.75,-284.62C236.75,-284.62 204.25,-284.62 204.25,-284.62 198.25,-284.62 192.25,-278.62 192.25,-272.62 192.25,-272.62 192.25,-260.62 192.25,-260.62 192.25,-254.62 198.25,-248.62 204.25,-248.62 204.25,-248.62 236.75,-248.62 236.75,-248.62 242.75,-248.62 248.75,-254.62 248.75,-260.62 248.75,-260.62 248.75,-272.62 248.75,-272.62 248.75,-278.62 242.75,-284.62 236.75,-284.62"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="220.5" y="-269.68" font-family="Helvetica,sans-Serif" font-size="11.00">Alerts</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="220.5" y="-256.18" font-family="Helvetica,sans-Serif" font-size="11.00">Service</text>
|
|
||||||
</g>
|
|
||||||
<!-- alerts->timescale -->
|
|
||||||
<g id="edge12" class="edge">
|
|
||||||
<title>alerts->timescale</title>
|
|
||||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M219.58,-248.38C217.61,-211.47 212.94,-124.24 210.34,-75.51"/>
|
|
||||||
<polygon fill="black" stroke="black" points="213.85,-75.6 209.82,-65.8 206.86,-75.97 213.85,-75.6"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="230.53" y="-172.38" font-family="Helvetica,sans-Serif" font-size="10.00">Store</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="230.53" y="-159.62" font-family="Helvetica,sans-Serif" font-size="10.00">Alerts</text>
|
|
||||||
</g>
|
|
||||||
<!-- collector->aggregator -->
|
|
||||||
<g id="edge6" class="edge">
|
|
||||||
<title>collector->aggregator</title>
|
|
||||||
<path fill="none" stroke="#388e3c" d="M310.96,-248.55C310.53,-234.65 309.9,-214.73 309.39,-198.45"/>
|
|
||||||
<polygon fill="#388e3c" stroke="#388e3c" points="312.9,-198.77 309.09,-188.89 305.91,-198.99 312.9,-198.77"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="327.98" y="-221.12" font-family="Helvetica,sans-Serif" font-size="10.00">gRPC</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="327.98" y="-208.38" font-family="Helvetica,sans-Serif" font-size="10.00">Stream</text>
|
|
||||||
</g>
|
|
||||||
<!-- events->gateway -->
|
|
||||||
<g id="edge11" class="edge">
|
|
||||||
<title>events->gateway</title>
|
|
||||||
<path fill="none" stroke="#7b1fa2" d="M281.13,-378.02C267.86,-372.71 253.29,-367.44 239.5,-363.62 212.49,-356.16 199.25,-370.98 177.25,-353.62 159.49,-339.61 150.46,-315.21 145.93,-295.98"/>
|
|
||||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="149.38,-295.39 143.95,-286.29 142.52,-296.79 149.38,-295.39"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="200.88" y="-337.75" font-family="Helvetica,sans-Serif" font-size="10.00">Subscribe</text>
|
|
||||||
</g>
|
|
||||||
<!-- events->alerts -->
|
|
||||||
<g id="edge10" class="edge">
|
|
||||||
<title>events->alerts</title>
|
|
||||||
<path fill="none" stroke="#7b1fa2" d="M277.27,-380.98C264.23,-374.18 251.36,-365.21 242.25,-353.62 229.43,-337.32 224.08,-314.36 221.89,-296.26"/>
|
|
||||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="225.38,-296.07 220.98,-286.43 218.41,-296.71 225.38,-296.07"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="265.88" y="-337.75" font-family="Helvetica,sans-Serif" font-size="10.00">Subscribe</text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 16 KiB |
@@ -1,83 +0,0 @@
|
|||||||
digraph DataFlow {
|
|
||||||
rankdir=LR;
|
|
||||||
compound=true;
|
|
||||||
fontname="Helvetica";
|
|
||||||
node [fontname="Helvetica", fontsize=10];
|
|
||||||
edge [fontname="Helvetica", fontsize=9];
|
|
||||||
|
|
||||||
labelloc="t";
|
|
||||||
label="Metrics Data Flow Pipeline";
|
|
||||||
fontsize=14;
|
|
||||||
|
|
||||||
node [shape=box, style="rounded,filled"];
|
|
||||||
|
|
||||||
// Collection
|
|
||||||
subgraph cluster_collect {
|
|
||||||
label="Collection (5s)";
|
|
||||||
style=filled;
|
|
||||||
fillcolor="#E3F2FD";
|
|
||||||
|
|
||||||
psutil [label="psutil\n(CPU, Mem, Disk)", shape=component, fillcolor="#BBDEFB"];
|
|
||||||
collector [label="Collector\nService", fillcolor="#90CAF9"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ingestion
|
|
||||||
subgraph cluster_ingest {
|
|
||||||
label="Ingestion";
|
|
||||||
style=filled;
|
|
||||||
fillcolor="#E8F5E9";
|
|
||||||
|
|
||||||
aggregator [label="Aggregator\n(gRPC)", fillcolor="#A5D6A7"];
|
|
||||||
validate [label="Validate &\nNormalize", shape=diamond, fillcolor="#C8E6C9"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Storage Hot
|
|
||||||
subgraph cluster_hot {
|
|
||||||
label="Hot Path (Real-time)";
|
|
||||||
style=filled;
|
|
||||||
fillcolor="#FFF3E0";
|
|
||||||
|
|
||||||
redis_state [label="Redis\nCurrent State", shape=cylinder, fillcolor="#FFCC80"];
|
|
||||||
redis_pubsub [label="Redis\nPub/Sub", shape=hexagon, fillcolor="#FFB74D"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Storage Warm
|
|
||||||
subgraph cluster_warm {
|
|
||||||
label="Warm Path (Historical)";
|
|
||||||
style=filled;
|
|
||||||
fillcolor="#FCE4EC";
|
|
||||||
|
|
||||||
raw [label="metrics_raw\n(5s, 24h)", shape=cylinder, fillcolor="#F8BBD9"];
|
|
||||||
agg_1m [label="metrics_1m\n(1m, 7d)", shape=cylinder, fillcolor="#F48FB1"];
|
|
||||||
agg_1h [label="metrics_1h\n(1h, 90d)", shape=cylinder, fillcolor="#EC407A"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consumers
|
|
||||||
subgraph cluster_consume {
|
|
||||||
label="Consumers";
|
|
||||||
style=filled;
|
|
||||||
fillcolor="#E8EAF6";
|
|
||||||
|
|
||||||
alerts [label="Alert\nService", fillcolor="#C5CAE9"];
|
|
||||||
gateway [label="Gateway\n(WebSocket)", fillcolor="#9FA8DA"];
|
|
||||||
lambda [label="Lambda\nAggregator", fillcolor="#7986CB", style="rounded,filled,dashed"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flow
|
|
||||||
psutil -> collector [label="Metrics"];
|
|
||||||
collector -> aggregator [label="gRPC\nStream"];
|
|
||||||
aggregator -> validate;
|
|
||||||
|
|
||||||
validate -> redis_state [label="Upsert"];
|
|
||||||
validate -> redis_pubsub [label="Publish"];
|
|
||||||
validate -> raw [label="Insert"];
|
|
||||||
|
|
||||||
redis_pubsub -> alerts [label="metrics.*"];
|
|
||||||
redis_pubsub -> gateway [label="metrics.*"];
|
|
||||||
|
|
||||||
raw -> agg_1m [label="Continuous\nAggregate", style=dashed];
|
|
||||||
agg_1m -> agg_1h [label="Hourly\nJob", style=dashed];
|
|
||||||
|
|
||||||
raw -> lambda [label="SQS\nTrigger", style=dotted];
|
|
||||||
lambda -> agg_1m [label="Batch\nWrite", style=dotted];
|
|
||||||
}
|
|
||||||
@@ -1,217 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
|
||||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<!-- Generated by graphviz version 14.1.1 (0)
|
|
||||||
-->
|
|
||||||
<!-- Title: DataFlow Pages: 1 -->
|
|
||||||
<svg width="1087pt" height="329pt"
|
|
||||||
viewBox="0.00 0.00 1087.00 329.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
||||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 325.25)">
|
|
||||||
<title>DataFlow</title>
|
|
||||||
<polygon fill="white" stroke="none" points="-4,4 -4,-325.25 1082.5,-325.25 1082.5,4 -4,4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="539.25" y="-303.95" font-family="Helvetica,sans-Serif" font-size="14.00">Metrics Data Flow Pipeline</text>
|
|
||||||
<g id="clust1" class="cluster">
|
|
||||||
<title>cluster_collect</title>
|
|
||||||
<polygon fill="#e3f2fd" stroke="black" points="8,-111 8,-188 254,-188 254,-111 8,-111"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="131" y="-170.7" font-family="Helvetica,sans-Serif" font-size="14.00">Collection (5s)</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust2" class="cluster">
|
|
||||||
<title>cluster_ingest</title>
|
|
||||||
<polygon fill="#e8f5e9" stroke="black" points="307,-95 307,-204 562.5,-204 562.5,-95 307,-95"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="434.75" y="-186.7" font-family="Helvetica,sans-Serif" font-size="14.00">Ingestion</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust3" class="cluster">
|
|
||||||
<title>cluster_hot</title>
|
|
||||||
<polygon fill="#fff3e0" stroke="black" points="614.75,-34 614.75,-193 769.5,-193 769.5,-34 614.75,-34"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="692.12" y="-175.7" font-family="Helvetica,sans-Serif" font-size="14.00">Hot Path (Real-time)</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust4" class="cluster">
|
|
||||||
<title>cluster_warm</title>
|
|
||||||
<polygon fill="#fce4ec" stroke="black" points="645.62,-201 645.62,-288 1070.5,-288 1070.5,-201 645.62,-201"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="858.06" y="-270.7" font-family="Helvetica,sans-Serif" font-size="14.00">Warm Path (Historical)</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust5" class="cluster">
|
|
||||||
<title>cluster_consume</title>
|
|
||||||
<polygon fill="#e8eaf6" stroke="black" points="840.5,-8 840.5,-193 935.25,-193 935.25,-8 840.5,-8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="887.88" y="-175.7" font-family="Helvetica,sans-Serif" font-size="14.00">Consumers</text>
|
|
||||||
</g>
|
|
||||||
<!-- psutil -->
|
|
||||||
<g id="node1" class="node">
|
|
||||||
<title>psutil</title>
|
|
||||||
<polygon fill="#bbdefb" stroke="black" points="118.25,-155 16,-155 16,-151 12,-151 12,-147 16,-147 16,-127 12,-127 12,-123 16,-123 16,-119 118.25,-119 118.25,-155"/>
|
|
||||||
<polyline fill="none" stroke="black" points="16,-151 20,-151 20,-147 16,-147"/>
|
|
||||||
<polyline fill="none" stroke="black" points="16,-127 20,-127 20,-123 16,-123"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="67.13" y="-140.25" font-family="Helvetica,sans-Serif" font-size="10.00">psutil</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="67.13" y="-127.5" font-family="Helvetica,sans-Serif" font-size="10.00">(CPU, Mem, Disk)</text>
|
|
||||||
</g>
|
|
||||||
<!-- collector -->
|
|
||||||
<g id="node2" class="node">
|
|
||||||
<title>collector</title>
|
|
||||||
<path fill="#90caf9" stroke="black" d="M234,-155C234,-155 198.5,-155 198.5,-155 192.5,-155 186.5,-149 186.5,-143 186.5,-143 186.5,-131 186.5,-131 186.5,-125 192.5,-119 198.5,-119 198.5,-119 234,-119 234,-119 240,-119 246,-125 246,-131 246,-131 246,-143 246,-143 246,-149 240,-155 234,-155"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="216.25" y="-140.25" font-family="Helvetica,sans-Serif" font-size="10.00">Collector</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="216.25" y="-127.5" font-family="Helvetica,sans-Serif" font-size="10.00">Service</text>
|
|
||||||
</g>
|
|
||||||
<!-- psutil->collector -->
|
|
||||||
<g id="edge1" class="edge">
|
|
||||||
<title>psutil->collector</title>
|
|
||||||
<path fill="none" stroke="black" d="M118.35,-137C136.74,-137 157.31,-137 174.75,-137"/>
|
|
||||||
<polygon fill="black" stroke="black" points="174.75,-140.5 184.75,-137 174.75,-133.5 174.75,-140.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="152.38" y="-139.7" font-family="Helvetica,sans-Serif" font-size="9.00">Metrics</text>
|
|
||||||
</g>
|
|
||||||
<!-- aggregator -->
|
|
||||||
<g id="node3" class="node">
|
|
||||||
<title>aggregator</title>
|
|
||||||
<path fill="#a5d6a7" stroke="black" d="M373,-155C373,-155 327,-155 327,-155 321,-155 315,-149 315,-143 315,-143 315,-131 315,-131 315,-125 321,-119 327,-119 327,-119 373,-119 373,-119 379,-119 385,-125 385,-131 385,-131 385,-143 385,-143 385,-149 379,-155 373,-155"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="350" y="-140.25" font-family="Helvetica,sans-Serif" font-size="10.00">Aggregator</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="350" y="-127.5" font-family="Helvetica,sans-Serif" font-size="10.00">(gRPC)</text>
|
|
||||||
</g>
|
|
||||||
<!-- collector->aggregator -->
|
|
||||||
<g id="edge2" class="edge">
|
|
||||||
<title>collector->aggregator</title>
|
|
||||||
<path fill="none" stroke="black" d="M246.49,-137C263.19,-137 284.49,-137 303.35,-137"/>
|
|
||||||
<polygon fill="black" stroke="black" points="303.2,-140.5 313.2,-137 303.2,-133.5 303.2,-140.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="280.5" y="-150.95" font-family="Helvetica,sans-Serif" font-size="9.00">gRPC</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="280.5" y="-139.7" font-family="Helvetica,sans-Serif" font-size="9.00">Stream</text>
|
|
||||||
</g>
|
|
||||||
<!-- validate -->
|
|
||||||
<g id="node4" class="node">
|
|
||||||
<title>validate</title>
|
|
||||||
<path fill="#c8e6c9" stroke="black" d="M477.54,-165.08C477.54,-165.08 432.71,-142.42 432.71,-142.42 427.35,-139.71 427.35,-134.29 432.71,-131.58 432.71,-131.58 477.54,-108.92 477.54,-108.92 482.9,-106.21 493.6,-106.21 498.96,-108.92 498.96,-108.92 543.79,-131.58 543.79,-131.58 549.15,-134.29 549.15,-139.71 543.79,-142.42 543.79,-142.42 498.96,-165.08 498.96,-165.08 493.6,-167.79 482.9,-167.79 477.54,-165.08"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="488.25" y="-140.25" font-family="Helvetica,sans-Serif" font-size="10.00">Validate &</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="488.25" y="-127.5" font-family="Helvetica,sans-Serif" font-size="10.00">Normalize</text>
|
|
||||||
</g>
|
|
||||||
<!-- aggregator->validate -->
|
|
||||||
<g id="edge3" class="edge">
|
|
||||||
<title>aggregator->validate</title>
|
|
||||||
<path fill="none" stroke="black" d="M385.38,-137C392.95,-137 401.25,-137 409.76,-137"/>
|
|
||||||
<polygon fill="black" stroke="black" points="409.49,-140.5 419.49,-137 409.49,-133.5 409.49,-140.5"/>
|
|
||||||
</g>
|
|
||||||
<!-- redis_state -->
|
|
||||||
<g id="node5" class="node">
|
|
||||||
<title>redis_state</title>
|
|
||||||
<path fill="#ffcc80" stroke="black" d="M731.88,-155.84C731.88,-158.15 713.83,-160.03 691.62,-160.03 669.42,-160.03 651.38,-158.15 651.38,-155.84 651.38,-155.84 651.38,-118.16 651.38,-118.16 651.38,-115.85 669.42,-113.97 691.62,-113.97 713.83,-113.97 731.88,-115.85 731.88,-118.16 731.88,-118.16 731.88,-155.84 731.88,-155.84"/>
|
|
||||||
<path fill="none" stroke="black" d="M731.88,-155.84C731.88,-153.53 713.83,-151.66 691.62,-151.66 669.42,-151.66 651.38,-153.53 651.38,-155.84"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="691.62" y="-140.25" font-family="Helvetica,sans-Serif" font-size="10.00">Redis</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="691.62" y="-127.5" font-family="Helvetica,sans-Serif" font-size="10.00">Current State</text>
|
|
||||||
</g>
|
|
||||||
<!-- validate->redis_state -->
|
|
||||||
<g id="edge4" class="edge">
|
|
||||||
<title>validate->redis_state</title>
|
|
||||||
<path fill="none" stroke="black" d="M555.47,-137C582.9,-137 614.22,-137 639.8,-137"/>
|
|
||||||
<polygon fill="black" stroke="black" points="639.6,-140.5 649.6,-137 639.6,-133.5 639.6,-140.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="588.62" y="-139.7" font-family="Helvetica,sans-Serif" font-size="9.00">Upsert</text>
|
|
||||||
</g>
|
|
||||||
<!-- redis_pubsub -->
|
|
||||||
<g id="node6" class="node">
|
|
||||||
<title>redis_pubsub</title>
|
|
||||||
<path fill="#ffb74d" stroke="black" d="M729.05,-78.12C729.05,-78.12 721.56,-87.24 721.56,-87.24 717.82,-91.79 708.18,-96.35 702.28,-96.35 702.28,-96.35 680.97,-96.35 680.97,-96.35 675.07,-96.35 665.43,-91.79 661.69,-87.24 661.69,-87.24 654.2,-78.12 654.2,-78.12 650.46,-73.56 650.46,-64.44 654.2,-59.88 654.2,-59.88 661.69,-50.76 661.69,-50.76 665.43,-46.21 675.07,-41.65 680.97,-41.65 680.97,-41.65 702.28,-41.65 702.28,-41.65 708.18,-41.65 717.82,-46.21 721.56,-50.76 721.56,-50.76 729.05,-59.88 729.05,-59.88 732.79,-64.44 732.79,-73.56 729.05,-78.12"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="691.62" y="-72.25" font-family="Helvetica,sans-Serif" font-size="10.00">Redis</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="691.62" y="-59.5" font-family="Helvetica,sans-Serif" font-size="10.00">Pub/Sub</text>
|
|
||||||
</g>
|
|
||||||
<!-- validate->redis_pubsub -->
|
|
||||||
<g id="edge5" class="edge">
|
|
||||||
<title>validate->redis_pubsub</title>
|
|
||||||
<path fill="none" stroke="black" d="M529.04,-123.57C562.44,-112.28 610.18,-96.17 645.1,-84.37"/>
|
|
||||||
<polygon fill="black" stroke="black" points="646.17,-87.71 654.53,-81.19 643.93,-81.07 646.17,-87.71"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="588.62" y="-109.77" font-family="Helvetica,sans-Serif" font-size="9.00">Publish</text>
|
|
||||||
</g>
|
|
||||||
<!-- raw -->
|
|
||||||
<g id="node7" class="node">
|
|
||||||
<title>raw</title>
|
|
||||||
<path fill="#f8bbd9" stroke="black" d="M729.62,-250.84C729.62,-253.15 712.59,-255.03 691.62,-255.03 670.66,-255.03 653.62,-253.15 653.62,-250.84 653.62,-250.84 653.62,-213.16 653.62,-213.16 653.62,-210.85 670.66,-208.97 691.62,-208.97 712.59,-208.97 729.62,-210.85 729.62,-213.16 729.62,-213.16 729.62,-250.84 729.62,-250.84"/>
|
|
||||||
<path fill="none" stroke="black" d="M729.62,-250.84C729.62,-248.53 712.59,-246.66 691.62,-246.66 670.66,-246.66 653.62,-248.53 653.62,-250.84"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="691.62" y="-235.25" font-family="Helvetica,sans-Serif" font-size="10.00">metrics_raw</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="691.62" y="-222.5" font-family="Helvetica,sans-Serif" font-size="10.00">(5s, 24h)</text>
|
|
||||||
</g>
|
|
||||||
<!-- validate->raw -->
|
|
||||||
<g id="edge6" class="edge">
|
|
||||||
<title>validate->raw</title>
|
|
||||||
<path fill="none" stroke="black" d="M523.01,-153.3C548.24,-165.44 583.6,-182.37 614.75,-197 623.81,-201.26 633.5,-205.76 642.83,-210.07"/>
|
|
||||||
<polygon fill="black" stroke="black" points="641.22,-213.19 651.77,-214.2 644.16,-206.83 641.22,-213.19"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="588.62" y="-194.9" font-family="Helvetica,sans-Serif" font-size="9.00">Insert</text>
|
|
||||||
</g>
|
|
||||||
<!-- alerts -->
|
|
||||||
<g id="node10" class="node">
|
|
||||||
<title>alerts</title>
|
|
||||||
<path fill="#c5cae9" stroke="black" d="M902.38,-106C902.38,-106 872.38,-106 872.38,-106 866.38,-106 860.38,-100 860.38,-94 860.38,-94 860.38,-82 860.38,-82 860.38,-76 866.38,-70 872.38,-70 872.38,-70 902.38,-70 902.38,-70 908.38,-70 914.38,-76 914.38,-82 914.38,-82 914.38,-94 914.38,-94 914.38,-100 908.38,-106 902.38,-106"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="887.38" y="-91.25" font-family="Helvetica,sans-Serif" font-size="10.00">Alert</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="887.38" y="-78.5" font-family="Helvetica,sans-Serif" font-size="10.00">Service</text>
|
|
||||||
</g>
|
|
||||||
<!-- redis_pubsub->alerts -->
|
|
||||||
<g id="edge7" class="edge">
|
|
||||||
<title>redis_pubsub->alerts</title>
|
|
||||||
<path fill="none" stroke="black" d="M733.71,-73.03C767.65,-76.36 815.43,-81.04 848.46,-84.28"/>
|
|
||||||
<polygon fill="black" stroke="black" points="848.11,-87.76 858.4,-85.26 848.79,-80.8 848.11,-87.76"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="805" y="-85.09" font-family="Helvetica,sans-Serif" font-size="9.00">metrics.*</text>
|
|
||||||
</g>
|
|
||||||
<!-- gateway -->
|
|
||||||
<g id="node11" class="node">
|
|
||||||
<title>gateway</title>
|
|
||||||
<path fill="#9fa8da" stroke="black" d="M913.75,-52C913.75,-52 861,-52 861,-52 855,-52 849,-46 849,-40 849,-40 849,-28 849,-28 849,-22 855,-16 861,-16 861,-16 913.75,-16 913.75,-16 919.75,-16 925.75,-22 925.75,-28 925.75,-28 925.75,-40 925.75,-40 925.75,-46 919.75,-52 913.75,-52"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="887.38" y="-37.25" font-family="Helvetica,sans-Serif" font-size="10.00">Gateway</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="887.38" y="-24.5" font-family="Helvetica,sans-Serif" font-size="10.00">(WebSocket)</text>
|
|
||||||
</g>
|
|
||||||
<!-- redis_pubsub->gateway -->
|
|
||||||
<g id="edge8" class="edge">
|
|
||||||
<title>redis_pubsub->gateway</title>
|
|
||||||
<path fill="none" stroke="black" d="M731.37,-62C761.89,-56.49 804.64,-48.77 837.51,-42.83"/>
|
|
||||||
<polygon fill="black" stroke="black" points="837.98,-46.3 847.2,-41.08 836.74,-39.41 837.98,-46.3"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="805" y="-55.25" font-family="Helvetica,sans-Serif" font-size="9.00">metrics.*</text>
|
|
||||||
</g>
|
|
||||||
<!-- agg_1m -->
|
|
||||||
<g id="node8" class="node">
|
|
||||||
<title>agg_1m</title>
|
|
||||||
<path fill="#f48fb1" stroke="black" d="M924.25,-250.84C924.25,-253.15 907.72,-255.03 887.38,-255.03 867.03,-255.03 850.5,-253.15 850.5,-250.84 850.5,-250.84 850.5,-213.16 850.5,-213.16 850.5,-210.85 867.03,-208.97 887.38,-208.97 907.72,-208.97 924.25,-210.85 924.25,-213.16 924.25,-213.16 924.25,-250.84 924.25,-250.84"/>
|
|
||||||
<path fill="none" stroke="black" d="M924.25,-250.84C924.25,-248.53 907.72,-246.66 887.38,-246.66 867.03,-246.66 850.5,-248.53 850.5,-250.84"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="887.38" y="-235.25" font-family="Helvetica,sans-Serif" font-size="10.00">metrics_1m</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="887.38" y="-222.5" font-family="Helvetica,sans-Serif" font-size="10.00">(1m, 7d)</text>
|
|
||||||
</g>
|
|
||||||
<!-- raw->agg_1m -->
|
|
||||||
<g id="edge9" class="edge">
|
|
||||||
<title>raw->agg_1m</title>
|
|
||||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M729.98,-232C760.97,-232 805.22,-232 838.74,-232"/>
|
|
||||||
<polygon fill="black" stroke="black" points="838.6,-235.5 848.6,-232 838.6,-228.5 838.6,-235.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="805" y="-245.95" font-family="Helvetica,sans-Serif" font-size="9.00">Continuous</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="805" y="-234.7" font-family="Helvetica,sans-Serif" font-size="9.00">Aggregate</text>
|
|
||||||
</g>
|
|
||||||
<!-- lambda -->
|
|
||||||
<g id="node12" class="node">
|
|
||||||
<title>lambda</title>
|
|
||||||
<path fill="#7986cb" stroke="black" stroke-dasharray="5,2" d="M910.38,-160C910.38,-160 864.38,-160 864.38,-160 858.38,-160 852.38,-154 852.38,-148 852.38,-148 852.38,-136 852.38,-136 852.38,-130 858.38,-124 864.38,-124 864.38,-124 910.38,-124 910.38,-124 916.38,-124 922.38,-130 922.38,-136 922.38,-136 922.38,-148 922.38,-148 922.38,-154 916.38,-160 910.38,-160"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="887.38" y="-145.25" font-family="Helvetica,sans-Serif" font-size="10.00">Lambda</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="887.38" y="-132.5" font-family="Helvetica,sans-Serif" font-size="10.00">Aggregator</text>
|
|
||||||
</g>
|
|
||||||
<!-- raw->lambda -->
|
|
||||||
<g id="edge11" class="edge">
|
|
||||||
<title>raw->lambda</title>
|
|
||||||
<path fill="none" stroke="black" stroke-dasharray="1,5" d="M729.81,-215.18C742.43,-209.45 756.59,-202.98 769.5,-197 793.37,-185.95 819.91,-173.48 841.65,-163.21"/>
|
|
||||||
<polygon fill="black" stroke="black" points="843,-166.44 850.54,-159.01 840,-160.12 843,-166.44"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="805" y="-205.05" font-family="Helvetica,sans-Serif" font-size="9.00">SQS</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="805" y="-193.8" font-family="Helvetica,sans-Serif" font-size="9.00">Trigger</text>
|
|
||||||
</g>
|
|
||||||
<!-- agg_1h -->
|
|
||||||
<g id="node9" class="node">
|
|
||||||
<title>agg_1h</title>
|
|
||||||
<path fill="#ec407a" stroke="black" d="M1062.5,-250.84C1062.5,-253.15 1046.81,-255.03 1027.5,-255.03 1008.19,-255.03 992.5,-253.15 992.5,-250.84 992.5,-250.84 992.5,-213.16 992.5,-213.16 992.5,-210.85 1008.19,-208.97 1027.5,-208.97 1046.81,-208.97 1062.5,-210.85 1062.5,-213.16 1062.5,-213.16 1062.5,-250.84 1062.5,-250.84"/>
|
|
||||||
<path fill="none" stroke="black" d="M1062.5,-250.84C1062.5,-248.53 1046.81,-246.66 1027.5,-246.66 1008.19,-246.66 992.5,-248.53 992.5,-250.84"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1027.5" y="-235.25" font-family="Helvetica,sans-Serif" font-size="10.00">metrics_1h</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="1027.5" y="-222.5" font-family="Helvetica,sans-Serif" font-size="10.00">(1h, 90d)</text>
|
|
||||||
</g>
|
|
||||||
<!-- agg_1m->agg_1h -->
|
|
||||||
<g id="edge10" class="edge">
|
|
||||||
<title>agg_1m->agg_1h</title>
|
|
||||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M924.67,-232C941.93,-232 962.74,-232 981.04,-232"/>
|
|
||||||
<polygon fill="black" stroke="black" points="980.84,-235.5 990.84,-232 980.84,-228.5 980.84,-235.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="959.88" y="-245.95" font-family="Helvetica,sans-Serif" font-size="9.00">Hourly</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="959.88" y="-234.7" font-family="Helvetica,sans-Serif" font-size="9.00">Job</text>
|
|
||||||
</g>
|
|
||||||
<!-- lambda->agg_1m -->
|
|
||||||
<g id="edge12" class="edge">
|
|
||||||
<title>lambda->agg_1m</title>
|
|
||||||
<path fill="none" stroke="black" stroke-dasharray="1,5" d="M887.38,-160.21C887.38,-170.91 887.38,-184.78 887.38,-197.47"/>
|
|
||||||
<polygon fill="black" stroke="black" points="883.88,-197.16 887.38,-207.16 890.88,-197.16 883.88,-197.16"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="873.12" y="-187.18" font-family="Helvetica,sans-Serif" font-size="9.00">Batch</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="873.12" y="-175.93" font-family="Helvetica,sans-Serif" font-size="9.00">Write</text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 17 KiB |
@@ -1,95 +0,0 @@
|
|||||||
digraph Deployment {
|
|
||||||
rankdir=TB;
|
|
||||||
compound=true;
|
|
||||||
fontname="Helvetica";
|
|
||||||
node [fontname="Helvetica", fontsize=10];
|
|
||||||
edge [fontname="Helvetica", fontsize=9];
|
|
||||||
|
|
||||||
labelloc="t";
|
|
||||||
label="Deployment Architecture";
|
|
||||||
fontsize=14;
|
|
||||||
|
|
||||||
node [shape=box, style="rounded,filled"];
|
|
||||||
|
|
||||||
// Local Development
|
|
||||||
subgraph cluster_local {
|
|
||||||
label="Local Development";
|
|
||||||
style=filled;
|
|
||||||
fillcolor="#E3F2FD";
|
|
||||||
|
|
||||||
subgraph cluster_kind {
|
|
||||||
label="Kind Cluster";
|
|
||||||
style=filled;
|
|
||||||
fillcolor="#BBDEFB";
|
|
||||||
|
|
||||||
tilt [label="Tilt\n(Live Reload)", shape=component, fillcolor="#90CAF9"];
|
|
||||||
k8s_local [label="K8s Pods\n(via Kustomize)", fillcolor="#64B5F6"];
|
|
||||||
}
|
|
||||||
|
|
||||||
compose [label="Docker Compose\n(Alternative)", fillcolor="#90CAF9", style="rounded,dashed"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// AWS Staging/Demo
|
|
||||||
subgraph cluster_aws {
|
|
||||||
label="AWS (sysmonstm.mcrn.ar)";
|
|
||||||
style=filled;
|
|
||||||
fillcolor="#E8F5E9";
|
|
||||||
|
|
||||||
subgraph cluster_ec2 {
|
|
||||||
label="EC2 t2.small";
|
|
||||||
style=filled;
|
|
||||||
fillcolor="#C8E6C9";
|
|
||||||
|
|
||||||
compose_ec2 [label="Docker Compose\n(All Services)", fillcolor="#A5D6A7"];
|
|
||||||
nginx [label="Nginx\n(SSL Termination)", fillcolor="#81C784"];
|
|
||||||
}
|
|
||||||
|
|
||||||
subgraph cluster_lambda {
|
|
||||||
label="Lambda (Data Processing)";
|
|
||||||
style=filled;
|
|
||||||
fillcolor="#DCEDC8";
|
|
||||||
|
|
||||||
lambda_agg [label="Aggregator\nLambda", fillcolor="#AED581"];
|
|
||||||
lambda_compact [label="Compactor\nLambda", fillcolor="#9CCC65"];
|
|
||||||
}
|
|
||||||
|
|
||||||
sqs [label="SQS\n(Buffer)", shape=hexagon, fillcolor="#FFE082"];
|
|
||||||
s3 [label="S3\n(Backup)", shape=cylinder, fillcolor="#FFE082"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// CI/CD
|
|
||||||
subgraph cluster_cicd {
|
|
||||||
label="CI/CD";
|
|
||||||
style=filled;
|
|
||||||
fillcolor="#F3E5F5";
|
|
||||||
|
|
||||||
woodpecker [label="Woodpecker CI", fillcolor="#CE93D8"];
|
|
||||||
registry [label="Container\nRegistry", shape=cylinder, fillcolor="#BA68C8"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collectors (External)
|
|
||||||
subgraph cluster_collectors {
|
|
||||||
label="Monitored Machines";
|
|
||||||
style=dashed;
|
|
||||||
color=gray;
|
|
||||||
|
|
||||||
coll1 [label="Collector\n(Machine 1)", fillcolor="#FFCCBC"];
|
|
||||||
coll2 [label="Collector\n(Machine 2)", fillcolor="#FFCCBC"];
|
|
||||||
coll3 [label="Collector\n(Machine N)", fillcolor="#FFCCBC"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connections
|
|
||||||
tilt -> k8s_local [style=invis];
|
|
||||||
woodpecker -> registry [label="Push"];
|
|
||||||
registry -> compose_ec2 [label="Pull"];
|
|
||||||
registry -> k8s_local [label="Pull", style=dashed];
|
|
||||||
|
|
||||||
nginx -> compose_ec2 [label="Proxy"];
|
|
||||||
compose_ec2 -> sqs [label="Events"];
|
|
||||||
sqs -> lambda_agg [label="Trigger"];
|
|
||||||
lambda_compact -> s3 [label="Archive"];
|
|
||||||
|
|
||||||
coll1 -> compose_ec2 [label="gRPC", lhead=cluster_ec2];
|
|
||||||
coll2 -> compose_ec2 [label="gRPC", lhead=cluster_ec2];
|
|
||||||
coll3 -> compose_ec2 [label="gRPC", lhead=cluster_ec2];
|
|
||||||
}
|
|
||||||
@@ -1,221 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
|
||||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<!-- Generated by graphviz version 14.1.1 (0)
|
|
||||||
-->
|
|
||||||
<!-- Title: Deployment Pages: 1 -->
|
|
||||||
<svg width="872pt" height="662pt"
|
|
||||||
viewBox="0.00 0.00 872.00 662.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
||||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 658.3)">
|
|
||||||
<title>Deployment</title>
|
|
||||||
<polygon fill="white" stroke="none" points="-4,4 -4,-658.3 868,-658.3 868,4 -4,4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="432" y="-637" font-family="Helvetica,sans-Serif" font-size="14.00">Deployment Architecture</text>
|
|
||||||
<g id="clust1" class="cluster">
|
|
||||||
<title>cluster_local</title>
|
|
||||||
<polygon fill="#e3f2fd" stroke="black" points="8,-307.77 8,-514.55 238,-514.55 238,-307.77 8,-307.77"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="123" y="-497.25" font-family="Helvetica,sans-Serif" font-size="14.00">Local Development</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust2" class="cluster">
|
|
||||||
<title>cluster_kind</title>
|
|
||||||
<polygon fill="#bbdefb" stroke="black" points="16,-315.77 16,-481.3 124,-481.3 124,-315.77 16,-315.77"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="70" y="-464" font-family="Helvetica,sans-Serif" font-size="14.00">Kind Cluster</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust3" class="cluster">
|
|
||||||
<title>cluster_aws</title>
|
|
||||||
<polygon fill="#e8f5e9" stroke="black" points="642,-8 642,-514.55 856,-514.55 856,-8 642,-8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="749" y="-497.25" font-family="Helvetica,sans-Serif" font-size="14.00">AWS (sysmonstm.mcrn.ar)</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust4" class="cluster">
|
|
||||||
<title>cluster_ec2</title>
|
|
||||||
<polygon fill="#c8e6c9" stroke="black" points="650,-315.77 650,-481.3 768,-481.3 768,-315.77 650,-315.77"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="709" y="-464" font-family="Helvetica,sans-Serif" font-size="14.00">EC2 t2.small</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust5" class="cluster">
|
|
||||||
<title>cluster_lambda</title>
|
|
||||||
<polygon fill="#dcedc8" stroke="black" points="650,-101.31 650,-178.56 848,-178.56 848,-101.31 650,-101.31"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="749" y="-161.26" font-family="Helvetica,sans-Serif" font-size="14.00">Lambda (Data Processing)</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust6" class="cluster">
|
|
||||||
<title>cluster_cicd</title>
|
|
||||||
<polygon fill="#f3e5f5" stroke="black" points="246,-399.02 246,-621.05 350,-621.05 350,-399.02 246,-399.02"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="298" y="-603.75" font-family="Helvetica,sans-Serif" font-size="14.00">CI/CD</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust7" class="cluster">
|
|
||||||
<title>cluster_collectors</title>
|
|
||||||
<polygon fill="none" stroke="gray" stroke-dasharray="5,2" points="358,-404.05 358,-481.3 634,-481.3 634,-404.05 358,-404.05"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="496" y="-464" font-family="Helvetica,sans-Serif" font-size="14.00">Monitored Machines</text>
|
|
||||||
</g>
|
|
||||||
<!-- tilt -->
|
|
||||||
<g id="node1" class="node">
|
|
||||||
<title>tilt</title>
|
|
||||||
<polygon fill="#90caf9" stroke="black" points="110.25,-448.05 29.75,-448.05 29.75,-444.05 25.75,-444.05 25.75,-440.05 29.75,-440.05 29.75,-420.05 25.75,-420.05 25.75,-416.05 29.75,-416.05 29.75,-412.05 110.25,-412.05 110.25,-448.05"/>
|
|
||||||
<polyline fill="none" stroke="black" points="29.75,-444.05 33.75,-444.05 33.75,-440.05 29.75,-440.05"/>
|
|
||||||
<polyline fill="none" stroke="black" points="29.75,-420.05 33.75,-420.05 33.75,-416.05 29.75,-416.05"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="70" y="-433.3" font-family="Helvetica,sans-Serif" font-size="10.00">Tilt</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="70" y="-420.55" font-family="Helvetica,sans-Serif" font-size="10.00">(Live Reload)</text>
|
|
||||||
</g>
|
|
||||||
<!-- k8s_local -->
|
|
||||||
<g id="node2" class="node">
|
|
||||||
<title>k8s_local</title>
|
|
||||||
<path fill="#64b5f6" stroke="black" d="M104.25,-359.77C104.25,-359.77 35.75,-359.77 35.75,-359.77 29.75,-359.77 23.75,-353.77 23.75,-347.77 23.75,-347.77 23.75,-335.77 23.75,-335.77 23.75,-329.77 29.75,-323.77 35.75,-323.77 35.75,-323.77 104.25,-323.77 104.25,-323.77 110.25,-323.77 116.25,-329.77 116.25,-335.77 116.25,-335.77 116.25,-347.77 116.25,-347.77 116.25,-353.77 110.25,-359.77 104.25,-359.77"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="70" y="-345.02" font-family="Helvetica,sans-Serif" font-size="10.00">K8s Pods</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="70" y="-332.27" font-family="Helvetica,sans-Serif" font-size="10.00">(via Kustomize)</text>
|
|
||||||
</g>
|
|
||||||
<!-- tilt->k8s_local -->
|
|
||||||
<!-- compose -->
|
|
||||||
<g id="node3" class="node">
|
|
||||||
<title>compose</title>
|
|
||||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M218.25,-448.05C218.25,-448.05 143.75,-448.05 143.75,-448.05 137.75,-448.05 131.75,-442.05 131.75,-436.05 131.75,-436.05 131.75,-424.05 131.75,-424.05 131.75,-418.05 137.75,-412.05 143.75,-412.05 143.75,-412.05 218.25,-412.05 218.25,-412.05 224.25,-412.05 230.25,-418.05 230.25,-424.05 230.25,-424.05 230.25,-436.05 230.25,-436.05 230.25,-442.05 224.25,-448.05 218.25,-448.05"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="181" y="-433.3" font-family="Helvetica,sans-Serif" font-size="10.00">Docker Compose</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="181" y="-420.55" font-family="Helvetica,sans-Serif" font-size="10.00">(Alternative)</text>
|
|
||||||
</g>
|
|
||||||
<!-- compose_ec2 -->
|
|
||||||
<g id="node4" class="node">
|
|
||||||
<title>compose_ec2</title>
|
|
||||||
<path fill="#a5d6a7" stroke="black" d="M744.25,-359.77C744.25,-359.77 669.75,-359.77 669.75,-359.77 663.75,-359.77 657.75,-353.77 657.75,-347.77 657.75,-347.77 657.75,-335.77 657.75,-335.77 657.75,-329.77 663.75,-323.77 669.75,-323.77 669.75,-323.77 744.25,-323.77 744.25,-323.77 750.25,-323.77 756.25,-329.77 756.25,-335.77 756.25,-335.77 756.25,-347.77 756.25,-347.77 756.25,-353.77 750.25,-359.77 744.25,-359.77"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="707" y="-345.02" font-family="Helvetica,sans-Serif" font-size="10.00">Docker Compose</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="707" y="-332.27" font-family="Helvetica,sans-Serif" font-size="10.00">(All Services)</text>
|
|
||||||
</g>
|
|
||||||
<!-- sqs -->
|
|
||||||
<g id="node8" class="node">
|
|
||||||
<title>sqs</title>
|
|
||||||
<path fill="#ffe082" stroke="black" d="M742.89,-252.28C742.89,-252.28 735.71,-261.4 735.71,-261.4 732.12,-265.96 722.73,-270.52 716.93,-270.52 716.93,-270.52 697.07,-270.52 697.07,-270.52 691.27,-270.52 681.88,-265.96 678.29,-261.4 678.29,-261.4 671.11,-252.28 671.11,-252.28 667.52,-247.72 667.52,-238.61 671.11,-234.05 671.11,-234.05 678.29,-224.93 678.29,-224.93 681.88,-220.37 691.27,-215.81 697.07,-215.81 697.07,-215.81 716.93,-215.81 716.93,-215.81 722.73,-215.81 732.12,-220.37 735.71,-224.93 735.71,-224.93 742.89,-234.05 742.89,-234.05 746.48,-238.61 746.48,-247.72 742.89,-252.28"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="707" y="-246.42" font-family="Helvetica,sans-Serif" font-size="10.00">SQS</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="707" y="-233.67" font-family="Helvetica,sans-Serif" font-size="10.00">(Buffer)</text>
|
|
||||||
</g>
|
|
||||||
<!-- compose_ec2->sqs -->
|
|
||||||
<g id="edge6" class="edge">
|
|
||||||
<title>compose_ec2->sqs</title>
|
|
||||||
<path fill="none" stroke="black" d="M707,-323.5C707,-311.94 707,-296.26 707,-281.89"/>
|
|
||||||
<polygon fill="black" stroke="black" points="710.5,-282.27 707,-272.27 703.5,-282.27 710.5,-282.27"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="722.38" y="-291.22" font-family="Helvetica,sans-Serif" font-size="9.00">Events</text>
|
|
||||||
</g>
|
|
||||||
<!-- nginx -->
|
|
||||||
<g id="node5" class="node">
|
|
||||||
<title>nginx</title>
|
|
||||||
<path fill="#81c784" stroke="black" d="M747.75,-448.05C747.75,-448.05 670.25,-448.05 670.25,-448.05 664.25,-448.05 658.25,-442.05 658.25,-436.05 658.25,-436.05 658.25,-424.05 658.25,-424.05 658.25,-418.05 664.25,-412.05 670.25,-412.05 670.25,-412.05 747.75,-412.05 747.75,-412.05 753.75,-412.05 759.75,-418.05 759.75,-424.05 759.75,-424.05 759.75,-436.05 759.75,-436.05 759.75,-442.05 753.75,-448.05 747.75,-448.05"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="709" y="-433.3" font-family="Helvetica,sans-Serif" font-size="10.00">Nginx</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="709" y="-420.55" font-family="Helvetica,sans-Serif" font-size="10.00">(SSL Termination)</text>
|
|
||||||
</g>
|
|
||||||
<!-- nginx->compose_ec2 -->
|
|
||||||
<g id="edge5" class="edge">
|
|
||||||
<title>nginx->compose_ec2</title>
|
|
||||||
<path fill="none" stroke="black" d="M708.6,-411.59C708.33,-400.13 707.98,-384.86 707.67,-371.63"/>
|
|
||||||
<polygon fill="black" stroke="black" points="711.17,-371.63 707.44,-361.72 704.17,-371.79 711.17,-371.63"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="720.43" y="-380.47" font-family="Helvetica,sans-Serif" font-size="9.00">Proxy</text>
|
|
||||||
</g>
|
|
||||||
<!-- lambda_agg -->
|
|
||||||
<g id="node6" class="node">
|
|
||||||
<title>lambda_agg</title>
|
|
||||||
<path fill="#aed581" stroke="black" d="M730,-145.31C730,-145.31 684,-145.31 684,-145.31 678,-145.31 672,-139.31 672,-133.31 672,-133.31 672,-121.31 672,-121.31 672,-115.31 678,-109.31 684,-109.31 684,-109.31 730,-109.31 730,-109.31 736,-109.31 742,-115.31 742,-121.31 742,-121.31 742,-133.31 742,-133.31 742,-139.31 736,-145.31 730,-145.31"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="707" y="-130.56" font-family="Helvetica,sans-Serif" font-size="10.00">Aggregator</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="707" y="-117.81" font-family="Helvetica,sans-Serif" font-size="10.00">Lambda</text>
|
|
||||||
</g>
|
|
||||||
<!-- lambda_compact -->
|
|
||||||
<g id="node7" class="node">
|
|
||||||
<title>lambda_compact</title>
|
|
||||||
<path fill="#9ccc65" stroke="black" d="M822.62,-145.31C822.62,-145.31 777.38,-145.31 777.38,-145.31 771.38,-145.31 765.38,-139.31 765.38,-133.31 765.38,-133.31 765.38,-121.31 765.38,-121.31 765.38,-115.31 771.38,-109.31 777.38,-109.31 777.38,-109.31 822.62,-109.31 822.62,-109.31 828.62,-109.31 834.62,-115.31 834.62,-121.31 834.62,-121.31 834.62,-133.31 834.62,-133.31 834.62,-139.31 828.62,-145.31 822.62,-145.31"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="800" y="-130.56" font-family="Helvetica,sans-Serif" font-size="10.00">Compactor</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="800" y="-117.81" font-family="Helvetica,sans-Serif" font-size="10.00">Lambda</text>
|
|
||||||
</g>
|
|
||||||
<!-- s3 -->
|
|
||||||
<g id="node9" class="node">
|
|
||||||
<title>s3</title>
|
|
||||||
<path fill="#ffe082" stroke="black" d="M829.38,-57.88C829.38,-60.19 816.21,-62.06 800,-62.06 783.79,-62.06 770.62,-60.19 770.62,-57.88 770.62,-57.88 770.62,-20.19 770.62,-20.19 770.62,-17.88 783.79,-16 800,-16 816.21,-16 829.38,-17.88 829.38,-20.19 829.38,-20.19 829.38,-57.88 829.38,-57.88"/>
|
|
||||||
<path fill="none" stroke="black" d="M829.38,-57.88C829.38,-55.56 816.21,-53.69 800,-53.69 783.79,-53.69 770.62,-55.56 770.62,-57.88"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="800" y="-42.28" font-family="Helvetica,sans-Serif" font-size="10.00">S3</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="800" y="-29.53" font-family="Helvetica,sans-Serif" font-size="10.00">(Backup)</text>
|
|
||||||
</g>
|
|
||||||
<!-- lambda_compact->s3 -->
|
|
||||||
<g id="edge8" class="edge">
|
|
||||||
<title>lambda_compact->s3</title>
|
|
||||||
<path fill="none" stroke="black" d="M800,-108.85C800,-98.81 800,-85.84 800,-73.88"/>
|
|
||||||
<polygon fill="black" stroke="black" points="803.5,-73.9 800,-63.9 796.5,-73.9 803.5,-73.9"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="816.88" y="-82.76" font-family="Helvetica,sans-Serif" font-size="9.00">Archive</text>
|
|
||||||
</g>
|
|
||||||
<!-- sqs->lambda_agg -->
|
|
||||||
<g id="edge7" class="edge">
|
|
||||||
<title>sqs->lambda_agg</title>
|
|
||||||
<path fill="none" stroke="black" d="M707,-215.47C707,-197.96 707,-175.06 707,-157.13"/>
|
|
||||||
<polygon fill="black" stroke="black" points="710.5,-157.15 707,-147.15 703.5,-157.15 710.5,-157.15"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="722.75" y="-189.26" font-family="Helvetica,sans-Serif" font-size="9.00">Trigger</text>
|
|
||||||
</g>
|
|
||||||
<!-- woodpecker -->
|
|
||||||
<g id="node10" class="node">
|
|
||||||
<title>woodpecker</title>
|
|
||||||
<path fill="#ce93d8" stroke="black" d="M330,-587.8C330,-587.8 266,-587.8 266,-587.8 260,-587.8 254,-581.8 254,-575.8 254,-575.8 254,-563.8 254,-563.8 254,-557.8 260,-551.8 266,-551.8 266,-551.8 330,-551.8 330,-551.8 336,-551.8 342,-557.8 342,-563.8 342,-563.8 342,-575.8 342,-575.8 342,-581.8 336,-587.8 330,-587.8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="298" y="-566.67" font-family="Helvetica,sans-Serif" font-size="10.00">Woodpecker CI</text>
|
|
||||||
</g>
|
|
||||||
<!-- registry -->
|
|
||||||
<g id="node11" class="node">
|
|
||||||
<title>registry</title>
|
|
||||||
<path fill="#ba68c8" stroke="black" d="M329.62,-448.89C329.62,-451.2 315.45,-453.08 298,-453.08 280.55,-453.08 266.38,-451.2 266.38,-448.89 266.38,-448.89 266.38,-411.21 266.38,-411.21 266.38,-408.89 280.55,-407.02 298,-407.02 315.45,-407.02 329.62,-408.89 329.62,-411.21 329.62,-411.21 329.62,-448.89 329.62,-448.89"/>
|
|
||||||
<path fill="none" stroke="black" d="M329.62,-448.89C329.62,-446.58 315.45,-444.71 298,-444.71 280.55,-444.71 266.38,-446.58 266.38,-448.89"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="298" y="-433.3" font-family="Helvetica,sans-Serif" font-size="10.00">Container</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="298" y="-420.55" font-family="Helvetica,sans-Serif" font-size="10.00">Registry</text>
|
|
||||||
</g>
|
|
||||||
<!-- woodpecker->registry -->
|
|
||||||
<g id="edge2" class="edge">
|
|
||||||
<title>woodpecker->registry</title>
|
|
||||||
<path fill="none" stroke="black" d="M298,-551.35C298,-529.66 298,-492.15 298,-464.77"/>
|
|
||||||
<polygon fill="black" stroke="black" points="301.5,-464.88 298,-454.88 294.5,-464.88 301.5,-464.88"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="308.88" y="-525.25" font-family="Helvetica,sans-Serif" font-size="9.00">Push</text>
|
|
||||||
</g>
|
|
||||||
<!-- registry->k8s_local -->
|
|
||||||
<g id="edge4" class="edge">
|
|
||||||
<title>registry->k8s_local</title>
|
|
||||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M265.9,-410.59C258.2,-406.51 249.91,-402.4 242,-399.02 204.6,-383.02 161.03,-368.81 127.1,-358.68"/>
|
|
||||||
<polygon fill="black" stroke="black" points="128.47,-355.44 117.89,-355.97 126.49,-362.15 128.47,-355.44"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="222.42" y="-380.47" font-family="Helvetica,sans-Serif" font-size="9.00">Pull</text>
|
|
||||||
</g>
|
|
||||||
<!-- registry->compose_ec2 -->
|
|
||||||
<g id="edge3" class="edge">
|
|
||||||
<title>registry->compose_ec2</title>
|
|
||||||
<path fill="none" stroke="black" d="M329.84,-409.93C337.55,-405.88 345.91,-401.95 354,-399.02 452.44,-363.35 574.46,-350.26 646.22,-345.49"/>
|
|
||||||
<polygon fill="black" stroke="black" points="646.02,-349.01 655.78,-344.88 645.58,-342.02 646.02,-349.01"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="427.09" y="-380.47" font-family="Helvetica,sans-Serif" font-size="9.00">Pull</text>
|
|
||||||
</g>
|
|
||||||
<!-- coll1 -->
|
|
||||||
<g id="node12" class="node">
|
|
||||||
<title>coll1</title>
|
|
||||||
<path fill="#ffccbc" stroke="black" d="M521.88,-448.05C521.88,-448.05 472.12,-448.05 472.12,-448.05 466.12,-448.05 460.12,-442.05 460.12,-436.05 460.12,-436.05 460.12,-424.05 460.12,-424.05 460.12,-418.05 466.12,-412.05 472.12,-412.05 472.12,-412.05 521.88,-412.05 521.88,-412.05 527.88,-412.05 533.88,-418.05 533.88,-424.05 533.88,-424.05 533.88,-436.05 533.88,-436.05 533.88,-442.05 527.88,-448.05 521.88,-448.05"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="497" y="-433.3" font-family="Helvetica,sans-Serif" font-size="10.00">Collector</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="497" y="-420.55" font-family="Helvetica,sans-Serif" font-size="10.00">(Machine 1)</text>
|
|
||||||
</g>
|
|
||||||
<!-- coll1->compose_ec2 -->
|
|
||||||
<g id="edge9" class="edge">
|
|
||||||
<title>coll1->compose_ec2</title>
|
|
||||||
<path fill="none" stroke="black" d="M521.16,-411.67C528.02,-407.19 535.63,-402.62 543,-399.02 576.02,-382.89 614.85,-369.35 646.44,-359.6"/>
|
|
||||||
<polygon fill="black" stroke="black" points="640.37,-365.52 648.58,-358.82 637.98,-358.94 640.37,-365.52"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="602.75" y="-380.47" font-family="Helvetica,sans-Serif" font-size="9.00">gRPC</text>
|
|
||||||
</g>
|
|
||||||
<!-- coll2 -->
|
|
||||||
<g id="node13" class="node">
|
|
||||||
<title>coll2</title>
|
|
||||||
<path fill="#ffccbc" stroke="black" d="M613.88,-448.05C613.88,-448.05 564.12,-448.05 564.12,-448.05 558.12,-448.05 552.12,-442.05 552.12,-436.05 552.12,-436.05 552.12,-424.05 552.12,-424.05 552.12,-418.05 558.12,-412.05 564.12,-412.05 564.12,-412.05 613.88,-412.05 613.88,-412.05 619.88,-412.05 625.88,-418.05 625.88,-424.05 625.88,-424.05 625.88,-436.05 625.88,-436.05 625.88,-442.05 619.88,-448.05 613.88,-448.05"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="589" y="-433.3" font-family="Helvetica,sans-Serif" font-size="10.00">Collector</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="589" y="-420.55" font-family="Helvetica,sans-Serif" font-size="10.00">(Machine 2)</text>
|
|
||||||
</g>
|
|
||||||
<!-- coll2->compose_ec2 -->
|
|
||||||
<g id="edge10" class="edge">
|
|
||||||
<title>coll2->compose_ec2</title>
|
|
||||||
<path fill="none" stroke="black" d="M612.88,-411.59C621.13,-405.55 630.83,-398.47 640.8,-391.17"/>
|
|
||||||
<polygon fill="black" stroke="black" points="642.77,-394.07 648.78,-385.34 638.64,-388.41 642.77,-394.07"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="670.19" y="-380.47" font-family="Helvetica,sans-Serif" font-size="9.00">gRPC</text>
|
|
||||||
</g>
|
|
||||||
<!-- coll3 -->
|
|
||||||
<g id="node14" class="node">
|
|
||||||
<title>coll3</title>
|
|
||||||
<path fill="#ffccbc" stroke="black" d="M429.62,-448.05C429.62,-448.05 378.38,-448.05 378.38,-448.05 372.38,-448.05 366.38,-442.05 366.38,-436.05 366.38,-436.05 366.38,-424.05 366.38,-424.05 366.38,-418.05 372.38,-412.05 378.38,-412.05 378.38,-412.05 429.62,-412.05 429.62,-412.05 435.62,-412.05 441.62,-418.05 441.62,-424.05 441.62,-424.05 441.62,-436.05 441.62,-436.05 441.62,-442.05 435.62,-448.05 429.62,-448.05"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="404" y="-433.3" font-family="Helvetica,sans-Serif" font-size="10.00">Collector</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="404" y="-420.55" font-family="Helvetica,sans-Serif" font-size="10.00">(Machine N)</text>
|
|
||||||
</g>
|
|
||||||
<!-- coll3->compose_ec2 -->
|
|
||||||
<g id="edge11" class="edge">
|
|
||||||
<title>coll3->compose_ec2</title>
|
|
||||||
<path fill="none" stroke="black" d="M427.53,-411.82C434.78,-407.12 442.97,-402.41 451,-399.02 514.86,-372.07 593.36,-357.28 646.47,-349.71"/>
|
|
||||||
<polygon fill="black" stroke="black" points="639.16,-354.39 648.5,-349.4 638.08,-347.48 639.16,-354.39"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="516.54" y="-380.47" font-family="Helvetica,sans-Serif" font-size="9.00">gRPC</text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 18 KiB |
@@ -1,67 +0,0 @@
|
|||||||
digraph GrpcServices {
|
|
||||||
rankdir=LR;
|
|
||||||
compound=true;
|
|
||||||
fontname="Helvetica";
|
|
||||||
node [fontname="Helvetica", fontsize=10];
|
|
||||||
edge [fontname="Helvetica", fontsize=9];
|
|
||||||
|
|
||||||
labelloc="t";
|
|
||||||
label="gRPC Service Definitions";
|
|
||||||
fontsize=14;
|
|
||||||
|
|
||||||
node [shape=record, style=filled];
|
|
||||||
|
|
||||||
// MetricsService
|
|
||||||
subgraph cluster_metrics {
|
|
||||||
label="MetricsService";
|
|
||||||
style=filled;
|
|
||||||
fillcolor="#E8F5E9";
|
|
||||||
|
|
||||||
metrics_svc [label="{MetricsService|+ StreamMetrics(stream Metric) → StreamAck\l+ GetCurrentState(StateRequest) → MachineState\l+ GetAllStates(Empty) → AllMachinesState\l}", fillcolor="#C8E6C9"];
|
|
||||||
|
|
||||||
metric_msg [label="{Metric|machine_id: string\lhostname: string\ltimestamp_ms: int64\ltype: MetricType\lvalue: double\llabels: map\l}", fillcolor="#A5D6A7"];
|
|
||||||
|
|
||||||
machine_state [label="{MachineState|machine_id: string\lhostname: string\llast_seen_ms: int64\lcurrent_metrics: Metric[]\lhealth: HealthStatus\lmetadata: map\l}", fillcolor="#A5D6A7"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// ControlService
|
|
||||||
subgraph cluster_control {
|
|
||||||
label="ControlService";
|
|
||||||
style=filled;
|
|
||||||
fillcolor="#E3F2FD";
|
|
||||||
|
|
||||||
control_svc [label="{ControlService|+ Control(stream Command) → stream Response\l}", fillcolor="#90CAF9"];
|
|
||||||
|
|
||||||
commands [label="{ControlCommand|command_id: string\l|UpdateIntervalCommand\lRestartCollectionCommand\lShutdownCommand\l}", fillcolor="#64B5F6"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConfigService
|
|
||||||
subgraph cluster_config {
|
|
||||||
label="ConfigService";
|
|
||||||
style=filled;
|
|
||||||
fillcolor="#FFF3E0";
|
|
||||||
|
|
||||||
config_svc [label="{ConfigService|+ GetConfig(ConfigRequest) → CollectorConfig\l+ WatchConfig(ConfigRequest) → stream CollectorConfig\l}", fillcolor="#FFE0B2"];
|
|
||||||
|
|
||||||
collector_config [label="{CollectorConfig|collection_interval_seconds: int32\lenabled_metrics: MetricType[]\llabels: map\lthresholds: ThresholdConfig[]\l}", fillcolor="#FFCC80"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enums
|
|
||||||
subgraph cluster_enums {
|
|
||||||
label="Enums";
|
|
||||||
style=filled;
|
|
||||||
fillcolor="#F3E5F5";
|
|
||||||
|
|
||||||
metric_type [label="{MetricType|CPU_PERCENT\lMEMORY_PERCENT\lDISK_PERCENT\lNETWORK_*\lLOAD_AVG_*\l...}", fillcolor="#E1BEE7"];
|
|
||||||
|
|
||||||
health_status [label="{HealthStatus|HEALTHY\lWARNING\lCRITICAL\lUNKNOWN\lOFFLINE\l}", fillcolor="#CE93D8"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Relationships
|
|
||||||
metrics_svc -> metric_msg [style=dashed];
|
|
||||||
metrics_svc -> machine_state [style=dashed];
|
|
||||||
control_svc -> commands [style=dashed];
|
|
||||||
config_svc -> collector_config [style=dashed];
|
|
||||||
metric_msg -> metric_type [style=dotted];
|
|
||||||
machine_state -> health_status [style=dotted];
|
|
||||||
}
|
|
||||||
@@ -1,171 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
|
||||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<!-- Generated by graphviz version 14.1.1 (0)
|
|
||||||
-->
|
|
||||||
<!-- Title: GrpcServices Pages: 1 -->
|
|
||||||
<svg width="1030pt" height="486pt"
|
|
||||||
viewBox="0.00 0.00 1030.00 486.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
||||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 482.25)">
|
|
||||||
<title>GrpcServices</title>
|
|
||||||
<polygon fill="white" stroke="none" points="-4,4 -4,-482.25 1026.25,-482.25 1026.25,4 -4,4"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="511.12" y="-460.95" font-family="Helvetica,sans-Serif" font-size="14.00">gRPC Service Definitions</text>
|
|
||||||
<g id="clust1" class="cluster">
|
|
||||||
<title>cluster_metrics</title>
|
|
||||||
<polygon fill="#e8f5e9" stroke="black" points="21.5,-8 21.5,-239 726.75,-239 726.75,-8 21.5,-8"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="374.12" y="-221.7" font-family="Helvetica,sans-Serif" font-size="14.00">MetricsService</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust2" class="cluster">
|
|
||||||
<title>cluster_control</title>
|
|
||||||
<polygon fill="#e3f2fd" stroke="black" points="23.38,-247 23.38,-336 799.25,-336 799.25,-247 23.38,-247"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="411.31" y="-318.7" font-family="Helvetica,sans-Serif" font-size="14.00">ControlService</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust3" class="cluster">
|
|
||||||
<title>cluster_config</title>
|
|
||||||
<polygon fill="#fff3e0" stroke="black" points="8,-344 8,-445 753,-445 753,-344 8,-344"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="380.5" y="-427.7" font-family="Helvetica,sans-Serif" font-size="14.00">ConfigService</text>
|
|
||||||
</g>
|
|
||||||
<g id="clust4" class="cluster">
|
|
||||||
<title>cluster_enums</title>
|
|
||||||
<polygon fill="#f3e5f5" stroke="black" points="819.25,-11 819.25,-229 1014.25,-229 1014.25,-11 819.25,-11"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="916.75" y="-211.7" font-family="Helvetica,sans-Serif" font-size="14.00">Enums</text>
|
|
||||||
</g>
|
|
||||||
<!-- metrics_svc -->
|
|
||||||
<g id="node1" class="node">
|
|
||||||
<title>metrics_svc</title>
|
|
||||||
<polygon fill="#c8e6c9" stroke="black" points="29.5,-87.88 29.5,-134.12 377.25,-134.12 377.25,-87.88 29.5,-87.88"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="73.5" y="-107.88" font-family="Helvetica,sans-Serif" font-size="10.00">MetricsService</text>
|
|
||||||
<polyline fill="none" stroke="black" points="117.5,-87.88 117.5,-134.12"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="125.5" y="-120.62" font-family="Helvetica,sans-Serif" font-size="10.00">+ StreamMetrics(stream Metric) → StreamAck</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="125.5" y="-107.88" font-family="Helvetica,sans-Serif" font-size="10.00">+ GetCurrentState(StateRequest) → MachineState</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="125.5" y="-95.12" font-family="Helvetica,sans-Serif" font-size="10.00">+ GetAllStates(Empty) → AllMachinesState</text>
|
|
||||||
</g>
|
|
||||||
<!-- metric_msg -->
|
|
||||||
<g id="node2" class="node">
|
|
||||||
<title>metric_msg</title>
|
|
||||||
<polygon fill="#a5d6a7" stroke="black" points="525.5,-16.75 525.5,-101.25 692.5,-101.25 692.5,-16.75 525.5,-16.75"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="548.88" y="-55.88" font-family="Helvetica,sans-Serif" font-size="10.00">Metric</text>
|
|
||||||
<polyline fill="none" stroke="black" points="572.25,-16.75 572.25,-101.25"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="580.25" y="-87.75" font-family="Helvetica,sans-Serif" font-size="10.00">machine_id: string</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="580.25" y="-75" font-family="Helvetica,sans-Serif" font-size="10.00">hostname: string</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="580.25" y="-62.25" font-family="Helvetica,sans-Serif" font-size="10.00">timestamp_ms: int64</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="580.25" y="-49.5" font-family="Helvetica,sans-Serif" font-size="10.00">type: MetricType</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="580.25" y="-36.75" font-family="Helvetica,sans-Serif" font-size="10.00">value: double</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="580.25" y="-24" font-family="Helvetica,sans-Serif" font-size="10.00">labels: map</text>
|
|
||||||
</g>
|
|
||||||
<!-- metrics_svc->metric_msg -->
|
|
||||||
<g id="edge1" class="edge">
|
|
||||||
<title>metrics_svc->metric_msg</title>
|
|
||||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M377.6,-88.68C424.41,-82.65 473.31,-76.35 513.96,-71.12"/>
|
|
||||||
<polygon fill="black" stroke="black" points="514.22,-74.61 523.69,-69.86 513.33,-67.67 514.22,-74.61"/>
|
|
||||||
</g>
|
|
||||||
<!-- machine_state -->
|
|
||||||
<g id="node3" class="node">
|
|
||||||
<title>machine_state</title>
|
|
||||||
<polygon fill="#a5d6a7" stroke="black" points="499.25,-120.75 499.25,-205.25 718.75,-205.25 718.75,-120.75 499.25,-120.75"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="540.62" y="-159.88" font-family="Helvetica,sans-Serif" font-size="10.00">MachineState</text>
|
|
||||||
<polyline fill="none" stroke="black" points="582,-120.75 582,-205.25"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="590" y="-191.75" font-family="Helvetica,sans-Serif" font-size="10.00">machine_id: string</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="590" y="-179" font-family="Helvetica,sans-Serif" font-size="10.00">hostname: string</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="590" y="-166.25" font-family="Helvetica,sans-Serif" font-size="10.00">last_seen_ms: int64</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="590" y="-153.5" font-family="Helvetica,sans-Serif" font-size="10.00">current_metrics: Metric[]</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="590" y="-140.75" font-family="Helvetica,sans-Serif" font-size="10.00">health: HealthStatus</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="590" y="-128" font-family="Helvetica,sans-Serif" font-size="10.00">metadata: map</text>
|
|
||||||
</g>
|
|
||||||
<!-- metrics_svc->machine_state -->
|
|
||||||
<g id="edge2" class="edge">
|
|
||||||
<title>metrics_svc->machine_state</title>
|
|
||||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M377.6,-133.32C414.74,-138.1 453.2,-143.06 487.8,-147.51"/>
|
|
||||||
<polygon fill="black" stroke="black" points="487.03,-150.94 497.4,-148.75 487.93,-144 487.03,-150.94"/>
|
|
||||||
</g>
|
|
||||||
<!-- metric_type -->
|
|
||||||
<g id="node8" class="node">
|
|
||||||
<title>metric_type</title>
|
|
||||||
<polygon fill="#e1bee7" stroke="black" points="827.25,-19.75 827.25,-104.25 1006.25,-104.25 1006.25,-19.75 827.25,-19.75"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="861.88" y="-58.88" font-family="Helvetica,sans-Serif" font-size="10.00">MetricType</text>
|
|
||||||
<polyline fill="none" stroke="black" points="896.5,-19.75 896.5,-104.25"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="904.5" y="-90.75" font-family="Helvetica,sans-Serif" font-size="10.00">CPU_PERCENT</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="904.5" y="-78" font-family="Helvetica,sans-Serif" font-size="10.00">MEMORY_PERCENT</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="904.5" y="-65.25" font-family="Helvetica,sans-Serif" font-size="10.00">DISK_PERCENT</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="904.5" y="-52.5" font-family="Helvetica,sans-Serif" font-size="10.00">NETWORK_*</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="904.5" y="-39.75" font-family="Helvetica,sans-Serif" font-size="10.00">LOAD_AVG_*</text>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="951.38" y="-27" font-family="Helvetica,sans-Serif" font-size="10.00">...</text>
|
|
||||||
</g>
|
|
||||||
<!-- metric_msg->metric_type -->
|
|
||||||
<g id="edge5" class="edge">
|
|
||||||
<title>metric_msg->metric_type</title>
|
|
||||||
<path fill="none" stroke="black" stroke-dasharray="1,5" d="M692.74,-59.81C730.57,-60.18 775.71,-60.63 815.45,-61.02"/>
|
|
||||||
<polygon fill="black" stroke="black" points="815.23,-64.51 825.27,-61.11 815.3,-57.51 815.23,-64.51"/>
|
|
||||||
</g>
|
|
||||||
<!-- health_status -->
|
|
||||||
<g id="node9" class="node">
|
|
||||||
<title>health_status</title>
|
|
||||||
<polygon fill="#ce93d8" stroke="black" points="842.25,-123.12 842.25,-194.88 991.25,-194.88 991.25,-123.12 842.25,-123.12"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="881.75" y="-155.88" font-family="Helvetica,sans-Serif" font-size="10.00">HealthStatus</text>
|
|
||||||
<polyline fill="none" stroke="black" points="921.25,-123.12 921.25,-194.88"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="929.25" y="-181.38" font-family="Helvetica,sans-Serif" font-size="10.00">HEALTHY</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="929.25" y="-168.62" font-family="Helvetica,sans-Serif" font-size="10.00">WARNING</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="929.25" y="-155.88" font-family="Helvetica,sans-Serif" font-size="10.00">CRITICAL</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="929.25" y="-143.12" font-family="Helvetica,sans-Serif" font-size="10.00">UNKNOWN</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="929.25" y="-130.38" font-family="Helvetica,sans-Serif" font-size="10.00">OFFLINE</text>
|
|
||||||
</g>
|
|
||||||
<!-- machine_state->health_status -->
|
|
||||||
<g id="edge6" class="edge">
|
|
||||||
<title>machine_state->health_status</title>
|
|
||||||
<path fill="none" stroke="black" stroke-dasharray="1,5" d="M719.09,-161.57C755.76,-161.09 796.1,-160.57 830.65,-160.11"/>
|
|
||||||
<polygon fill="black" stroke="black" points="830.67,-163.61 840.62,-159.98 830.58,-156.61 830.67,-163.61"/>
|
|
||||||
</g>
|
|
||||||
<!-- control_svc -->
|
|
||||||
<g id="node4" class="node">
|
|
||||||
<title>control_svc</title>
|
|
||||||
<polygon fill="#90caf9" stroke="black" points="31.38,-261 31.38,-297 375.38,-297 375.38,-261 31.38,-261"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="75" y="-276" font-family="Helvetica,sans-Serif" font-size="10.00">ControlService</text>
|
|
||||||
<polyline fill="none" stroke="black" points="118.62,-261.25 118.62,-297"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="126.62" y="-276" font-family="Helvetica,sans-Serif" font-size="10.00">+ Control(stream Command) → stream Response</text>
|
|
||||||
</g>
|
|
||||||
<!-- commands -->
|
|
||||||
<g id="node5" class="node">
|
|
||||||
<title>commands</title>
|
|
||||||
<polygon fill="#64b5f6" stroke="black" points="426.75,-255.88 426.75,-302.12 791.25,-302.12 791.25,-255.88 426.75,-255.88"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="477.5" y="-275.88" font-family="Helvetica,sans-Serif" font-size="10.00">ControlCommand</text>
|
|
||||||
<polyline fill="none" stroke="black" points="528.25,-255.88 528.25,-302.12"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="536.25" y="-275.88" font-family="Helvetica,sans-Serif" font-size="10.00">command_id: string</text>
|
|
||||||
<polyline fill="none" stroke="black" points="641,-255.88 641,-302.12"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="649" y="-288.62" font-family="Helvetica,sans-Serif" font-size="10.00">UpdateIntervalCommand</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="649" y="-275.88" font-family="Helvetica,sans-Serif" font-size="10.00">RestartCollectionCommand</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="649" y="-263.12" font-family="Helvetica,sans-Serif" font-size="10.00">ShutdownCommand</text>
|
|
||||||
</g>
|
|
||||||
<!-- control_svc->commands -->
|
|
||||||
<g id="edge3" class="edge">
|
|
||||||
<title>control_svc->commands</title>
|
|
||||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M375.84,-279C388.79,-279 401.92,-279 414.99,-279"/>
|
|
||||||
<polygon fill="black" stroke="black" points="414.95,-282.5 424.95,-279 414.95,-275.5 414.95,-282.5"/>
|
|
||||||
</g>
|
|
||||||
<!-- config_svc -->
|
|
||||||
<g id="node6" class="node">
|
|
||||||
<title>config_svc</title>
|
|
||||||
<polygon fill="#ffe0b2" stroke="black" points="16,-364 16,-400 390.75,-400 390.75,-364 16,-364"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="57.38" y="-379.12" font-family="Helvetica,sans-Serif" font-size="10.00">ConfigService</text>
|
|
||||||
<polyline fill="none" stroke="black" points="98.75,-364.5 98.75,-400"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="106.75" y="-385.5" font-family="Helvetica,sans-Serif" font-size="10.00">+ GetConfig(ConfigRequest) → CollectorConfig</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="106.75" y="-372.75" font-family="Helvetica,sans-Serif" font-size="10.00">+ WatchConfig(ConfigRequest) → stream CollectorConfig</text>
|
|
||||||
</g>
|
|
||||||
<!-- collector_config -->
|
|
||||||
<g id="node7" class="node">
|
|
||||||
<title>collector_config</title>
|
|
||||||
<polygon fill="#ffcc80" stroke="black" points="473,-352.5 473,-411.5 745,-411.5 745,-352.5 473,-352.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="middle" x="518.12" y="-378.88" font-family="Helvetica,sans-Serif" font-size="10.00">CollectorConfig</text>
|
|
||||||
<polyline fill="none" stroke="black" points="563.25,-352.5 563.25,-411.5"/>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="571.25" y="-398" font-family="Helvetica,sans-Serif" font-size="10.00">collection_interval_seconds: int32</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="571.25" y="-385.25" font-family="Helvetica,sans-Serif" font-size="10.00">enabled_metrics: MetricType[]</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="571.25" y="-372.5" font-family="Helvetica,sans-Serif" font-size="10.00">labels: map</text>
|
|
||||||
<text xml:space="preserve" text-anchor="start" x="571.25" y="-359.75" font-family="Helvetica,sans-Serif" font-size="10.00">thresholds: ThresholdConfig[]</text>
|
|
||||||
</g>
|
|
||||||
<!-- config_svc->collector_config -->
|
|
||||||
<g id="edge4" class="edge">
|
|
||||||
<title>config_svc->collector_config</title>
|
|
||||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M391.12,-382C414.61,-382 438.36,-382 461.11,-382"/>
|
|
||||||
<polygon fill="black" stroke="black" points="461.03,-385.5 471.03,-382 461.03,-378.5 461.03,-385.5"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 13 KiB |
@@ -1,130 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Graph Viewer - System Monitor</title>
|
|
||||||
<link rel="stylesheet" href="styles.css" />
|
|
||||||
</head>
|
|
||||||
<body class="graph-viewer">
|
|
||||||
<header class="graph-header">
|
|
||||||
<a href="../index.html" class="back-link">← Index</a>
|
|
||||||
<div class="nav-controls">
|
|
||||||
<button
|
|
||||||
onclick="navigate(-1)"
|
|
||||||
id="btn-prev"
|
|
||||||
title="Previous (←)"
|
|
||||||
>
|
|
||||||
◀
|
|
||||||
</button>
|
|
||||||
<span id="nav-position">1 / 4</span>
|
|
||||||
<button onclick="navigate(1)" id="btn-next" title="Next (→)">
|
|
||||||
▶
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<h1 id="graph-title">Loading...</h1>
|
|
||||||
<div class="graph-controls">
|
|
||||||
<button onclick="setMode('fit')">Fit</button>
|
|
||||||
<button onclick="setMode('fit-width')">Width</button>
|
|
||||||
<button onclick="setMode('fit-height')">Height</button>
|
|
||||||
<button onclick="setMode('actual-size')">100%</button>
|
|
||||||
<button onclick="downloadSvg()">↓ SVG</button>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="graph-container" id="graph-container">
|
|
||||||
<img id="graph-img" src="" alt="Graph" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const graphOrder = [
|
|
||||||
"01-system-overview",
|
|
||||||
"02-data-flow",
|
|
||||||
"03-deployment",
|
|
||||||
"04-grpc-services",
|
|
||||||
];
|
|
||||||
|
|
||||||
const graphs = {
|
|
||||||
"01-system-overview": {
|
|
||||||
title: "System Overview",
|
|
||||||
file: "01-system-overview.svg",
|
|
||||||
},
|
|
||||||
"02-data-flow": {
|
|
||||||
title: "Data Flow Pipeline",
|
|
||||||
file: "02-data-flow.svg",
|
|
||||||
},
|
|
||||||
"03-deployment": {
|
|
||||||
title: "Deployment Architecture",
|
|
||||||
file: "03-deployment.svg",
|
|
||||||
},
|
|
||||||
"04-grpc-services": {
|
|
||||||
title: "gRPC Service Definitions",
|
|
||||||
file: "04-grpc-services.svg",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const params = new URLSearchParams(window.location.search);
|
|
||||||
let graphKey = params.get("g") || "01-system-overview";
|
|
||||||
let currentIndex = graphOrder.indexOf(graphKey);
|
|
||||||
if (currentIndex === -1) currentIndex = 0;
|
|
||||||
|
|
||||||
function loadGraph(key) {
|
|
||||||
const graph = graphs[key];
|
|
||||||
document.getElementById("graph-title").textContent =
|
|
||||||
graph.title;
|
|
||||||
document.getElementById("graph-img").src = graph.file;
|
|
||||||
document.title = graph.title + " - System Monitor";
|
|
||||||
history.replaceState(null, "", "?g=" + key);
|
|
||||||
graphKey = key;
|
|
||||||
updateNavHints();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateNavHints() {
|
|
||||||
const idx = graphOrder.indexOf(graphKey);
|
|
||||||
const prevBtn = document.getElementById("btn-prev");
|
|
||||||
const nextBtn = document.getElementById("btn-next");
|
|
||||||
prevBtn.disabled = idx === 0;
|
|
||||||
nextBtn.disabled = idx === graphOrder.length - 1;
|
|
||||||
document.getElementById("nav-position").textContent =
|
|
||||||
idx + 1 + " / " + graphOrder.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
function navigate(direction) {
|
|
||||||
const idx = graphOrder.indexOf(graphKey);
|
|
||||||
const newIdx = idx + direction;
|
|
||||||
if (newIdx >= 0 && newIdx < graphOrder.length) {
|
|
||||||
currentIndex = newIdx;
|
|
||||||
loadGraph(graphOrder[newIdx]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setMode(mode) {
|
|
||||||
const container = document.getElementById("graph-container");
|
|
||||||
container.className = "graph-container " + mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
function downloadSvg() {
|
|
||||||
const graph = graphs[graphKey];
|
|
||||||
const link = document.createElement("a");
|
|
||||||
link.href = graph.file;
|
|
||||||
link.download = graph.file;
|
|
||||||
link.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keyboard navigation
|
|
||||||
document.addEventListener("keydown", (e) => {
|
|
||||||
if (e.key === "ArrowLeft") {
|
|
||||||
navigate(-1);
|
|
||||||
} else if (e.key === "ArrowRight") {
|
|
||||||
navigate(1);
|
|
||||||
} else if (e.key === "Escape") {
|
|
||||||
window.location.href = "../index.html";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initialize
|
|
||||||
loadGraph(graphOrder[currentIndex]);
|
|
||||||
setMode("fit");
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,262 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<meta http-equiv="refresh" content="0; url=../index.html" />
|
|
||||||
<title>System Monitor - Redirecting...</title>
|
|
||||||
<link rel="stylesheet" href="styles.css" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<h1>System Monitoring Platform</h1>
|
|
||||||
<p class="subtitle">Architecture & Design Documentation</p>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<section class="graph-section" id="overview">
|
|
||||||
<div class="graph-header-row">
|
|
||||||
<h2>System Overview</h2>
|
|
||||||
<a href="graph.html?g=01-system-overview" class="view-btn"
|
|
||||||
>View Full</a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<a href="graph.html?g=01-system-overview" class="graph-preview">
|
|
||||||
<img src="01-system-overview.svg" alt="System Overview" />
|
|
||||||
</a>
|
|
||||||
<div class="graph-details">
|
|
||||||
<p>
|
|
||||||
High-level architecture showing all services, data
|
|
||||||
stores, and communication patterns.
|
|
||||||
</p>
|
|
||||||
<h4>Key Components</h4>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<strong>Collector</strong>: Runs on each monitored
|
|
||||||
machine, streams metrics via gRPC
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Aggregator</strong>: Central gRPC server,
|
|
||||||
receives streams, normalizes data
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Gateway</strong>: FastAPI service, WebSocket
|
|
||||||
for browser, REST for queries
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Alerts</strong>: Subscribes to events,
|
|
||||||
evaluates thresholds, triggers actions
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="graph-section" id="data-flow">
|
|
||||||
<div class="graph-header-row">
|
|
||||||
<h2>Data Flow Pipeline</h2>
|
|
||||||
<a href="graph.html?g=02-data-flow" class="view-btn"
|
|
||||||
>View Full</a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<a href="graph.html?g=02-data-flow" class="graph-preview">
|
|
||||||
<img src="02-data-flow.svg" alt="Data Flow" />
|
|
||||||
</a>
|
|
||||||
<div class="graph-details">
|
|
||||||
<p>
|
|
||||||
How metrics flow from collection through storage with
|
|
||||||
different retention tiers.
|
|
||||||
</p>
|
|
||||||
<h4>Storage Tiers</h4>
|
|
||||||
<table class="details-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Tier</th>
|
|
||||||
<th>Resolution</th>
|
|
||||||
<th>Retention</th>
|
|
||||||
<th>Use Case</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>Hot (Redis)</td>
|
|
||||||
<td>5s</td>
|
|
||||||
<td>5 min</td>
|
|
||||||
<td>Current state, live dashboard</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Raw (TimescaleDB)</td>
|
|
||||||
<td>5s</td>
|
|
||||||
<td>24h</td>
|
|
||||||
<td>Recent detailed analysis</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>1-min Aggregates</td>
|
|
||||||
<td>1m</td>
|
|
||||||
<td>7d</td>
|
|
||||||
<td>Week view, trends</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>1-hour Aggregates</td>
|
|
||||||
<td>1h</td>
|
|
||||||
<td>90d</td>
|
|
||||||
<td>Long-term analysis</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="graph-section" id="deployment">
|
|
||||||
<div class="graph-header-row">
|
|
||||||
<h2>Deployment Architecture</h2>
|
|
||||||
<a href="graph.html?g=03-deployment" class="view-btn"
|
|
||||||
>View Full</a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<a href="graph.html?g=03-deployment" class="graph-preview">
|
|
||||||
<img src="03-deployment.svg" alt="Deployment" />
|
|
||||||
</a>
|
|
||||||
<div class="graph-details">
|
|
||||||
<p>
|
|
||||||
Deployment options from local development to AWS
|
|
||||||
production.
|
|
||||||
</p>
|
|
||||||
<h4>Environments</h4>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<strong>Local Dev</strong>: Kind + Tilt for K8s, or
|
|
||||||
Docker Compose
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Demo (EC2)</strong>: Docker Compose on
|
|
||||||
t2.small at sysmonstm.mcrn.ar
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Lambda Pipeline</strong>: SQS-triggered
|
|
||||||
aggregation for data processing experience
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="graph-section" id="grpc">
|
|
||||||
<div class="graph-header-row">
|
|
||||||
<h2>gRPC Service Definitions</h2>
|
|
||||||
<a href="graph.html?g=04-grpc-services" class="view-btn"
|
|
||||||
>View Full</a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<a href="graph.html?g=04-grpc-services" class="graph-preview">
|
|
||||||
<img src="04-grpc-services.svg" alt="gRPC Services" />
|
|
||||||
</a>
|
|
||||||
<div class="graph-details">
|
|
||||||
<p>Protocol Buffer service and message definitions.</p>
|
|
||||||
<h4>Services</h4>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<strong>MetricsService</strong>: Client-side
|
|
||||||
streaming for metrics ingestion
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>ControlService</strong>: Bidirectional
|
|
||||||
streaming for collector control
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>ConfigService</strong>: Server-side
|
|
||||||
streaming for config updates
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="findings-section">
|
|
||||||
<h2>Interview Talking Points</h2>
|
|
||||||
<div class="findings-grid">
|
|
||||||
<article class="finding-card">
|
|
||||||
<h3>Domain Mapping</h3>
|
|
||||||
<ul>
|
|
||||||
<li>Machine = Payment Processor</li>
|
|
||||||
<li>Metrics Stream = Transaction Stream</li>
|
|
||||||
<li>Thresholds = Fraud Detection</li>
|
|
||||||
<li>Aggregator = Payment Hub</li>
|
|
||||||
</ul>
|
|
||||||
</article>
|
|
||||||
<article class="finding-card">
|
|
||||||
<h3>gRPC Patterns</h3>
|
|
||||||
<ul>
|
|
||||||
<li>Client streaming (metrics)</li>
|
|
||||||
<li>Server streaming (config)</li>
|
|
||||||
<li>Bidirectional (control)</li>
|
|
||||||
<li>Health checking</li>
|
|
||||||
</ul>
|
|
||||||
</article>
|
|
||||||
<article class="finding-card">
|
|
||||||
<h3>Event-Driven</h3>
|
|
||||||
<ul>
|
|
||||||
<li>Redis Pub/Sub (current)</li>
|
|
||||||
<li>Abstraction for Kafka switch</li>
|
|
||||||
<li>Decoupled alert processing</li>
|
|
||||||
<li>Real-time WebSocket push</li>
|
|
||||||
</ul>
|
|
||||||
</article>
|
|
||||||
<article class="finding-card">
|
|
||||||
<h3>Resilience</h3>
|
|
||||||
<ul>
|
|
||||||
<li>Collectors are independent</li>
|
|
||||||
<li>Graceful degradation</li>
|
|
||||||
<li>Retry with backoff</li>
|
|
||||||
<li>Health checks everywhere</li>
|
|
||||||
</ul>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="tech-section">
|
|
||||||
<h2>Technology Stack</h2>
|
|
||||||
<div class="tech-grid">
|
|
||||||
<div class="tech-column">
|
|
||||||
<h3>Core</h3>
|
|
||||||
<ul>
|
|
||||||
<li>Python 3.11+</li>
|
|
||||||
<li>FastAPI</li>
|
|
||||||
<li>gRPC / protobuf</li>
|
|
||||||
<li>asyncio</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="tech-column">
|
|
||||||
<h3>Data</h3>
|
|
||||||
<ul>
|
|
||||||
<li>TimescaleDB</li>
|
|
||||||
<li>Redis</li>
|
|
||||||
<li>Redis Pub/Sub</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="tech-column">
|
|
||||||
<h3>Infrastructure</h3>
|
|
||||||
<ul>
|
|
||||||
<li>Docker</li>
|
|
||||||
<li>Kubernetes</li>
|
|
||||||
<li>Kind + Tilt</li>
|
|
||||||
<li>Terraform</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="tech-column">
|
|
||||||
<h3>CI/CD</h3>
|
|
||||||
<ul>
|
|
||||||
<li>Woodpecker CI</li>
|
|
||||||
<li>Kustomize</li>
|
|
||||||
<li>Container Registry</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
<p>System Monitoring Platform - Architecture Documentation</p>
|
|
||||||
<p class="date">
|
|
||||||
Generated: <time datetime="2025-12-29">December 2025</time>
|
|
||||||
</p>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,343 +0,0 @@
|
|||||||
:root {
|
|
||||||
--bg-primary: #1a1a2e;
|
|
||||||
--bg-secondary: #16213e;
|
|
||||||
--bg-card: #0f3460;
|
|
||||||
--text-primary: #eee;
|
|
||||||
--text-secondary: #a0a0a0;
|
|
||||||
--accent: #e94560;
|
|
||||||
--accent-secondary: #533483;
|
|
||||||
--border: #2a2a4a;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
|
|
||||||
background: var(--bg-primary);
|
|
||||||
color: var(--text-primary);
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
background: linear-gradient(135deg, var(--bg-secondary), var(--accent-secondary));
|
|
||||||
padding: 2rem;
|
|
||||||
text-align: center;
|
|
||||||
border-bottom: 2px solid var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
header h1 {
|
|
||||||
font-size: 2rem;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .subtitle {
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
max-width: 1400px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Graph sections */
|
|
||||||
.graph-section {
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 1.5rem;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-header-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-header-row h2 {
|
|
||||||
font-size: 1.25rem;
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-btn {
|
|
||||||
background: var(--accent);
|
|
||||||
color: white;
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
text-decoration: none;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
transition: opacity 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-btn:hover {
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-preview {
|
|
||||||
display: block;
|
|
||||||
background: white;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 1rem;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
overflow: auto;
|
|
||||||
max-height: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-preview img {
|
|
||||||
max-width: 100%;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-details {
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-details h4 {
|
|
||||||
color: var(--text-primary);
|
|
||||||
margin: 1rem 0 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-details ul {
|
|
||||||
margin-left: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-details li {
|
|
||||||
margin-bottom: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tech section */
|
|
||||||
.tech-section {
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 1.5rem;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tech-section h2 {
|
|
||||||
color: var(--accent);
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tech-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
||||||
gap: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tech-column h3 {
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-size: 1rem;
|
|
||||||
margin-bottom: 0.75rem;
|
|
||||||
padding-bottom: 0.5rem;
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tech-column ul {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tech-column li {
|
|
||||||
padding: 0.25rem 0;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Findings */
|
|
||||||
.findings-section {
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.findings-section h2 {
|
|
||||||
color: var(--accent);
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.findings-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.finding-card {
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 1.25rem;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.finding-card h3 {
|
|
||||||
color: var(--accent);
|
|
||||||
font-size: 1rem;
|
|
||||||
margin-bottom: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.finding-card ul {
|
|
||||||
margin-left: 1rem;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.finding-card code {
|
|
||||||
background: var(--bg-primary);
|
|
||||||
padding: 0.125rem 0.375rem;
|
|
||||||
border-radius: 3px;
|
|
||||||
font-size: 0.85em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Footer */
|
|
||||||
footer {
|
|
||||||
text-align: center;
|
|
||||||
padding: 2rem;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
border-top: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
footer .date {
|
|
||||||
font-size: 0.85rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Graph viewer page */
|
|
||||||
body.graph-viewer {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 1rem;
|
|
||||||
padding: 0.75rem 1rem;
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-link {
|
|
||||||
color: var(--accent);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-controls {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-controls button {
|
|
||||||
background: var(--bg-card);
|
|
||||||
color: var(--text-primary);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
padding: 0.25rem 0.75rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-controls button:disabled {
|
|
||||||
opacity: 0.3;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
#nav-position {
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-size: 0.85rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-header h1 {
|
|
||||||
flex: 1;
|
|
||||||
font-size: 1rem;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-controls {
|
|
||||||
display: flex;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-controls button {
|
|
||||||
background: var(--bg-card);
|
|
||||||
color: var(--text-primary);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
padding: 0.375rem 0.75rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-controls button:hover {
|
|
||||||
background: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-container {
|
|
||||||
flex: 1;
|
|
||||||
overflow: auto;
|
|
||||||
background: white;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: flex-start;
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-container.fit img {
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: calc(100vh - 60px);
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-container.fit-width img {
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-container.fit-height img {
|
|
||||||
height: calc(100vh - 60px);
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-container.actual-size img {
|
|
||||||
/* No constraints */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tables */
|
|
||||||
.details-table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
margin: 1rem 0;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.details-table th,
|
|
||||||
.details-table td {
|
|
||||||
padding: 0.5rem;
|
|
||||||
text-align: left;
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.details-table th {
|
|
||||||
color: var(--text-primary);
|
|
||||||
background: var(--bg-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.details-table td {
|
|
||||||
color: var(--text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.details-table code {
|
|
||||||
background: var(--bg-primary);
|
|
||||||
padding: 0.125rem 0.375rem;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note {
|
|
||||||
font-style: italic;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
}
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 400">
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="headerGrad" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
||||||
<stop offset="0%" style="stop-color:#16213e"/>
|
|
||||||
<stop offset="100%" style="stop-color:#533483"/>
|
|
||||||
</linearGradient>
|
|
||||||
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
|
||||||
<polygon points="0 0, 10 3.5, 0 7" fill="#e94560"/>
|
|
||||||
</marker>
|
|
||||||
<marker id="arrowhead-gray" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
|
||||||
<polygon points="0 0, 10 3.5, 0 7" fill="#64748b"/>
|
|
||||||
</marker>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- Background -->
|
|
||||||
<rect width="800" height="400" fill="#1a1a2e"/>
|
|
||||||
|
|
||||||
<!-- Title -->
|
|
||||||
<text x="400" y="30" text-anchor="middle" fill="#e94560" font-family="system-ui" font-size="18" font-weight="bold">System Architecture Overview</text>
|
|
||||||
|
|
||||||
<!-- Machine boxes (left side) -->
|
|
||||||
<g transform="translate(50, 70)">
|
|
||||||
<rect width="120" height="60" rx="8" fill="#0f3460" stroke="#e94560" stroke-width="2"/>
|
|
||||||
<text x="60" y="25" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="12" font-weight="bold">Machine 1</text>
|
|
||||||
<text x="60" y="42" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="10">Collector</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(50, 150)">
|
|
||||||
<rect width="120" height="60" rx="8" fill="#0f3460" stroke="#e94560" stroke-width="2"/>
|
|
||||||
<text x="60" y="25" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="12" font-weight="bold">Machine 2</text>
|
|
||||||
<text x="60" y="42" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="10">Collector</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(50, 230)">
|
|
||||||
<rect width="120" height="60" rx="8" fill="#0f3460" stroke="#e94560" stroke-width="2"/>
|
|
||||||
<text x="60" y="25" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="12" font-weight="bold">Machine N</text>
|
|
||||||
<text x="60" y="42" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="10">Collector</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Aggregator (center) -->
|
|
||||||
<g transform="translate(280, 130)">
|
|
||||||
<rect width="140" height="80" rx="8" fill="#16213e" stroke="#e94560" stroke-width="2"/>
|
|
||||||
<text x="70" y="30" text-anchor="middle" fill="#e94560" font-family="system-ui" font-size="14" font-weight="bold">Aggregator</text>
|
|
||||||
<text x="70" y="50" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="10">gRPC Server</text>
|
|
||||||
<text x="70" y="65" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="10">Batch Processing</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Storage (bottom center) -->
|
|
||||||
<g transform="translate(250, 300)">
|
|
||||||
<rect width="80" height="50" rx="6" fill="#0f3460" stroke="#64748b" stroke-width="1"/>
|
|
||||||
<text x="40" y="22" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="11" font-weight="bold">Redis</text>
|
|
||||||
<text x="40" y="38" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="9">Hot Data</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(350, 300)">
|
|
||||||
<rect width="100" height="50" rx="6" fill="#0f3460" stroke="#64748b" stroke-width="1"/>
|
|
||||||
<text x="50" y="22" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="11" font-weight="bold">TimescaleDB</text>
|
|
||||||
<text x="50" y="38" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="9">Historical</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Gateway (right) -->
|
|
||||||
<g transform="translate(520, 130)">
|
|
||||||
<rect width="120" height="80" rx="8" fill="#16213e" stroke="#e94560" stroke-width="2"/>
|
|
||||||
<text x="60" y="30" text-anchor="middle" fill="#e94560" font-family="system-ui" font-size="14" font-weight="bold">Gateway</text>
|
|
||||||
<text x="60" y="50" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="10">FastAPI</text>
|
|
||||||
<text x="60" y="65" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="10">WebSocket</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Browser (far right) -->
|
|
||||||
<g transform="translate(700, 140)">
|
|
||||||
<rect width="80" height="60" rx="8" fill="#0f3460" stroke="#4ade80" stroke-width="2"/>
|
|
||||||
<text x="40" y="25" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="11" font-weight="bold">Browser</text>
|
|
||||||
<text x="40" y="42" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="9">Dashboard</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Alerts (bottom right) -->
|
|
||||||
<g transform="translate(540, 300)">
|
|
||||||
<rect width="100" height="50" rx="6" fill="#0f3460" stroke="#fbbf24" stroke-width="1"/>
|
|
||||||
<text x="50" y="22" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="11" font-weight="bold">Alerts</text>
|
|
||||||
<text x="50" y="38" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="9">Threshold Rules</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Arrows: Collectors to Aggregator -->
|
|
||||||
<line x1="170" y1="100" x2="275" y2="160" stroke="#e94560" stroke-width="2" marker-end="url(#arrowhead)"/>
|
|
||||||
<line x1="170" y1="180" x2="275" y2="170" stroke="#e94560" stroke-width="2" marker-end="url(#arrowhead)"/>
|
|
||||||
<line x1="170" y1="260" x2="275" y2="185" stroke="#e94560" stroke-width="2" marker-end="url(#arrowhead)"/>
|
|
||||||
|
|
||||||
<!-- Arrow: Aggregator to Storage -->
|
|
||||||
<line x1="320" y1="210" x2="300" y2="295" stroke="#64748b" stroke-width="1.5" marker-end="url(#arrowhead-gray)"/>
|
|
||||||
<line x1="380" y1="210" x2="400" y2="295" stroke="#64748b" stroke-width="1.5" marker-end="url(#arrowhead-gray)"/>
|
|
||||||
|
|
||||||
<!-- Arrow: Aggregator to Gateway -->
|
|
||||||
<line x1="420" y1="170" x2="515" y2="170" stroke="#e94560" stroke-width="2" marker-end="url(#arrowhead)"/>
|
|
||||||
|
|
||||||
<!-- Arrow: Gateway to Browser -->
|
|
||||||
<line x1="640" y1="170" x2="695" y2="170" stroke="#4ade80" stroke-width="2" marker-end="url(#arrowhead)"/>
|
|
||||||
|
|
||||||
<!-- Arrow: Event stream to Alerts -->
|
|
||||||
<path d="M 400 210 Q 450 260 535 325" stroke="#fbbf24" stroke-width="1.5" fill="none" marker-end="url(#arrowhead)"/>
|
|
||||||
|
|
||||||
<!-- Labels for arrows -->
|
|
||||||
<text x="220" y="125" fill="#e94560" font-family="system-ui" font-size="9">gRPC Stream</text>
|
|
||||||
<text x="460" y="155" fill="#e94560" font-family="system-ui" font-size="9">Events</text>
|
|
||||||
<text x="660" y="155" fill="#4ade80" font-family="system-ui" font-size="9">WS</text>
|
|
||||||
<text x="470" y="280" fill="#fbbf24" font-family="system-ui" font-size="9">Pub/Sub</text>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 6.1 KiB |
@@ -1,83 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 300">
|
|
||||||
<defs>
|
|
||||||
<marker id="arr" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
|
||||||
<polygon points="0 0, 10 3.5, 0 7" fill="#e94560"/>
|
|
||||||
</marker>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- Background -->
|
|
||||||
<rect width="700" height="300" fill="#1a1a2e"/>
|
|
||||||
|
|
||||||
<!-- Title -->
|
|
||||||
<text x="350" y="30" text-anchor="middle" fill="#e94560" font-family="system-ui" font-size="16" font-weight="bold">gRPC Client-Side Streaming</text>
|
|
||||||
|
|
||||||
<!-- Collector box -->
|
|
||||||
<g transform="translate(50, 80)">
|
|
||||||
<rect width="180" height="160" rx="8" fill="#16213e" stroke="#e94560" stroke-width="2"/>
|
|
||||||
<text x="90" y="25" text-anchor="middle" fill="#e94560" font-family="system-ui" font-size="13" font-weight="bold">Collector</text>
|
|
||||||
|
|
||||||
<!-- Metric items -->
|
|
||||||
<g transform="translate(15, 45)">
|
|
||||||
<rect width="150" height="25" rx="4" fill="#0f3460"/>
|
|
||||||
<text x="10" y="17" fill="#a0a0a0" font-family="monospace" font-size="10">CPU: 45%</text>
|
|
||||||
</g>
|
|
||||||
<g transform="translate(15, 75)">
|
|
||||||
<rect width="150" height="25" rx="4" fill="#0f3460"/>
|
|
||||||
<text x="10" y="17" fill="#a0a0a0" font-family="monospace" font-size="10">Memory: 62%</text>
|
|
||||||
</g>
|
|
||||||
<g transform="translate(15, 105)">
|
|
||||||
<rect width="150" height="25" rx="4" fill="#0f3460"/>
|
|
||||||
<text x="10" y="17" fill="#a0a0a0" font-family="monospace" font-size="10">Disk: 78%</text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Stream visualization -->
|
|
||||||
<g transform="translate(250, 100)">
|
|
||||||
<!-- Stream line -->
|
|
||||||
<line x1="0" y1="60" x2="180" y2="60" stroke="#e94560" stroke-width="3" stroke-dasharray="8,4"/>
|
|
||||||
|
|
||||||
<!-- Metric packets -->
|
|
||||||
<g transform="translate(20, 45)">
|
|
||||||
<rect width="30" height="30" rx="4" fill="#e94560"/>
|
|
||||||
<text x="15" y="20" text-anchor="middle" fill="#fff" font-family="monospace" font-size="10">M1</text>
|
|
||||||
</g>
|
|
||||||
<g transform="translate(70, 45)">
|
|
||||||
<rect width="30" height="30" rx="4" fill="#e94560" opacity="0.8"/>
|
|
||||||
<text x="15" y="20" text-anchor="middle" fill="#fff" font-family="monospace" font-size="10">M2</text>
|
|
||||||
</g>
|
|
||||||
<g transform="translate(120, 45)">
|
|
||||||
<rect width="30" height="30" rx="4" fill="#e94560" opacity="0.6"/>
|
|
||||||
<text x="15" y="20" text-anchor="middle" fill="#fff" font-family="monospace" font-size="10">M3</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<text x="90" y="110" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="10">Continuous stream of metrics</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Aggregator box -->
|
|
||||||
<g transform="translate(450, 80)">
|
|
||||||
<rect width="180" height="160" rx="8" fill="#16213e" stroke="#e94560" stroke-width="2"/>
|
|
||||||
<text x="90" y="25" text-anchor="middle" fill="#e94560" font-family="system-ui" font-size="13" font-weight="bold">Aggregator</text>
|
|
||||||
|
|
||||||
<!-- Batch indicator -->
|
|
||||||
<g transform="translate(15, 45)">
|
|
||||||
<rect width="150" height="40" rx="4" fill="#0f3460"/>
|
|
||||||
<text x="75" y="17" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="10">Batch: 20 metrics</text>
|
|
||||||
<text x="75" y="32" text-anchor="middle" fill="#4ade80" font-family="system-ui" font-size="9">Flush to storage</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Storage icons -->
|
|
||||||
<g transform="translate(15, 100)">
|
|
||||||
<rect width="65" height="30" rx="4" fill="#0f3460"/>
|
|
||||||
<text x="32" y="20" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="9">Redis</text>
|
|
||||||
</g>
|
|
||||||
<g transform="translate(95, 100)">
|
|
||||||
<rect width="70" height="30" rx="4" fill="#0f3460"/>
|
|
||||||
<text x="35" y="20" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="9">Timescale</text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Legend -->
|
|
||||||
<g transform="translate(50, 260)">
|
|
||||||
<text x="0" y="0" fill="#a0a0a0" font-family="system-ui" font-size="11">One persistent connection. Metrics flow continuously. No polling overhead.</text>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 3.8 KiB |
@@ -1,83 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 320">
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="hotGrad" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
||||||
<stop offset="0%" style="stop-color:#ef4444"/>
|
|
||||||
<stop offset="100%" style="stop-color:#f97316"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="warmGrad" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
||||||
<stop offset="0%" style="stop-color:#f97316"/>
|
|
||||||
<stop offset="100%" style="stop-color:#eab308"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="coldGrad" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
||||||
<stop offset="0%" style="stop-color:#3b82f6"/>
|
|
||||||
<stop offset="100%" style="stop-color:#6366f1"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- Background -->
|
|
||||||
<rect width="700" height="320" fill="#1a1a2e"/>
|
|
||||||
|
|
||||||
<!-- Title -->
|
|
||||||
<text x="350" y="30" text-anchor="middle" fill="#e94560" font-family="system-ui" font-size="16" font-weight="bold">Tiered Storage Architecture</text>
|
|
||||||
|
|
||||||
<!-- Hot tier -->
|
|
||||||
<g transform="translate(50, 60)">
|
|
||||||
<rect width="180" height="100" rx="8" fill="#16213e" stroke="url(#hotGrad)" stroke-width="3"/>
|
|
||||||
<rect x="10" y="10" width="30" height="10" rx="2" fill="url(#hotGrad)"/>
|
|
||||||
<text x="50" y="18" fill="#ef4444" font-family="system-ui" font-size="11" font-weight="bold">HOT</text>
|
|
||||||
<text x="90" y="45" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="14" font-weight="bold">Redis</text>
|
|
||||||
<text x="90" y="65" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="10">5 second resolution</text>
|
|
||||||
<text x="90" y="80" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="10">5 minute retention</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Warm tier - Raw -->
|
|
||||||
<g transform="translate(260, 60)">
|
|
||||||
<rect width="180" height="100" rx="8" fill="#16213e" stroke="url(#warmGrad)" stroke-width="3"/>
|
|
||||||
<rect x="10" y="10" width="40" height="10" rx="2" fill="url(#warmGrad)"/>
|
|
||||||
<text x="58" y="18" fill="#f97316" font-family="system-ui" font-size="11" font-weight="bold">WARM</text>
|
|
||||||
<text x="90" y="45" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="14" font-weight="bold">TimescaleDB</text>
|
|
||||||
<text x="90" y="65" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="10">5 second resolution</text>
|
|
||||||
<text x="90" y="80" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="10">24 hour retention</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Cold tier - Aggregates -->
|
|
||||||
<g transform="translate(470, 60)">
|
|
||||||
<rect width="180" height="100" rx="8" fill="#16213e" stroke="url(#coldGrad)" stroke-width="3"/>
|
|
||||||
<rect x="10" y="10" width="40" height="10" rx="2" fill="url(#coldGrad)"/>
|
|
||||||
<text x="58" y="18" fill="#3b82f6" font-family="system-ui" font-size="11" font-weight="bold">COLD</text>
|
|
||||||
<text x="90" y="45" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="14" font-weight="bold">Aggregates</text>
|
|
||||||
<text x="90" y="65" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="10">1 min / 1 hour resolution</text>
|
|
||||||
<text x="90" y="80" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="10">7 / 90 day retention</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Use cases -->
|
|
||||||
<g transform="translate(50, 180)">
|
|
||||||
<rect width="180" height="60" rx="6" fill="#0f3460"/>
|
|
||||||
<text x="90" y="20" text-anchor="middle" fill="#ef4444" font-family="system-ui" font-size="11" font-weight="bold">Dashboard</text>
|
|
||||||
<text x="90" y="38" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="10">"What's the CPU now?"</text>
|
|
||||||
<text x="90" y="52" text-anchor="middle" fill="#4ade80" font-family="system-ui" font-size="9">Fast in-memory reads</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(260, 180)">
|
|
||||||
<rect width="180" height="60" rx="6" fill="#0f3460"/>
|
|
||||||
<text x="90" y="20" text-anchor="middle" fill="#f97316" font-family="system-ui" font-size="11" font-weight="bold">Recent Graphs</text>
|
|
||||||
<text x="90" y="38" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="10">"Last hour of metrics"</text>
|
|
||||||
<text x="90" y="52" text-anchor="middle" fill="#4ade80" font-family="system-ui" font-size="9">Hypertable queries</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(470, 180)">
|
|
||||||
<rect width="180" height="60" rx="6" fill="#0f3460"/>
|
|
||||||
<text x="90" y="20" text-anchor="middle" fill="#3b82f6" font-family="system-ui" font-size="11" font-weight="bold">Trends / Reports</text>
|
|
||||||
<text x="90" y="38" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="10">"Weekly CPU average"</text>
|
|
||||||
<text x="90" y="52" text-anchor="middle" fill="#4ade80" font-family="system-ui" font-size="9">Pre-aggregated data</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Arrow showing flow -->
|
|
||||||
<g transform="translate(50, 260)">
|
|
||||||
<line x1="90" y1="15" x2="560" y2="15" stroke="#64748b" stroke-width="2"/>
|
|
||||||
<polygon points="560,10 580,15 560,20" fill="#64748b"/>
|
|
||||||
<text x="90" y="40" fill="#ef4444" font-family="system-ui" font-size="10">Fastest</text>
|
|
||||||
<text x="320" y="40" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="10">Access Time</text>
|
|
||||||
<text x="550" y="40" text-anchor="end" fill="#3b82f6" font-family="system-ui" font-size="10">Slowest</text>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 5.1 KiB |
@@ -1,84 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 280">
|
|
||||||
<defs>
|
|
||||||
<marker id="arrPink" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
|
||||||
<polygon points="0 0, 8 3, 0 6" fill="#e94560"/>
|
|
||||||
</marker>
|
|
||||||
<marker id="arrYellow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
|
||||||
<polygon points="0 0, 8 3, 0 6" fill="#fbbf24"/>
|
|
||||||
</marker>
|
|
||||||
<marker id="arrGreen" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
|
||||||
<polygon points="0 0, 8 3, 0 6" fill="#4ade80"/>
|
|
||||||
</marker>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- Background -->
|
|
||||||
<rect width="700" height="280" fill="#1a1a2e"/>
|
|
||||||
|
|
||||||
<!-- Title -->
|
|
||||||
<text x="350" y="28" text-anchor="middle" fill="#e94560" font-family="system-ui" font-size="16" font-weight="bold">Event-Driven Architecture</text>
|
|
||||||
|
|
||||||
<!-- Aggregator (publisher) -->
|
|
||||||
<g transform="translate(50, 70)">
|
|
||||||
<rect width="120" height="70" rx="8" fill="#16213e" stroke="#e94560" stroke-width="2"/>
|
|
||||||
<text x="60" y="30" text-anchor="middle" fill="#e94560" font-family="system-ui" font-size="12" font-weight="bold">Aggregator</text>
|
|
||||||
<text x="60" y="50" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="10">Publisher</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Event Stream (center) -->
|
|
||||||
<g transform="translate(230, 55)">
|
|
||||||
<rect width="200" height="100" rx="8" fill="#0f3460" stroke="#fbbf24" stroke-width="2"/>
|
|
||||||
<text x="100" y="25" text-anchor="middle" fill="#fbbf24" font-family="system-ui" font-size="13" font-weight="bold">Event Stream</text>
|
|
||||||
<text x="100" y="45" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="10">Redis Pub/Sub</text>
|
|
||||||
|
|
||||||
<!-- Topics -->
|
|
||||||
<g transform="translate(15, 55)">
|
|
||||||
<rect width="80" height="22" rx="4" fill="#16213e"/>
|
|
||||||
<text x="40" y="15" text-anchor="middle" fill="#e94560" font-family="monospace" font-size="9">metrics.raw</text>
|
|
||||||
</g>
|
|
||||||
<g transform="translate(105, 55)">
|
|
||||||
<rect width="80" height="22" rx="4" fill="#16213e"/>
|
|
||||||
<text x="40" y="15" text-anchor="middle" fill="#fbbf24" font-family="monospace" font-size="9">alerts.*</text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Subscribers -->
|
|
||||||
<g transform="translate(490, 50)">
|
|
||||||
<rect width="130" height="50" rx="6" fill="#16213e" stroke="#4ade80" stroke-width="2"/>
|
|
||||||
<text x="65" y="22" text-anchor="middle" fill="#4ade80" font-family="system-ui" font-size="11" font-weight="bold">Gateway</text>
|
|
||||||
<text x="65" y="38" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="9">WebSocket push</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(490, 115)">
|
|
||||||
<rect width="130" height="50" rx="6" fill="#16213e" stroke="#fbbf24" stroke-width="2"/>
|
|
||||||
<text x="65" y="22" text-anchor="middle" fill="#fbbf24" font-family="system-ui" font-size="11" font-weight="bold">Alerts</text>
|
|
||||||
<text x="65" y="38" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="9">Threshold check</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Arrows -->
|
|
||||||
<line x1="170" y1="105" x2="225" y2="105" stroke="#e94560" stroke-width="2" marker-end="url(#arrPink)"/>
|
|
||||||
<line x1="430" y1="85" x2="485" y2="75" stroke="#4ade80" stroke-width="2" marker-end="url(#arrGreen)"/>
|
|
||||||
<line x1="430" y1="115" x2="485" y2="140" stroke="#fbbf24" stroke-width="2" marker-end="url(#arrYellow)"/>
|
|
||||||
|
|
||||||
<!-- Benefits box -->
|
|
||||||
<g transform="translate(50, 180)">
|
|
||||||
<rect width="600" height="80" rx="8" fill="#0f3460"/>
|
|
||||||
<text x="20" y="25" fill="#eee" font-family="system-ui" font-size="12" font-weight="bold">Benefits:</text>
|
|
||||||
|
|
||||||
<g transform="translate(20, 40)">
|
|
||||||
<circle cx="6" cy="6" r="4" fill="#4ade80"/>
|
|
||||||
<text x="16" y="10" fill="#a0a0a0" font-family="system-ui" font-size="10">Decoupled services - can restart independently</text>
|
|
||||||
</g>
|
|
||||||
<g transform="translate(20, 58)">
|
|
||||||
<circle cx="6" cy="6" r="4" fill="#4ade80"/>
|
|
||||||
<text x="16" y="10" fill="#a0a0a0" font-family="system-ui" font-size="10">Easy to add new subscribers without changing publisher</text>
|
|
||||||
</g>
|
|
||||||
<g transform="translate(320, 40)">
|
|
||||||
<circle cx="6" cy="6" r="4" fill="#4ade80"/>
|
|
||||||
<text x="16" y="10" fill="#a0a0a0" font-family="system-ui" font-size="10">Abstraction allows switching backends (Kafka)</text>
|
|
||||||
</g>
|
|
||||||
<g transform="translate(320, 58)">
|
|
||||||
<circle cx="6" cy="6" r="4" fill="#4ade80"/>
|
|
||||||
<text x="16" y="10" fill="#a0a0a0" font-family="system-ui" font-size="10">Natural audit trail of all events</text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 4.3 KiB |
@@ -1,80 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 350">
|
|
||||||
<defs>
|
|
||||||
<marker id="arrRight" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
|
||||||
<polygon points="0 0, 10 3.5, 0 7" fill="#e94560"/>
|
|
||||||
</marker>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- Background -->
|
|
||||||
<rect width="700" height="350" fill="#1a1a2e"/>
|
|
||||||
|
|
||||||
<!-- Title -->
|
|
||||||
<text x="350" y="30" text-anchor="middle" fill="#e94560" font-family="system-ui" font-size="16" font-weight="bold">Domain Mapping: Monitoring to Payments</text>
|
|
||||||
|
|
||||||
<!-- Left column header -->
|
|
||||||
<g transform="translate(50, 55)">
|
|
||||||
<rect width="250" height="35" rx="6" fill="#16213e" stroke="#e94560" stroke-width="2"/>
|
|
||||||
<text x="125" y="23" text-anchor="middle" fill="#e94560" font-family="system-ui" font-size="13" font-weight="bold">System Monitoring</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Right column header -->
|
|
||||||
<g transform="translate(400, 55)">
|
|
||||||
<rect width="250" height="35" rx="6" fill="#16213e" stroke="#4ade80" stroke-width="2"/>
|
|
||||||
<text x="125" y="23" text-anchor="middle" fill="#4ade80" font-family="system-ui" font-size="13" font-weight="bold">Payment Processing</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Row 1 -->
|
|
||||||
<g transform="translate(50, 105)">
|
|
||||||
<rect width="250" height="40" rx="4" fill="#0f3460"/>
|
|
||||||
<text x="125" y="17" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="11" font-weight="bold">Machine</text>
|
|
||||||
<text x="125" y="32" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="9">workstation, laptop, server</text>
|
|
||||||
</g>
|
|
||||||
<line x1="305" y1="125" x2="395" y2="125" stroke="#e94560" stroke-width="2" marker-end="url(#arrRight)"/>
|
|
||||||
<g transform="translate(400, 105)">
|
|
||||||
<rect width="250" height="40" rx="4" fill="#0f3460"/>
|
|
||||||
<text x="125" y="17" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="11" font-weight="bold">Payment Processor</text>
|
|
||||||
<text x="125" y="32" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="9">Stripe, PayPal, bank API</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Row 2 -->
|
|
||||||
<g transform="translate(50, 155)">
|
|
||||||
<rect width="250" height="40" rx="4" fill="#0f3460"/>
|
|
||||||
<text x="125" y="17" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="11" font-weight="bold">Metrics Stream</text>
|
|
||||||
<text x="125" y="32" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="9">CPU, memory, disk usage</text>
|
|
||||||
</g>
|
|
||||||
<line x1="305" y1="175" x2="395" y2="175" stroke="#e94560" stroke-width="2" marker-end="url(#arrRight)"/>
|
|
||||||
<g transform="translate(400, 155)">
|
|
||||||
<rect width="250" height="40" rx="4" fill="#0f3460"/>
|
|
||||||
<text x="125" y="17" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="11" font-weight="bold">Transaction Stream</text>
|
|
||||||
<text x="125" y="32" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="9">payments, refunds, disputes</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Row 3 -->
|
|
||||||
<g transform="translate(50, 205)">
|
|
||||||
<rect width="250" height="40" rx="4" fill="#0f3460"/>
|
|
||||||
<text x="125" y="17" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="11" font-weight="bold">Alert Thresholds</text>
|
|
||||||
<text x="125" y="32" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="9">CPU > 80%, disk > 90%</text>
|
|
||||||
</g>
|
|
||||||
<line x1="305" y1="225" x2="395" y2="225" stroke="#e94560" stroke-width="2" marker-end="url(#arrRight)"/>
|
|
||||||
<g transform="translate(400, 205)">
|
|
||||||
<rect width="250" height="40" rx="4" fill="#0f3460"/>
|
|
||||||
<text x="125" y="17" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="11" font-weight="bold">Fraud Detection</text>
|
|
||||||
<text x="125" y="32" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="9">amount > $10k, velocity checks</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Row 4 -->
|
|
||||||
<g transform="translate(50, 255)">
|
|
||||||
<rect width="250" height="40" rx="4" fill="#0f3460"/>
|
|
||||||
<text x="125" y="17" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="11" font-weight="bold">Aggregator</text>
|
|
||||||
<text x="125" y="32" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="9">normalize, store, publish</text>
|
|
||||||
</g>
|
|
||||||
<line x1="305" y1="275" x2="395" y2="275" stroke="#e94560" stroke-width="2" marker-end="url(#arrRight)"/>
|
|
||||||
<g transform="translate(400, 255)">
|
|
||||||
<rect width="250" height="40" rx="4" fill="#0f3460"/>
|
|
||||||
<text x="125" y="17" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="11" font-weight="bold">Payment Hub</text>
|
|
||||||
<text x="125" y="32" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="9">normalize, ledger, audit</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Footer note -->
|
|
||||||
<text x="350" y="330" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="11" font-style="italic">Same architecture, different domain vocabulary</text>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 4.7 KiB |
@@ -1,125 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 380">
|
|
||||||
<defs>
|
|
||||||
<marker id="arrBlue" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
|
||||||
<polygon points="0 0, 8 3, 0 6" fill="#3b82f6"/>
|
|
||||||
</marker>
|
|
||||||
<marker id="arrPurple" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
|
||||||
<polygon points="0 0, 8 3, 0 6" fill="#a855f7"/>
|
|
||||||
</marker>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- Background -->
|
|
||||||
<rect width="700" height="380" fill="#1a1a2e"/>
|
|
||||||
|
|
||||||
<!-- Title -->
|
|
||||||
<text x="350" y="28" text-anchor="middle" fill="#e94560" font-family="system-ui" font-size="16" font-weight="bold">Deskmeter: Current vs Enhanced Architecture</text>
|
|
||||||
|
|
||||||
<!-- Current section -->
|
|
||||||
<g transform="translate(30, 50)">
|
|
||||||
<text x="150" y="15" text-anchor="middle" fill="#64748b" font-family="system-ui" font-size="12" font-weight="bold">CURRENT</text>
|
|
||||||
|
|
||||||
<!-- Daemon -->
|
|
||||||
<g transform="translate(20, 30)">
|
|
||||||
<rect width="100" height="50" rx="6" fill="#16213e" stroke="#64748b" stroke-width="1"/>
|
|
||||||
<text x="50" y="22" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="10" font-weight="bold">dmcore</text>
|
|
||||||
<text x="50" y="38" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="8">polls wmctrl</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- MongoDB -->
|
|
||||||
<g transform="translate(160, 30)">
|
|
||||||
<rect width="80" height="50" rx="6" fill="#0f3460" stroke="#64748b" stroke-width="1"/>
|
|
||||||
<text x="40" y="22" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="10" font-weight="bold">MongoDB</text>
|
|
||||||
<text x="40" y="38" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="8">all data</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Flask -->
|
|
||||||
<g transform="translate(90, 100)">
|
|
||||||
<rect width="100" height="50" rx="6" fill="#16213e" stroke="#64748b" stroke-width="1"/>
|
|
||||||
<text x="50" y="22" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="10" font-weight="bold">Flask</text>
|
|
||||||
<text x="50" y="38" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="8">AJAX polling</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Arrows -->
|
|
||||||
<line x1="120" y1="55" x2="155" y2="55" stroke="#64748b" stroke-width="1"/>
|
|
||||||
<line x1="200" y1="80" x2="155" y2="100" stroke="#64748b" stroke-width="1"/>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Arrow between sections -->
|
|
||||||
<g transform="translate(320, 100)">
|
|
||||||
<line x1="0" y1="30" x2="50" y2="30" stroke="#e94560" stroke-width="3" marker-end="url(#arrBlue)"/>
|
|
||||||
<text x="25" y="50" text-anchor="middle" fill="#e94560" font-family="system-ui" font-size="10">enhance</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Enhanced section -->
|
|
||||||
<g transform="translate(380, 50)">
|
|
||||||
<text x="150" y="15" text-anchor="middle" fill="#4ade80" font-family="system-ui" font-size="12" font-weight="bold">ENHANCED</text>
|
|
||||||
|
|
||||||
<!-- Multiple machines -->
|
|
||||||
<g transform="translate(0, 30)">
|
|
||||||
<rect width="70" height="35" rx="4" fill="#16213e" stroke="#3b82f6" stroke-width="1"/>
|
|
||||||
<text x="35" y="22" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="9">Machine 1</text>
|
|
||||||
</g>
|
|
||||||
<g transform="translate(0, 70)">
|
|
||||||
<rect width="70" height="35" rx="4" fill="#16213e" stroke="#3b82f6" stroke-width="1"/>
|
|
||||||
<text x="35" y="22" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="9">Machine 2</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Event stream -->
|
|
||||||
<g transform="translate(100, 40)">
|
|
||||||
<rect width="80" height="55" rx="6" fill="#0f3460" stroke="#fbbf24" stroke-width="2"/>
|
|
||||||
<text x="40" y="22" text-anchor="middle" fill="#fbbf24" font-family="system-ui" font-size="9" font-weight="bold">Events</text>
|
|
||||||
<text x="40" y="38" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="8">Pub/Sub</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Gateway with WebSocket -->
|
|
||||||
<g transform="translate(210, 30)">
|
|
||||||
<rect width="90" height="50" rx="6" fill="#16213e" stroke="#4ade80" stroke-width="2"/>
|
|
||||||
<text x="45" y="20" text-anchor="middle" fill="#4ade80" font-family="system-ui" font-size="9" font-weight="bold">Gateway</text>
|
|
||||||
<text x="45" y="35" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="8">WebSocket</text>
|
|
||||||
<text x="45" y="45" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="8">real-time</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Focus Alerts -->
|
|
||||||
<g transform="translate(210, 90)">
|
|
||||||
<rect width="90" height="40" rx="6" fill="#16213e" stroke="#a855f7" stroke-width="2"/>
|
|
||||||
<text x="45" y="17" text-anchor="middle" fill="#a855f7" font-family="system-ui" font-size="9" font-weight="bold">Focus Alerts</text>
|
|
||||||
<text x="45" y="32" text-anchor="middle" fill="#a0a0a0" font-family="system-ui" font-size="8">thresholds</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Arrows -->
|
|
||||||
<line x1="70" y1="47" x2="95" y2="60" stroke="#3b82f6" stroke-width="1.5" marker-end="url(#arrBlue)"/>
|
|
||||||
<line x1="70" y1="87" x2="95" y2="75" stroke="#3b82f6" stroke-width="1.5" marker-end="url(#arrBlue)"/>
|
|
||||||
<line x1="180" y1="60" x2="205" y2="55" stroke="#fbbf24" stroke-width="1.5"/>
|
|
||||||
<line x1="180" y1="75" x2="205" y2="110" stroke="#a855f7" stroke-width="1.5" marker-end="url(#arrPurple)"/>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Benefits list -->
|
|
||||||
<g transform="translate(30, 200)">
|
|
||||||
<rect width="640" height="160" rx="8" fill="#0f3460"/>
|
|
||||||
<text x="320" y="25" text-anchor="middle" fill="#eee" font-family="system-ui" font-size="13" font-weight="bold">What sysmonstm Patterns Add to Deskmeter</text>
|
|
||||||
|
|
||||||
<g transform="translate(30, 45)">
|
|
||||||
<circle cx="8" cy="8" r="5" fill="#4ade80"/>
|
|
||||||
<text x="22" y="12" fill="#eee" font-family="system-ui" font-size="11" font-weight="bold">Real-time updates</text>
|
|
||||||
<text x="22" y="28" fill="#a0a0a0" font-family="system-ui" font-size="10">WebSocket push instead of AJAX polling every 5 seconds</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(30, 75)">
|
|
||||||
<circle cx="8" cy="8" r="5" fill="#3b82f6"/>
|
|
||||||
<text x="22" y="12" fill="#eee" font-family="system-ui" font-size="11" font-weight="bold">Multi-machine tracking</text>
|
|
||||||
<text x="22" y="28" fill="#a0a0a0" font-family="system-ui" font-size="10">Monitor workstation + laptop productivity in one dashboard</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(30, 105)">
|
|
||||||
<circle cx="8" cy="8" r="5" fill="#a855f7"/>
|
|
||||||
<text x="22" y="12" fill="#eee" font-family="system-ui" font-size="11" font-weight="bold">Focus alerts</text>
|
|
||||||
<text x="22" y="28" fill="#a0a0a0" font-family="system-ui" font-size="10">Notify when context-switching too often or idle too long</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(30, 135)">
|
|
||||||
<circle cx="8" cy="8" r="5" fill="#fbbf24"/>
|
|
||||||
<text x="22" y="12" fill="#eee" font-family="system-ui" font-size="11" font-weight="bold">Event-driven architecture</text>
|
|
||||||
<text x="22" y="28" fill="#a0a0a0" font-family="system-ui" font-size="10">Decoupled services, easy to add new subscribers</text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 6.7 KiB |
@@ -1,282 +0,0 @@
|
|||||||
# Same Patterns, Different Domains
|
|
||||||
|
|
||||||
The architecture behind sysmonstm isn't specific to system monitoring. The patterns - streaming data collection, event-driven processing, tiered storage, real-time dashboards - apply to many domains. This article explores two: payment processing systems and desktop productivity tracking.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Payment Processing Systems
|
|
||||||
|
|
||||||
The sysmonstm architecture was intentionally designed to map to payment processing. Here's how each component translates.
|
|
||||||
|
|
||||||
### Domain Mapping
|
|
||||||
|
|
||||||
| sysmonstm | Payment System |
|
|
||||||
|-----------|----------------|
|
|
||||||
| Machine | Payment Processor (Stripe, PayPal, bank API) |
|
|
||||||
| Metrics Stream | Transaction Stream |
|
|
||||||
| Aggregator | Payment Hub |
|
|
||||||
| Alert Thresholds | Fraud Detection Rules |
|
|
||||||
| Alert Service | Risk Management |
|
|
||||||
| Redis (current state) | Transaction Cache |
|
|
||||||
| TimescaleDB (history) | Transaction Ledger |
|
|
||||||
| Event Stream | Audit Trail |
|
|
||||||
|
|
||||||
### How It Would Work
|
|
||||||
|
|
||||||
**Collectors become processor adapters.** Instead of collecting CPU and memory via psutil, each adapter connects to a payment processor's API or webhook endpoint:
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Conceptual - not actual code
|
|
||||||
class StripeAdapter:
|
|
||||||
async def stream_transactions(self):
|
|
||||||
async for event in stripe.webhook_events():
|
|
||||||
yield Transaction(
|
|
||||||
processor="stripe",
|
|
||||||
amount=event.amount,
|
|
||||||
currency=event.currency,
|
|
||||||
status=event.status,
|
|
||||||
customer_id=event.customer,
|
|
||||||
timestamp=event.created,
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
The gRPC streaming pattern remains identical. Each adapter streams transactions to a central aggregator.
|
|
||||||
|
|
||||||
**The aggregator normalizes data.** Stripe sends amounts in cents. PayPal sends them in dollars. Bank APIs use different currency codes. The aggregator normalizes everything to a consistent format before storage:
|
|
||||||
|
|
||||||
```python
|
|
||||||
# In the aggregator's StreamTransactions handler
|
|
||||||
async for tx in request_iterator:
|
|
||||||
normalized = normalize_transaction(tx)
|
|
||||||
await self.store(normalized)
|
|
||||||
await self.publisher.publish("transactions.raw", normalized)
|
|
||||||
```
|
|
||||||
|
|
||||||
This is the same pattern as `services/aggregator/main.py:47-95` - receive stream, batch, flush to storage, publish events.
|
|
||||||
|
|
||||||
**Alerts become fraud detection.** Instead of "CPU > 80%", rules look like:
|
|
||||||
|
|
||||||
- Transaction amount > $10,000 (large transaction)
|
|
||||||
- More than 5 transactions from same card in 1 minute (velocity check)
|
|
||||||
- Transaction from country different than cardholder's (geographic anomaly)
|
|
||||||
|
|
||||||
The `AlertEvaluator` pattern from `services/alerts/main.py:44-77` handles this:
|
|
||||||
|
|
||||||
```python
|
|
||||||
class FraudEvaluator:
|
|
||||||
RULES = [
|
|
||||||
FraudRule("large_transaction", "amount", "gt", 10000, "review"),
|
|
||||||
FraudRule("velocity", "transactions_per_minute", "gt", 5, "block"),
|
|
||||||
]
|
|
||||||
|
|
||||||
def evaluate(self, transaction: dict) -> list[FraudAlert]:
|
|
||||||
# Same operator-based evaluation as AlertEvaluator
|
|
||||||
pass
|
|
||||||
```
|
|
||||||
|
|
||||||
**The event stream becomes an audit trail.** Financial systems require complete audit logs. Every transaction, every state change, every decision must be recorded. The event abstraction from `shared/events/base.py` already provides this:
|
|
||||||
|
|
||||||
```python
|
|
||||||
await self.publisher.publish(
|
|
||||||
topic="transactions.processed",
|
|
||||||
payload={
|
|
||||||
"transaction_id": tx.id,
|
|
||||||
"processor": tx.processor,
|
|
||||||
"amount": tx.amount,
|
|
||||||
"decision": "approved",
|
|
||||||
"timestamp": datetime.utcnow().isoformat(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
Subscribe to these events for compliance reporting, analytics, or real-time monitoring.
|
|
||||||
|
|
||||||
**Tiered storage handles transaction volumes.** Hot transactions (last hour) in Redis for quick lookups. Recent transactions (last month) in PostgreSQL for operational queries. Historical transactions archived to S3 for compliance retention. Same pattern as sysmonstm's Redis + TimescaleDB setup.
|
|
||||||
|
|
||||||
### What Changes
|
|
||||||
|
|
||||||
- **Authentication**: Payment APIs require OAuth, API keys, mTLS. The collector adapters need credential management.
|
|
||||||
- **Idempotency**: Transactions must be processed exactly once. The aggregator needs deduplication.
|
|
||||||
- **Compliance**: PCI-DSS requires encryption, access controls, audit logging. More infrastructure, same patterns.
|
|
||||||
|
|
||||||
### What Stays the Same
|
|
||||||
|
|
||||||
- gRPC streaming from multiple sources to central aggregator
|
|
||||||
- Event-driven processing for decoupled services
|
|
||||||
- Threshold-based alerting
|
|
||||||
- Real-time dashboard via WebSocket
|
|
||||||
- Tiered storage for different access patterns
|
|
||||||
|
|
||||||
## Deskmeter: A Workspace Timer Application
|
|
||||||
|
|
||||||
Deskmeter is a productivity tracking application that monitors desktop workspace switches and task changes. It runs on Linux, tracks time spent on different tasks, and displays the data through a web dashboard.
|
|
||||||
|
|
||||||
Current architecture:
|
|
||||||
- **dmcore daemon**: Polls workspace state every 2 seconds using `wmctrl`
|
|
||||||
- **MongoDB**: Stores workspace switches with timestamps and durations
|
|
||||||
- **Flask web server**: Serves calendar views and task summaries
|
|
||||||
- **GNOME extension**: Shows current task in the top panel
|
|
||||||
|
|
||||||
This works, but sysmonstm patterns could enhance it significantly.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Current Deskmeter Implementation
|
|
||||||
|
|
||||||
The core daemon (`dmapp/dmcore/main.py`) polls in a loop:
|
|
||||||
|
|
||||||
```python
|
|
||||||
while True:
|
|
||||||
current_workspace = active_workspace() # Calls wmctrl
|
|
||||||
current_task = state.retrieve("current").get("task")
|
|
||||||
|
|
||||||
# Track the switch
|
|
||||||
last_switch_time = track_workspace_switch(
|
|
||||||
current_workspace,
|
|
||||||
current_task,
|
|
||||||
last_switch_time
|
|
||||||
)
|
|
||||||
|
|
||||||
time.sleep(2)
|
|
||||||
```
|
|
||||||
|
|
||||||
The web server (`dmapp/dmweb/dm.py`) uses Flask with template rendering:
|
|
||||||
|
|
||||||
```python
|
|
||||||
@dmbp.route("/calendar/<string:scope>")
|
|
||||||
def calendar_view(scope="daily", year=None, month=None, day=None):
|
|
||||||
blocks = get_task_blocks_calendar(start, end, task, ...)
|
|
||||||
return render_template("calendar_view.html", blocks=blocks, ...)
|
|
||||||
```
|
|
||||||
|
|
||||||
The dashboard refreshes via page reload or AJAX polling.
|
|
||||||
|
|
||||||
### How sysmonstm Patterns Would Improve It
|
|
||||||
|
|
||||||
**Replace polling with streaming.** Instead of the daemon polling every 2 seconds and the web dashboard polling for updates, use the same event-driven architecture as sysmonstm.
|
|
||||||
|
|
||||||
The daemon becomes an event publisher:
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Conceptual improvement
|
|
||||||
class WorkspaceMonitor:
|
|
||||||
async def run(self):
|
|
||||||
publisher = get_publisher(source="workspace-monitor")
|
|
||||||
await publisher.connect()
|
|
||||||
|
|
||||||
while self.running:
|
|
||||||
workspace = await self.detect_workspace()
|
|
||||||
task = await self.get_current_task()
|
|
||||||
|
|
||||||
if workspace != self.last_workspace or task != self.last_task:
|
|
||||||
await publisher.publish(
|
|
||||||
topic="workspace.switch",
|
|
||||||
payload={
|
|
||||||
"workspace": workspace,
|
|
||||||
"task": task,
|
|
||||||
"timestamp": datetime.now().isoformat(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
self.last_workspace = workspace
|
|
||||||
self.last_task = task
|
|
||||||
|
|
||||||
await asyncio.sleep(2)
|
|
||||||
```
|
|
||||||
|
|
||||||
The web server subscribes to events and pushes to browsers via WebSocket - exactly like `services/gateway/main.py:88-130`:
|
|
||||||
|
|
||||||
```python
|
|
||||||
async def event_listener():
|
|
||||||
async with get_subscriber(topics=["workspace.*"]) as subscriber:
|
|
||||||
async for event in subscriber.consume():
|
|
||||||
await manager.broadcast({
|
|
||||||
"type": "workspace_switch",
|
|
||||||
"data": event.payload,
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
The GNOME extension could subscribe directly instead of polling an HTTP endpoint.
|
|
||||||
|
|
||||||
**Add multi-machine support.** With sysmonstm's architecture, tracking multiple machines is trivial. Run the workspace monitor daemon on each machine. Each streams events to an aggregator. The dashboard shows all machines.
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Each machine's monitor includes machine_id
|
|
||||||
await publisher.publish(
|
|
||||||
topic="workspace.switch",
|
|
||||||
payload={
|
|
||||||
"machine_id": self.machine_id, # "workstation", "laptop", etc.
|
|
||||||
"workspace": workspace,
|
|
||||||
"task": task,
|
|
||||||
"timestamp": datetime.now().isoformat(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
The dashboard groups by machine or shows a combined view. Same pattern as sysmonstm's multi-machine monitoring.
|
|
||||||
|
|
||||||
**Add focus alerts.** The alert service pattern from `services/alerts/main.py` applies directly:
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Focus time rules
|
|
||||||
FocusRule("context_switching", "switches_per_hour", "gt", 10, "warning")
|
|
||||||
FocusRule("long_idle", "idle_minutes", "gt", 30, "info")
|
|
||||||
FocusRule("deep_work", "focus_minutes", "gt", 90, "success")
|
|
||||||
```
|
|
||||||
|
|
||||||
When you switch tasks more than 10 times in an hour, get a notification. When you've been focused for 90 minutes, celebrate. The evaluator pattern handles both alerts and achievements.
|
|
||||||
|
|
||||||
**Improve time-series storage.** Deskmeter uses MongoDB for everything. With sysmonstm's tiered approach:
|
|
||||||
|
|
||||||
- **Redis**: Current task, current workspace, last 5 minutes of switches
|
|
||||||
- **TimescaleDB**: Historical switches with automatic downsampling
|
|
||||||
|
|
||||||
Query "what was I doing at 3pm yesterday" hits warm storage. Query "how much time did I spend on project X this month" uses aggregated data. Same queries, faster execution.
|
|
||||||
|
|
||||||
### Implementation Path
|
|
||||||
|
|
||||||
1. **Add event publishing to dmcore.** Keep the polling loop but publish events instead of writing directly to MongoDB.
|
|
||||||
|
|
||||||
2. **Add WebSocket to dmweb.** Subscribe to events, push to connected browsers. The calendar view updates in real-time.
|
|
||||||
|
|
||||||
3. **Add Redis for current state.** Dashboard reads current task from Redis instead of querying MongoDB.
|
|
||||||
|
|
||||||
4. **Add focus alerts.** New service that subscribes to workspace events, evaluates rules, publishes alerts.
|
|
||||||
|
|
||||||
5. **Add multi-machine support.** Run dmcore on multiple machines. Aggregate events centrally.
|
|
||||||
|
|
||||||
Each step is independent. The system works after each one. Same phased approach as sysmonstm.
|
|
||||||
|
|
||||||
### Code Mapping
|
|
||||||
|
|
||||||
| sysmonstm Component | Deskmeter Equivalent |
|
|
||||||
|---------------------|---------------------|
|
|
||||||
| `services/collector/` | `dmapp/dmcore/main.py` - workspace monitoring |
|
|
||||||
| `services/aggregator/` | Event aggregation (new) |
|
|
||||||
| `services/gateway/` | `dmapp/dmweb/dm.py` + WebSocket (enhanced) |
|
|
||||||
| `services/alerts/` | Focus alerts service (new) |
|
|
||||||
| `proto/metrics.proto` | Workspace event schema |
|
|
||||||
| `shared/events/` | Same - reusable |
|
|
||||||
|
|
||||||
The event abstraction from sysmonstm (`shared/events/`) works directly. The configuration pattern from `shared/config.py` works directly. The structured logging from `shared/logging.py` works directly.
|
|
||||||
|
|
||||||
## The Common Thread
|
|
||||||
|
|
||||||
Both payment processing and productivity tracking share the same fundamental pattern:
|
|
||||||
|
|
||||||
1. **Multiple data sources** streaming to a central point
|
|
||||||
2. **Normalization** of different formats into consistent schema
|
|
||||||
3. **Real-time processing** for dashboards and alerts
|
|
||||||
4. **Historical storage** for analysis and compliance
|
|
||||||
5. **Event-driven decoupling** for extensibility
|
|
||||||
|
|
||||||
sysmonstm demonstrates these patterns with system metrics. The patterns transfer to any domain with similar characteristics:
|
|
||||||
|
|
||||||
- IoT sensor networks (temperature, humidity, motion)
|
|
||||||
- Log aggregation (application logs from multiple services)
|
|
||||||
- Social media analytics (tweets, posts, mentions)
|
|
||||||
- Trading systems (market data from multiple exchanges)
|
|
||||||
- Fleet management (GPS, fuel, diagnostics from vehicles)
|
|
||||||
|
|
||||||
The specific metrics change. The thresholds change. The domain vocabulary changes. The architecture stays the same.
|
|
||||||
|
|
||||||
Build it once for metrics. Apply it anywhere.
|
|
||||||
@@ -1,421 +0,0 @@
|
|||||||
# Building sysmonstm: From Idea to Working System
|
|
||||||
|
|
||||||
This is the story of building a distributed system monitoring platform. Not a tutorial with sanitized examples, but an explanation of the actual decisions made, the trade-offs considered, and the code that resulted.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## The Problem
|
|
||||||
|
|
||||||
I have multiple development machines. A workstation, a laptop, sometimes a remote VM. Each one occasionally runs out of disk space, hits memory limits, or has a runaway process eating CPU. The pattern was always the same: something breaks, I SSH in, run `htop`, realize the problem, fix it.
|
|
||||||
|
|
||||||
The obvious solution is a monitoring dashboard. Something that shows all machines in one place, updates in real-time, and alerts before things break.
|
|
||||||
|
|
||||||
But the real motivation was an interview. The job description mentioned gRPC, streaming patterns, event-driven architecture. Building a monitoring system would demonstrate all of these while solving an actual problem.
|
|
||||||
|
|
||||||
## Architecture Decisions
|
|
||||||
|
|
||||||
### Why gRPC Instead of REST
|
|
||||||
|
|
||||||
REST would work fine. Poll each machine every few seconds, aggregate the results. Simple.
|
|
||||||
|
|
||||||
But gRPC offers streaming. Instead of the aggregator asking each machine "what are your metrics right now?", each machine opens a persistent connection and continuously pushes metrics. This is more efficient (one connection instead of repeated requests) and lower latency (metrics arrive as soon as they're collected).
|
|
||||||
|
|
||||||
The proto definition in `proto/metrics.proto` defines this as client-side streaming:
|
|
||||||
|
|
||||||
```protobuf
|
|
||||||
service MetricsService {
|
|
||||||
// Client-side streaming: collector streams metrics to aggregator
|
|
||||||
rpc StreamMetrics(stream Metric) returns (StreamAck) {}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The collector is the client. It streams metrics. The aggregator is the server. It receives them. When the stream ends (collector shuts down, network drops), the aggregator gets a `StreamAck` response.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Why This Storage Tier Approach
|
|
||||||
|
|
||||||
Metrics have different access patterns at different ages:
|
|
||||||
|
|
||||||
- **Right now**: The dashboard needs current CPU/memory/disk for all machines. Access pattern: read all, very frequently.
|
|
||||||
- **Last hour**: Graphs showing recent trends. Access pattern: read range, somewhat frequently.
|
|
||||||
- **Last week**: Investigating what happened yesterday. Access pattern: read range, occasionally.
|
|
||||||
- **Last month**: Capacity planning. Access pattern: aggregated queries, rarely.
|
|
||||||
|
|
||||||
Storing everything in one place forces a choice between fast reads (keep it all in memory) and storage efficiency (keep it on disk). The solution is tiered storage:
|
|
||||||
|
|
||||||
- **Redis** (`services/aggregator/storage.py`): Current state only. Each machine's latest metrics, with 5-minute TTL. Dashboard reads hit Redis.
|
|
||||||
- **TimescaleDB** (`scripts/init-db.sql`): Historical data. Raw metrics at 5-second resolution for 24 hours, then automatically downsampled to 1-minute and 1-hour aggregates with longer retention.
|
|
||||||
|
|
||||||
The aggregator writes to both on every batch. Redis for live dashboard. TimescaleDB for history.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Why Event-Driven for Alerts
|
|
||||||
|
|
||||||
The alerts service needs to evaluate every metric against threshold rules. Two options:
|
|
||||||
|
|
||||||
1. **Direct call**: Aggregator calls alerts service for each metric batch.
|
|
||||||
2. **Event stream**: Aggregator publishes events. Alerts service subscribes.
|
|
||||||
|
|
||||||
Option 2 decouples them. The aggregator doesn't know or care if the alerts service is running. It publishes events regardless. The alerts service can be restarted, scaled, or replaced without touching the aggregator.
|
|
||||||
|
|
||||||
The event abstraction in `shared/events/base.py` defines the interface:
|
|
||||||
|
|
||||||
```python
|
|
||||||
class EventPublisher(ABC):
|
|
||||||
@abstractmethod
|
|
||||||
async def publish(self, topic: str, payload: dict[str, Any], **kwargs) -> str:
|
|
||||||
pass
|
|
||||||
|
|
||||||
class EventSubscriber(ABC):
|
|
||||||
@abstractmethod
|
|
||||||
async def consume(self) -> AsyncIterator[Event]:
|
|
||||||
pass
|
|
||||||
```
|
|
||||||
|
|
||||||
Currently backed by Redis Pub/Sub (`shared/events/redis_pubsub.py`). The abstraction means switching to Kafka or RabbitMQ later requires implementing a new backend, not changing any service code.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Phase 1: MVP - Getting Streaming to Work
|
|
||||||
|
|
||||||
The goal was simple: run a collector, see metrics appear in the aggregator's logs.
|
|
||||||
|
|
||||||
### The Collector
|
|
||||||
|
|
||||||
`services/collector/main.py` is a gRPC client. The core is an async generator that yields metrics forever:
|
|
||||||
|
|
||||||
```python
|
|
||||||
async def _metric_generator(self):
|
|
||||||
"""Async generator that yields metrics at the configured interval."""
|
|
||||||
while self.running:
|
|
||||||
batch = self.collector.collect()
|
|
||||||
protos = self._batch_to_proto(batch)
|
|
||||||
|
|
||||||
for proto in protos:
|
|
||||||
yield proto
|
|
||||||
|
|
||||||
await asyncio.sleep(self.config.collection_interval)
|
|
||||||
```
|
|
||||||
|
|
||||||
This generator is passed directly to the gRPC stub:
|
|
||||||
|
|
||||||
```python
|
|
||||||
response = await self.stub.StreamMetrics(self._metric_generator())
|
|
||||||
```
|
|
||||||
|
|
||||||
The gRPC library handles the streaming. Each `yield` sends a message. The connection stays open until the generator stops or the network fails.
|
|
||||||
|
|
||||||
The actual metric collection happens in `services/collector/metrics.py` using `psutil`:
|
|
||||||
|
|
||||||
```python
|
|
||||||
def _collect_cpu(self) -> list[MetricValue]:
|
|
||||||
metrics = []
|
|
||||||
cpu_percent = psutil.cpu_percent(interval=None)
|
|
||||||
metrics.append(MetricValue("CPU_PERCENT", cpu_percent))
|
|
||||||
|
|
||||||
per_core = psutil.cpu_percent(interval=None, percpu=True)
|
|
||||||
for i, pct in enumerate(per_core):
|
|
||||||
metrics.append(MetricValue(
|
|
||||||
"CPU_PERCENT_PER_CORE",
|
|
||||||
pct,
|
|
||||||
{"core": str(i)}
|
|
||||||
))
|
|
||||||
return metrics
|
|
||||||
```
|
|
||||||
|
|
||||||
### The Aggregator
|
|
||||||
|
|
||||||
`services/aggregator/main.py` is a gRPC server. The `StreamMetrics` method receives the stream:
|
|
||||||
|
|
||||||
```python
|
|
||||||
async def StreamMetrics(self, request_iterator, context):
|
|
||||||
metrics_received = 0
|
|
||||||
current_batch: list[tuple[str, float, dict]] = []
|
|
||||||
|
|
||||||
async for metric in request_iterator:
|
|
||||||
metrics_received += 1
|
|
||||||
|
|
||||||
metric_type = metrics_pb2.MetricType.Name(metric.type)
|
|
||||||
current_batch.append((metric_type, metric.value, dict(metric.labels)))
|
|
||||||
|
|
||||||
if len(current_batch) >= 20:
|
|
||||||
await self._flush_batch(...)
|
|
||||||
current_batch = []
|
|
||||||
```
|
|
||||||
|
|
||||||
The `request_iterator` is an async iterator over incoming metrics. The `async for` loop processes them as they arrive. Batching (flush every 20 metrics) reduces storage writes.
|
|
||||||
|
|
||||||
### Retry Logic
|
|
||||||
|
|
||||||
Networks fail. The collector needs to reconnect. The pattern is exponential backoff:
|
|
||||||
|
|
||||||
```python
|
|
||||||
retry_count = 0
|
|
||||||
max_retries = 10
|
|
||||||
base_delay = 1.0
|
|
||||||
|
|
||||||
while self.running:
|
|
||||||
try:
|
|
||||||
await self.stub.StreamMetrics(self._metric_generator())
|
|
||||||
retry_count = 0 # Success - reset counter
|
|
||||||
except grpc.aio.AioRpcError as e:
|
|
||||||
retry_count += 1
|
|
||||||
delay = min(base_delay * (2**retry_count), 60.0) # Cap at 60 seconds
|
|
||||||
await asyncio.sleep(delay)
|
|
||||||
await self.disconnect()
|
|
||||||
await self.connect()
|
|
||||||
```
|
|
||||||
|
|
||||||
First failure waits 2 seconds. Second waits 4. Third waits 8. Capped at 60 seconds. After 10 failures, give up.
|
|
||||||
|
|
||||||
## Phase 2: Dashboard - Making It Visible
|
|
||||||
|
|
||||||
Metrics in logs are useless. A dashboard makes them useful.
|
|
||||||
|
|
||||||
### The Gateway
|
|
||||||
|
|
||||||
`services/gateway/main.py` is a FastAPI application serving two purposes:
|
|
||||||
|
|
||||||
1. **REST API**: Query current and historical metrics
|
|
||||||
2. **WebSocket**: Push real-time updates to browsers
|
|
||||||
|
|
||||||
The WebSocket connection manager (`services/gateway/main.py:40-67`) tracks active connections:
|
|
||||||
|
|
||||||
```python
|
|
||||||
class ConnectionManager:
|
|
||||||
def __init__(self):
|
|
||||||
self.active_connections: list[WebSocket] = []
|
|
||||||
|
|
||||||
async def broadcast(self, message: dict) -> None:
|
|
||||||
data = json.dumps(message)
|
|
||||||
for connection in self.active_connections:
|
|
||||||
await connection.send_text(data)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Event to WebSocket Bridge
|
|
||||||
|
|
||||||
The gateway subscribes to the same event stream as alerts. When a metric event arrives, it broadcasts to all connected browsers:
|
|
||||||
|
|
||||||
```python
|
|
||||||
async def event_listener():
|
|
||||||
async with get_subscriber(topics=["metrics.raw", "alerts.*"]) as subscriber:
|
|
||||||
async for event in subscriber.consume():
|
|
||||||
await manager.broadcast({
|
|
||||||
"type": "metrics",
|
|
||||||
"data": event.payload,
|
|
||||||
"timestamp": event.timestamp.isoformat(),
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
This runs as a background task, started in the FastAPI lifespan handler (`services/gateway/main.py:145-175`).
|
|
||||||
|
|
||||||
### Handling Partial Batches
|
|
||||||
|
|
||||||
The aggregator batches metrics (flush every 20). This means a single collection cycle might arrive as multiple events. The dashboard needs complete machine state, not partial updates.
|
|
||||||
|
|
||||||
Solution: merge incoming metrics into a cache (`services/gateway/main.py:108-120`):
|
|
||||||
|
|
||||||
```python
|
|
||||||
machine_metrics_cache: dict[str, dict] = {}
|
|
||||||
|
|
||||||
# In event_listener:
|
|
||||||
machine_id = event.payload.get("machine_id", "")
|
|
||||||
incoming_metrics = event.payload.get("metrics", {})
|
|
||||||
|
|
||||||
if machine_id not in machine_metrics_cache:
|
|
||||||
machine_metrics_cache[machine_id] = {}
|
|
||||||
machine_metrics_cache[machine_id].update(incoming_metrics)
|
|
||||||
```
|
|
||||||
|
|
||||||
New metrics merge with existing. The broadcast includes the full merged state.
|
|
||||||
|
|
||||||
## Phase 3: Alerts - Adding Intelligence
|
|
||||||
|
|
||||||
The alerts service subscribes to metric events and evaluates them against rules.
|
|
||||||
|
|
||||||
### Rule Evaluation
|
|
||||||
|
|
||||||
`services/alerts/main.py` defines an `AlertEvaluator` class:
|
|
||||||
|
|
||||||
```python
|
|
||||||
class AlertEvaluator:
|
|
||||||
OPERATORS = {
|
|
||||||
"gt": lambda v, t: v > t,
|
|
||||||
"lt": lambda v, t: v < t,
|
|
||||||
"gte": lambda v, t: v >= t,
|
|
||||||
"lte": lambda v, t: v <= t,
|
|
||||||
"eq": lambda v, t: v == t,
|
|
||||||
}
|
|
||||||
|
|
||||||
def evaluate(self, machine_id: str, metrics: dict[str, float]) -> list[Alert]:
|
|
||||||
new_alerts = []
|
|
||||||
for metric_type, value in metrics.items():
|
|
||||||
rule = self.rules.get(metric_type)
|
|
||||||
if not rule:
|
|
||||||
continue
|
|
||||||
|
|
||||||
op_func = self.OPERATORS.get(rule.operator)
|
|
||||||
if op_func(value, rule.threshold):
|
|
||||||
# Threshold exceeded
|
|
||||||
new_alerts.append(Alert(...))
|
|
||||||
return new_alerts
|
|
||||||
```
|
|
||||||
|
|
||||||
### Avoiding Duplicate Alerts
|
|
||||||
|
|
||||||
If CPU stays above 80% for an hour, we want one alert, not 720 (one per 5-second check).
|
|
||||||
|
|
||||||
The evaluator tracks active alerts:
|
|
||||||
|
|
||||||
```python
|
|
||||||
self.active_alerts: dict[str, Alert] = {} # key: f"{machine_id}:{rule_name}"
|
|
||||||
|
|
||||||
# In evaluate():
|
|
||||||
alert_key = f"{machine_id}:{rule.name}"
|
|
||||||
if op_func(value, rule.threshold):
|
|
||||||
if alert_key not in self.active_alerts:
|
|
||||||
# New alert - trigger it
|
|
||||||
self.active_alerts[alert_key] = alert
|
|
||||||
new_alerts.append(alert)
|
|
||||||
# Otherwise already active - ignore
|
|
||||||
else:
|
|
||||||
# Threshold no longer exceeded - resolve
|
|
||||||
if alert_key in self.active_alerts:
|
|
||||||
del self.active_alerts[alert_key]
|
|
||||||
```
|
|
||||||
|
|
||||||
New alert only triggers if not already in `active_alerts`. When the metric drops below threshold, the alert is removed and can trigger again later.
|
|
||||||
|
|
||||||
## Phase 4: Polish - Production Patterns
|
|
||||||
|
|
||||||
### Structured Logging
|
|
||||||
|
|
||||||
Every service uses `shared/logging.py` for structured JSON logging:
|
|
||||||
|
|
||||||
```python
|
|
||||||
logger.info(
|
|
||||||
"stream_completed",
|
|
||||||
machine_id=current_machine,
|
|
||||||
metrics_received=metrics_received,
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
Output:
|
|
||||||
```json
|
|
||||||
{"event": "stream_completed", "machine_id": "workstation", "metrics_received": 1500, "timestamp": "..."}
|
|
||||||
```
|
|
||||||
|
|
||||||
This is searchable. "Show me all logs where metrics_received > 1000" is a simple query.
|
|
||||||
|
|
||||||
### Health Checks
|
|
||||||
|
|
||||||
Every service has health endpoints. The aggregator uses gRPC health checking (`services/aggregator/main.py:236-240`):
|
|
||||||
|
|
||||||
```python
|
|
||||||
health_servicer = health.HealthServicer()
|
|
||||||
health_servicer.set("", health_pb2.HealthCheckResponse.SERVING)
|
|
||||||
health_servicer.set("MetricsService", health_pb2.HealthCheckResponse.SERVING)
|
|
||||||
health_pb2_grpc.add_HealthServicer_to_server(health_servicer, self.server)
|
|
||||||
```
|
|
||||||
|
|
||||||
The gateway has HTTP health endpoints (`services/gateway/main.py:197-216`):
|
|
||||||
|
|
||||||
```python
|
|
||||||
@app.get("/ready")
|
|
||||||
async def readiness_check():
|
|
||||||
checks = {"gateway": "ok"}
|
|
||||||
|
|
||||||
try:
|
|
||||||
await grpc_stub.GetAllStates(metrics_pb2.Empty(), timeout=2.0)
|
|
||||||
checks["aggregator"] = "ok"
|
|
||||||
except Exception as e:
|
|
||||||
checks["aggregator"] = f"error: {str(e)}"
|
|
||||||
|
|
||||||
return {"status": "ready", "checks": checks}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Graceful Degradation
|
|
||||||
|
|
||||||
The aggregator continues streaming even if storage fails (`services/aggregator/main.py:137-152`):
|
|
||||||
|
|
||||||
```python
|
|
||||||
try:
|
|
||||||
await self.redis.update_machine_state(...)
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.warning("redis_update_failed", error=str(e))
|
|
||||||
# Don't re-raise - continue processing
|
|
||||||
|
|
||||||
try:
|
|
||||||
await self.timescale.insert_metrics(...)
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.warning("timescale_insert_failed", error=str(e))
|
|
||||||
# Don't re-raise - continue processing
|
|
||||||
```
|
|
||||||
|
|
||||||
Redis down? Metrics still flow to TimescaleDB. TimescaleDB down? Metrics still flow to the event stream. This keeps the system partially functional during partial failures.
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
|
|
||||||
All configuration uses Pydantic with environment variable support (`shared/config.py`):
|
|
||||||
|
|
||||||
```python
|
|
||||||
class CollectorConfig(BaseSettings):
|
|
||||||
machine_id: str = Field(default_factory=lambda: socket.gethostname())
|
|
||||||
aggregator_url: str = "aggregator:50051"
|
|
||||||
collection_interval: int = 5
|
|
||||||
|
|
||||||
model_config = SettingsConfigDict(env_prefix="COLLECTOR_")
|
|
||||||
```
|
|
||||||
|
|
||||||
Set `COLLECTOR_AGGREGATOR_URL=192.168.1.100:50051` and it overrides the default. No code changes for different environments.
|
|
||||||
|
|
||||||
## What Worked
|
|
||||||
|
|
||||||
**The event abstraction.** Adding a new consumer (like the gateway's WebSocket bridge) required zero changes to the aggregator. Subscribe to the topic, process events.
|
|
||||||
|
|
||||||
**Tiered storage.** Redis handles the hot path (dashboard reads). TimescaleDB handles history. Each optimized for its access pattern.
|
|
||||||
|
|
||||||
**Graceful degradation.** During development, I regularly restarted individual services. The system stayed partially functional throughout.
|
|
||||||
|
|
||||||
## What Could Be Better
|
|
||||||
|
|
||||||
**No backpressure.** If the aggregator falls behind, events accumulate in memory. A production system would need flow control.
|
|
||||||
|
|
||||||
**Alert rules are database-only.** Changing thresholds requires database updates. A proper config management system would be better.
|
|
||||||
|
|
||||||
**No authentication.** The gRPC channels are insecure. Production would need TLS and service authentication.
|
|
||||||
|
|
||||||
## Key Files Reference
|
|
||||||
|
|
||||||
| Component | File | Purpose |
|
|
||||||
|-----------|------|---------|
|
|
||||||
| Proto definitions | `proto/metrics.proto` | gRPC service and message definitions |
|
|
||||||
| Collector main | `services/collector/main.py` | gRPC client, streaming logic |
|
|
||||||
| Metric collection | `services/collector/metrics.py` | psutil wrappers |
|
|
||||||
| Aggregator main | `services/aggregator/main.py` | gRPC server, batch processing |
|
|
||||||
| Storage layer | `services/aggregator/storage.py` | Redis + TimescaleDB abstraction |
|
|
||||||
| Gateway main | `services/gateway/main.py` | FastAPI, WebSocket, event bridge |
|
|
||||||
| Alerts main | `services/alerts/main.py` | Event subscription, rule evaluation |
|
|
||||||
| Event abstraction | `shared/events/base.py` | Publisher/subscriber interfaces |
|
|
||||||
| Redis events | `shared/events/redis_pubsub.py` | Redis Pub/Sub implementation |
|
|
||||||
| Configuration | `shared/config.py` | Pydantic settings for all services |
|
|
||||||
| DB initialization | `scripts/init-db.sql` | TimescaleDB schema, hypertables |
|
|
||||||
| Docker setup | `docker-compose.yml` | Full stack orchestration |
|
|
||||||
|
|
||||||
## Running It
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker compose up
|
|
||||||
```
|
|
||||||
|
|
||||||
Open `http://localhost:8000` for the dashboard. Metrics appear within seconds.
|
|
||||||
|
|
||||||
To add another machine, run the collector pointed at your aggregator:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
COLLECTOR_AGGREGATOR_URL=your-server:50051 python services/collector/main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
It connects, starts streaming, and appears on the dashboard.
|
|
||||||
@@ -1,425 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Loading... - System Monitor Docs</title>
|
|
||||||
<link rel="stylesheet" href="../architecture/styles.css">
|
|
||||||
<link rel="stylesheet" href="../static/prism/prism-tomorrow.min.css">
|
|
||||||
<link rel="stylesheet" href="../static/prism/prism-line-numbers.min.css">
|
|
||||||
<style>
|
|
||||||
/* Article layout */
|
|
||||||
.article-container {
|
|
||||||
max-width: 900px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-header {
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
padding-bottom: 1rem;
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-header h1 {
|
|
||||||
font-size: 2rem;
|
|
||||||
color: var(--text-primary);
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-meta {
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-size: 0.875rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-link {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
color: var(--accent);
|
|
||||||
text-decoration: none;
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-link:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Markdown content styling */
|
|
||||||
.markdown-content {
|
|
||||||
color: var(--text-primary);
|
|
||||||
line-height: 1.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content h1 {
|
|
||||||
font-size: 2rem;
|
|
||||||
color: var(--accent);
|
|
||||||
margin: 2rem 0 1rem;
|
|
||||||
padding-bottom: 0.5rem;
|
|
||||||
border-bottom: 2px solid var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content h2 {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
color: var(--text-primary);
|
|
||||||
margin: 2rem 0 1rem;
|
|
||||||
padding-bottom: 0.25rem;
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content h3 {
|
|
||||||
font-size: 1.25rem;
|
|
||||||
color: var(--accent);
|
|
||||||
margin: 1.5rem 0 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content h4 {
|
|
||||||
font-size: 1.1rem;
|
|
||||||
color: var(--text-primary);
|
|
||||||
margin: 1.25rem 0 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content p {
|
|
||||||
margin: 1rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content a {
|
|
||||||
color: var(--accent);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content ul, .markdown-content ol {
|
|
||||||
margin: 1rem 0;
|
|
||||||
padding-left: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content li {
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content blockquote {
|
|
||||||
border-left: 4px solid var(--accent);
|
|
||||||
margin: 1.5rem 0;
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
border-radius: 0 8px 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content blockquote p {
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Code blocks */
|
|
||||||
.markdown-content pre {
|
|
||||||
margin: 1.5rem 0;
|
|
||||||
border-radius: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content pre[class*="language-"] {
|
|
||||||
background: #1e293b;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content code:not([class*="language-"]) {
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
padding: 0.2rem 0.4rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 0.9em;
|
|
||||||
color: var(--accent);
|
|
||||||
font-family: 'JetBrains Mono', 'Fira Code', Consolas, monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content code[class*="language-"] {
|
|
||||||
font-family: 'JetBrains Mono', 'Fira Code', Consolas, monospace;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tables */
|
|
||||||
.markdown-content table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
margin: 1.5rem 0;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content th,
|
|
||||||
.markdown-content td {
|
|
||||||
padding: 0.75rem 1rem;
|
|
||||||
text-align: left;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content th {
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content td {
|
|
||||||
background: var(--bg-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content tr:hover td {
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Strong/bold */
|
|
||||||
.markdown-content strong {
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Horizontal rules */
|
|
||||||
.markdown-content hr {
|
|
||||||
border: none;
|
|
||||||
border-top: 1px solid var(--border);
|
|
||||||
margin: 2rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Images/Diagrams */
|
|
||||||
.markdown-content img {
|
|
||||||
max-width: 100%;
|
|
||||||
height: auto;
|
|
||||||
border-radius: 8px;
|
|
||||||
margin: 1.5rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Diagram container */
|
|
||||||
.diagram {
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 1.5rem;
|
|
||||||
margin: 2rem 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.diagram img {
|
|
||||||
margin: 0;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.diagram-caption {
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-size: 0.875rem;
|
|
||||||
margin-top: 1rem;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Loading state */
|
|
||||||
.loading {
|
|
||||||
text-align: center;
|
|
||||||
padding: 4rem;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading::after {
|
|
||||||
content: '';
|
|
||||||
display: inline-block;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
border: 2px solid var(--border);
|
|
||||||
border-top-color: var(--accent);
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
margin-left: 1rem;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
to { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Error state */
|
|
||||||
.error {
|
|
||||||
text-align: center;
|
|
||||||
padding: 4rem;
|
|
||||||
color: #ef4444;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Table of contents */
|
|
||||||
.toc {
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 1.5rem;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc h4 {
|
|
||||||
margin: 0 0 1rem;
|
|
||||||
color: var(--accent);
|
|
||||||
font-size: 0.875rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.05em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc ul {
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc li {
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc a {
|
|
||||||
color: var(--text-secondary);
|
|
||||||
text-decoration: none;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc a:hover {
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc .toc-h3 {
|
|
||||||
padding-left: 1rem;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<h1>System Monitoring Platform</h1>
|
|
||||||
<p class="subtitle">Documentation</p>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main class="article-container">
|
|
||||||
<a href="../index.html" class="back-link">← Back to Index</a>
|
|
||||||
|
|
||||||
<div id="toc" class="toc" style="display: none;">
|
|
||||||
<h4>Contents</h4>
|
|
||||||
<ul id="toc-list"></ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<article id="content" class="markdown-content">
|
|
||||||
<div class="loading">Loading article</div>
|
|
||||||
</article>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
<p>System Monitoring Platform - Documentation</p>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<!-- Markdown parser -->
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
||||||
|
|
||||||
<!-- Prism syntax highlighting -->
|
|
||||||
<script src="../static/prism/prism.min.js"></script>
|
|
||||||
<script src="../static/prism/prism-python.min.js"></script>
|
|
||||||
<script src="../static/prism/prism-bash.min.js"></script>
|
|
||||||
<script src="../static/prism/prism-protobuf.min.js"></script>
|
|
||||||
<script src="../static/prism/prism-json.min.js"></script>
|
|
||||||
<script src="../static/prism/prism-line-numbers.min.js"></script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Get the markdown file from URL parameter
|
|
||||||
const params = new URLSearchParams(window.location.search);
|
|
||||||
const file = params.get('file');
|
|
||||||
|
|
||||||
if (!file) {
|
|
||||||
document.getElementById('content').innerHTML = '<div class="error">No file specified. Use ?file=filename.md</div>';
|
|
||||||
} else {
|
|
||||||
loadMarkdown(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadMarkdown(filename) {
|
|
||||||
try {
|
|
||||||
const response = await fetch(filename);
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`Failed to load ${filename}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const markdown = await response.text();
|
|
||||||
renderMarkdown(markdown, filename);
|
|
||||||
} catch (error) {
|
|
||||||
document.getElementById('content').innerHTML = `<div class="error">Error: ${error.message}</div>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderMarkdown(markdown, filename) {
|
|
||||||
// Configure marked
|
|
||||||
marked.setOptions({
|
|
||||||
gfm: true,
|
|
||||||
breaks: false,
|
|
||||||
headerIds: true,
|
|
||||||
mangle: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Custom renderer for code blocks
|
|
||||||
const renderer = new marked.Renderer();
|
|
||||||
|
|
||||||
renderer.code = function(code, language) {
|
|
||||||
const lang = language || 'plaintext';
|
|
||||||
const langClass = `language-${lang}`;
|
|
||||||
// Escape HTML in code
|
|
||||||
const escaped = code
|
|
||||||
.replace(/&/g, '&')
|
|
||||||
.replace(/</g, '<')
|
|
||||||
.replace(/>/g, '>');
|
|
||||||
return `<pre class="line-numbers"><code class="${langClass}">${escaped}</code></pre>`;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Custom heading renderer to add IDs
|
|
||||||
renderer.heading = function(text, level) {
|
|
||||||
const id = text.toLowerCase()
|
|
||||||
.replace(/<[^>]*>/g, '')
|
|
||||||
.replace(/[^\w\s-]/g, '')
|
|
||||||
.replace(/\s+/g, '-');
|
|
||||||
return `<h${level} id="${id}">${text}</h${level}>`;
|
|
||||||
};
|
|
||||||
|
|
||||||
marked.setOptions({ renderer });
|
|
||||||
|
|
||||||
// Render markdown
|
|
||||||
const html = marked.parse(markdown);
|
|
||||||
document.getElementById('content').innerHTML = html;
|
|
||||||
|
|
||||||
// Extract title from first h1
|
|
||||||
const titleMatch = markdown.match(/^#\s+(.+)$/m);
|
|
||||||
if (titleMatch) {
|
|
||||||
document.title = titleMatch[1] + ' - System Monitor Docs';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build table of contents
|
|
||||||
buildToc();
|
|
||||||
|
|
||||||
// Apply Prism highlighting
|
|
||||||
Prism.highlightAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildToc() {
|
|
||||||
const content = document.getElementById('content');
|
|
||||||
const headings = content.querySelectorAll('h2, h3');
|
|
||||||
const tocList = document.getElementById('toc-list');
|
|
||||||
|
|
||||||
if (headings.length < 3) {
|
|
||||||
return; // Don't show TOC for short articles
|
|
||||||
}
|
|
||||||
|
|
||||||
headings.forEach(heading => {
|
|
||||||
const li = document.createElement('li');
|
|
||||||
if (heading.tagName === 'H3') {
|
|
||||||
li.className = 'toc-h3';
|
|
||||||
}
|
|
||||||
|
|
||||||
const a = document.createElement('a');
|
|
||||||
a.href = '#' + heading.id;
|
|
||||||
a.textContent = heading.textContent;
|
|
||||||
li.appendChild(a);
|
|
||||||
tocList.appendChild(li);
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('toc').style.display = 'block';
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,393 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>System Monitor - Documentation</title>
|
|
||||||
<link rel="stylesheet" href="architecture/styles.css" />
|
|
||||||
<style>
|
|
||||||
/* Additional styles for docs index */
|
|
||||||
.nav-section {
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 1.5rem;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-section h2 {
|
|
||||||
color: var(--accent);
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
font-size: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-links {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-link {
|
|
||||||
display: block;
|
|
||||||
background: var(--bg-card);
|
|
||||||
padding: 1.25rem;
|
|
||||||
border-radius: 8px;
|
|
||||||
text-decoration: none;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
transition:
|
|
||||||
border-color 0.2s,
|
|
||||||
transform 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-link:hover {
|
|
||||||
border-color: var(--accent);
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-link h3 {
|
|
||||||
color: var(--text-primary);
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-link p {
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-size: 0.875rem;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-link .tag {
|
|
||||||
display: inline-block;
|
|
||||||
background: var(--accent-secondary);
|
|
||||||
color: var(--text-primary);
|
|
||||||
padding: 0.125rem 0.5rem;
|
|
||||||
border-radius: 3px;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
margin-top: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-divider {
|
|
||||||
border: none;
|
|
||||||
border-top: 1px solid var(--border);
|
|
||||||
margin: 2rem 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<h1>System Monitoring Platform</h1>
|
|
||||||
<p class="subtitle">Documentation</p>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<!-- Explainer Articles -->
|
|
||||||
<section class="nav-section">
|
|
||||||
<h2>Explainer Articles</h2>
|
|
||||||
<div class="doc-links">
|
|
||||||
<a
|
|
||||||
href="explainer/viewer.html?file=sysmonstm-from-start-to-finish.md"
|
|
||||||
class="doc-link"
|
|
||||||
>
|
|
||||||
<h3>sysmonstm: From Start to Finish</h3>
|
|
||||||
<p>
|
|
||||||
The complete story of building this monitoring
|
|
||||||
platform. Architecture decisions, trade-offs, and
|
|
||||||
code walkthrough from MVP to production patterns.
|
|
||||||
</p>
|
|
||||||
<span class="tag">Article</span>
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="explainer/viewer.html?file=other-applications.md"
|
|
||||||
class="doc-link"
|
|
||||||
>
|
|
||||||
<h3>Same Patterns, Different Domains</h3>
|
|
||||||
<p>
|
|
||||||
How the same architecture applies to payment
|
|
||||||
processing systems and the Deskmeter workspace
|
|
||||||
timer. Domain mapping and implementation paths.
|
|
||||||
</p>
|
|
||||||
<span class="tag">Article</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<hr class="section-divider" />
|
|
||||||
|
|
||||||
<!-- Architecture Diagrams -->
|
|
||||||
<section class="graph-section" id="overview">
|
|
||||||
<div class="graph-header-row">
|
|
||||||
<h2>System Overview</h2>
|
|
||||||
<a
|
|
||||||
href="architecture/graph.html?g=01-system-overview"
|
|
||||||
class="view-btn"
|
|
||||||
>View Full</a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<a
|
|
||||||
href="architecture/graph.html?g=01-system-overview"
|
|
||||||
class="graph-preview"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="architecture/01-system-overview.svg"
|
|
||||||
alt="System Overview"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
<div class="graph-details">
|
|
||||||
<p>
|
|
||||||
High-level architecture showing all services, data
|
|
||||||
stores, and communication patterns.
|
|
||||||
</p>
|
|
||||||
<h4>Key Components</h4>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<strong>Collector</strong>: Runs on each monitored
|
|
||||||
machine, streams metrics via gRPC
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Aggregator</strong>: Central gRPC server,
|
|
||||||
receives streams, normalizes data
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Gateway</strong>: FastAPI service, WebSocket
|
|
||||||
for browser, REST for queries
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Alerts</strong>: Subscribes to events,
|
|
||||||
evaluates thresholds, triggers actions
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="graph-section" id="data-flow">
|
|
||||||
<div class="graph-header-row">
|
|
||||||
<h2>Data Flow Pipeline</h2>
|
|
||||||
<a
|
|
||||||
href="architecture/graph.html?g=02-data-flow"
|
|
||||||
class="view-btn"
|
|
||||||
>View Full</a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<a
|
|
||||||
href="architecture/graph.html?g=02-data-flow"
|
|
||||||
class="graph-preview"
|
|
||||||
>
|
|
||||||
<img src="architecture/02-data-flow.svg" alt="Data Flow" />
|
|
||||||
</a>
|
|
||||||
<div class="graph-details">
|
|
||||||
<p>
|
|
||||||
How metrics flow from collection through storage with
|
|
||||||
different retention tiers.
|
|
||||||
</p>
|
|
||||||
<h4>Storage Tiers</h4>
|
|
||||||
<table class="details-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Tier</th>
|
|
||||||
<th>Resolution</th>
|
|
||||||
<th>Retention</th>
|
|
||||||
<th>Use Case</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>Hot (Redis)</td>
|
|
||||||
<td>5s</td>
|
|
||||||
<td>5 min</td>
|
|
||||||
<td>Current state, live dashboard</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Raw (TimescaleDB)</td>
|
|
||||||
<td>5s</td>
|
|
||||||
<td>24h</td>
|
|
||||||
<td>Recent detailed analysis</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>1-min Aggregates</td>
|
|
||||||
<td>1m</td>
|
|
||||||
<td>7d</td>
|
|
||||||
<td>Week view, trends</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>1-hour Aggregates</td>
|
|
||||||
<td>1h</td>
|
|
||||||
<td>90d</td>
|
|
||||||
<td>Long-term analysis</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="graph-section" id="deployment">
|
|
||||||
<div class="graph-header-row">
|
|
||||||
<h2>Deployment Architecture</h2>
|
|
||||||
<a
|
|
||||||
href="architecture/graph.html?g=03-deployment"
|
|
||||||
class="view-btn"
|
|
||||||
>View Full</a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<a
|
|
||||||
href="architecture/graph.html?g=03-deployment"
|
|
||||||
class="graph-preview"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="architecture/03-deployment.svg"
|
|
||||||
alt="Deployment"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
<div class="graph-details">
|
|
||||||
<p>
|
|
||||||
Deployment options from local development to AWS
|
|
||||||
production.
|
|
||||||
</p>
|
|
||||||
<h4>Environments</h4>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<strong>Local Dev</strong>: Kind + Tilt for K8s, or
|
|
||||||
Docker Compose
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Demo (EC2)</strong>: Docker Compose on
|
|
||||||
t2.small at sysmonstm.mcrn.ar
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Lambda Pipeline</strong>: SQS-triggered
|
|
||||||
aggregation for data processing experience
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="graph-section" id="grpc">
|
|
||||||
<div class="graph-header-row">
|
|
||||||
<h2>gRPC Service Definitions</h2>
|
|
||||||
<a
|
|
||||||
href="architecture/graph.html?g=04-grpc-services"
|
|
||||||
class="view-btn"
|
|
||||||
>View Full</a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<a
|
|
||||||
href="architecture/graph.html?g=04-grpc-services"
|
|
||||||
class="graph-preview"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="architecture/04-grpc-services.svg"
|
|
||||||
alt="gRPC Services"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
<div class="graph-details">
|
|
||||||
<p>Protocol Buffer service and message definitions.</p>
|
|
||||||
<h4>Services</h4>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<strong>MetricsService</strong>: Client-side
|
|
||||||
streaming for metrics ingestion
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>ControlService</strong>: Bidirectional
|
|
||||||
streaming for collector control
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>ConfigService</strong>: Server-side
|
|
||||||
streaming for config updates
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<hr class="section-divider" />
|
|
||||||
|
|
||||||
<section class="findings-section">
|
|
||||||
<h2>Interview Talking Points</h2>
|
|
||||||
<div class="findings-grid">
|
|
||||||
<article class="finding-card">
|
|
||||||
<h3>Domain Mapping</h3>
|
|
||||||
<ul>
|
|
||||||
<li>Machine = Payment Processor</li>
|
|
||||||
<li>Metrics Stream = Transaction Stream</li>
|
|
||||||
<li>Thresholds = Fraud Detection</li>
|
|
||||||
<li>Aggregator = Payment Hub</li>
|
|
||||||
</ul>
|
|
||||||
</article>
|
|
||||||
<article class="finding-card">
|
|
||||||
<h3>gRPC Patterns</h3>
|
|
||||||
<ul>
|
|
||||||
<li>Client streaming (metrics)</li>
|
|
||||||
<li>Server streaming (config)</li>
|
|
||||||
<li>Bidirectional (control)</li>
|
|
||||||
<li>Health checking</li>
|
|
||||||
</ul>
|
|
||||||
</article>
|
|
||||||
<article class="finding-card">
|
|
||||||
<h3>Event-Driven</h3>
|
|
||||||
<ul>
|
|
||||||
<li>Redis Pub/Sub (current)</li>
|
|
||||||
<li>Abstraction for Kafka switch</li>
|
|
||||||
<li>Decoupled alert processing</li>
|
|
||||||
<li>Real-time WebSocket push</li>
|
|
||||||
</ul>
|
|
||||||
</article>
|
|
||||||
<article class="finding-card">
|
|
||||||
<h3>Resilience</h3>
|
|
||||||
<ul>
|
|
||||||
<li>Collectors are independent</li>
|
|
||||||
<li>Graceful degradation</li>
|
|
||||||
<li>Retry with backoff</li>
|
|
||||||
<li>Health checks everywhere</li>
|
|
||||||
</ul>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="tech-section">
|
|
||||||
<h2>Technology Stack</h2>
|
|
||||||
<div class="tech-grid">
|
|
||||||
<div class="tech-column">
|
|
||||||
<h3>Core</h3>
|
|
||||||
<ul>
|
|
||||||
<li>Python 3.11+</li>
|
|
||||||
<li>FastAPI</li>
|
|
||||||
<li>gRPC / protobuf</li>
|
|
||||||
<li>asyncio</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="tech-column">
|
|
||||||
<h3>Data</h3>
|
|
||||||
<ul>
|
|
||||||
<li>TimescaleDB</li>
|
|
||||||
<li>Redis</li>
|
|
||||||
<li>Redis Pub/Sub</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="tech-column">
|
|
||||||
<h3>Infrastructure</h3>
|
|
||||||
<ul>
|
|
||||||
<li>Docker</li>
|
|
||||||
<li>Kubernetes</li>
|
|
||||||
<li>Kind + Tilt</li>
|
|
||||||
<li>Terraform</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="tech-column">
|
|
||||||
<h3>CI/CD</h3>
|
|
||||||
<ul>
|
|
||||||
<li>Woodpecker CI</li>
|
|
||||||
<li>Kustomize</li>
|
|
||||||
<li>Container Registry</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
<p>System Monitoring Platform - Documentation</p>
|
|
||||||
<p class="date">
|
|
||||||
Generated: <time datetime="2025-12-31">December 2025</time>
|
|
||||||
</p>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.json={property:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,lookbehind:!0,greedy:!0},comment:{pattern:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}},Prism.languages.webmanifest=Prism.languages.json;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(e){var s=/\b(?:bool|bytes|double|s?fixed(?:32|64)|float|[su]?int(?:32|64)|string)\b/;e.languages.protobuf=e.languages.extend("clike",{"class-name":[{pattern:/(\b(?:enum|extend|message|service)\s+)[A-Za-z_]\w*(?=\s*\{)/,lookbehind:!0},{pattern:/(\b(?:rpc\s+\w+|returns)\s*\(\s*(?:stream\s+)?)\.?[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*(?=\s*\))/,lookbehind:!0}],keyword:/\b(?:enum|extend|extensions|import|message|oneof|option|optional|package|public|repeated|required|reserved|returns|rpc(?=\s+\w)|service|stream|syntax|to)\b(?!\s*=\s*\d)/,function:/\b[a-z_]\w*(?=\s*\()/i}),e.languages.insertBefore("protobuf","operator",{map:{pattern:/\bmap<\s*[\w.]+\s*,\s*[\w.]+\s*>(?=\s+[a-z_]\w*\s*[=;])/i,alias:"class-name",inside:{punctuation:/[<>.,]/,builtin:s}},builtin:s,"positional-class-name":{pattern:/(?:\b|\B\.)[a-z_]\w*(?:\.[a-z_]\w*)*(?=\s+[a-z_]\w*\s*[=;])/i,alias:"class-name",inside:{punctuation:/\./}},annotation:{pattern:/(\[\s*)[a-z_]\w*(?=\s*=)/i,lookbehind:!0}})}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern://,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},Prism.languages.python["string-interpolation"].inside.interpolation.inside.rest=Prism.languages.python,Prism.languages.py=Prism.languages.python;
|
|
||||||
1
atlas/static/prism/prism-gherkin.min.js
vendored
@@ -1 +0,0 @@
|
|||||||
pre[class*=language-].line-numbers{position:relative;padding-left:3.8em;counter-reset:linenumber}pre[class*=language-].line-numbers>code{position:relative;white-space:inherit}.line-numbers .line-numbers-rows{position:absolute;pointer-events:none;top:0;font-size:100%;left:-3.8em;width:3em;letter-spacing:-1px;border-right:1px solid #999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.line-numbers-rows>span{display:block;counter-increment:linenumber}.line-numbers-rows>span:before{content:counter(linenumber);color:#999;display:block;padding-right:.8em;text-align:right}
|
|
||||||
1
atlas/static/prism/prism-line-numbers.min.js
vendored
@@ -1 +0,0 @@
|
|||||||
!function(){if("undefined"!=typeof Prism&&"undefined"!=typeof document){var e="line-numbers",n=/\n(?!$)/g,t=Prism.plugins.lineNumbers={getLine:function(n,t){if("PRE"===n.tagName&&n.classList.contains(e)){var i=n.querySelector(".line-numbers-rows");if(i){var r=parseInt(n.getAttribute("data-start"),10)||1,s=r+(i.children.length-1);t<r&&(t=r),t>s&&(t=s);var l=t-r;return i.children[l]}}},resize:function(e){r([e])},assumeViewportIndependence:!0},i=void 0;window.addEventListener("resize",(function(){t.assumeViewportIndependence&&i===window.innerWidth||(i=window.innerWidth,r(Array.prototype.slice.call(document.querySelectorAll("pre.line-numbers"))))})),Prism.hooks.add("complete",(function(t){if(t.code){var i=t.element,s=i.parentNode;if(s&&/pre/i.test(s.nodeName)&&!i.querySelector(".line-numbers-rows")&&Prism.util.isActive(i,e)){i.classList.remove(e),s.classList.add(e);var l,o=t.code.match(n),a=o?o.length+1:1,u=new Array(a+1).join("<span></span>");(l=document.createElement("span")).setAttribute("aria-hidden","true"),l.className="line-numbers-rows",l.innerHTML=u,s.hasAttribute("data-start")&&(s.style.counterReset="linenumber "+(parseInt(s.getAttribute("data-start"),10)-1)),t.element.appendChild(l),r([s]),Prism.hooks.run("line-numbers",t)}}})),Prism.hooks.add("line-numbers",(function(e){e.plugins=e.plugins||{},e.plugins.lineNumbers=!0}))}function r(e){if(0!=(e=e.filter((function(e){var n,t=(n=e,n?window.getComputedStyle?getComputedStyle(n):n.currentStyle||null:null)["white-space"];return"pre-wrap"===t||"pre-line"===t}))).length){var t=e.map((function(e){var t=e.querySelector("code"),i=e.querySelector(".line-numbers-rows");if(t&&i){var r=e.querySelector(".line-numbers-sizer"),s=t.textContent.split(n);r||((r=document.createElement("span")).className="line-numbers-sizer",t.appendChild(r)),r.innerHTML="0",r.style.display="block";var l=r.getBoundingClientRect().height;return r.innerHTML="",{element:e,lines:s,lineHeights:[],oneLinerHeight:l,sizer:r}}})).filter(Boolean);t.forEach((function(e){var n=e.sizer,t=e.lines,i=e.lineHeights,r=e.oneLinerHeight;i[t.length-1]=void 0,t.forEach((function(e,t){if(e&&e.length>1){var s=n.appendChild(document.createElement("span"));s.style.display="block",s.textContent=e}else i[t]=r}))})),t.forEach((function(e){for(var n=e.sizer,t=e.lineHeights,i=0,r=0;r<t.length;r++)void 0===t[r]&&(t[r]=n.children[i++].getBoundingClientRect().height)})),t.forEach((function(e){var n=e.sizer,t=e.element.querySelector(".line-numbers-rows");n.style.display="none",n.innerHTML="",e.lineHeights.forEach((function(e,n){t.children[n].style.height=e+"px"}))}))}}}();
|
|
||||||
1
atlas/static/prism/prism-tomorrow.min.css
vendored
@@ -1 +0,0 @@
|
|||||||
code[class*=language-],pre[class*=language-]{color:#ccc;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}
|
|
||||||
1
atlas/static/prism/prism.min.js
vendored
529
build.py
@@ -2,59 +2,54 @@
|
|||||||
"""
|
"""
|
||||||
Soleprint Build Tool
|
Soleprint Build Tool
|
||||||
|
|
||||||
Builds the soleprint instance using modelgen for model generation.
|
Generates soleprint instances from source + room config.
|
||||||
|
|
||||||
All modes copy files (no symlinks) for Docker compatibility.
|
|
||||||
After editing source files, re-run `python build.py` to update gen/.
|
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
python build.py # Build gen/standalone/
|
python build.py # Build gen/standalone/
|
||||||
python build.py --cfg <room> # Build gen/<room>/
|
python build.py --cfg <room> # Build gen/<room>/
|
||||||
python build.py --all # Build all (standalone + rooms)
|
python build.py --all # Build all rooms
|
||||||
python build.py --deploy --output /path/ # Build for production
|
python build.py --output /path/ # Build to custom path
|
||||||
python build.py --models # Only regenerate models
|
python build.py --models # Only regenerate models
|
||||||
|
|
||||||
Examples:
|
Generated structure for standalone rooms:
|
||||||
# Build standalone
|
gen/standalone/
|
||||||
python build.py
|
artery/, atlas/, station/, main.py, ...
|
||||||
cd gen/standalone && .venv/bin/python run.py
|
|
||||||
|
|
||||||
# With room config
|
Generated structure for managed rooms:
|
||||||
python build.py --cfg myroom
|
gen/<room>/
|
||||||
cd gen/myroom && .venv/bin/python run.py
|
<managed_name>/ # Copied repos + ctrl
|
||||||
|
link/ # Bridge code
|
||||||
# Build all targets
|
soleprint/ # Soleprint instance
|
||||||
python build.py --all
|
|
||||||
|
|
||||||
# Build for deployment
|
|
||||||
python build.py --deploy --output ../deploy/soleprint/
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# SPR root is where this script lives
|
|
||||||
SPR_ROOT = Path(__file__).resolve().parent
|
SPR_ROOT = Path(__file__).resolve().parent
|
||||||
|
|
||||||
# Configure logging
|
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.INFO,
|
|
||||||
format="%(message)s",
|
|
||||||
)
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def load_config(cfg_name: str | None) -> dict:
|
||||||
|
"""Load room config.json."""
|
||||||
|
room = cfg_name or "standalone"
|
||||||
|
config_path = SPR_ROOT / "cfg" / room / "config.json"
|
||||||
|
if config_path.exists():
|
||||||
|
return json.loads(config_path.read_text())
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
def ensure_dir(path: Path):
|
def ensure_dir(path: Path):
|
||||||
"""Create directory if it doesn't exist."""
|
|
||||||
path.mkdir(parents=True, exist_ok=True)
|
path.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
def copy_path(source: Path, target: Path):
|
def copy_path(source: Path, target: Path, quiet: bool = False):
|
||||||
"""Copy file or directory, resolving symlinks."""
|
"""Copy file or directory, resolving symlinks."""
|
||||||
if target.is_symlink():
|
if target.is_symlink():
|
||||||
target.unlink()
|
target.unlink()
|
||||||
@@ -66,40 +61,162 @@ def copy_path(source: Path, target: Path):
|
|||||||
|
|
||||||
if source.is_dir():
|
if source.is_dir():
|
||||||
shutil.copytree(source, target, symlinks=False)
|
shutil.copytree(source, target, symlinks=False)
|
||||||
log.info(f" Copied: {target.name}/ ({count_files(target)} files)")
|
if not quiet:
|
||||||
|
log.info(f" {target.name}/")
|
||||||
else:
|
else:
|
||||||
shutil.copy2(source, target)
|
shutil.copy2(source, target)
|
||||||
log.info(f" Copied: {target.name}")
|
if not quiet:
|
||||||
|
log.info(f" {target.name}")
|
||||||
|
|
||||||
|
|
||||||
def count_files(path: Path) -> int:
|
def count_files(path: Path) -> int:
|
||||||
"""Count files in directory recursively."""
|
|
||||||
return sum(1 for _ in path.rglob("*") if _.is_file())
|
return sum(1 for _ in path.rglob("*") if _.is_file())
|
||||||
|
|
||||||
|
|
||||||
def generate_models(output_dir: Path, cfg_name: str | None = None):
|
def merge_into(source: Path, target: Path):
|
||||||
"""Generate models using modelgen tool.
|
"""Merge source directory into target (additive, overwrites conflicts)."""
|
||||||
|
if not source.exists():
|
||||||
|
return
|
||||||
|
for item in source.rglob("*"):
|
||||||
|
if item.is_file():
|
||||||
|
rel = item.relative_to(source)
|
||||||
|
dest = target / rel
|
||||||
|
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
shutil.copy2(item, dest)
|
||||||
|
|
||||||
Args:
|
|
||||||
output_dir: Directory where models/pydantic/__init__.py will be created
|
def parse_gitignore(gitignore_path: Path) -> set[str]:
|
||||||
cfg_name: Room config name (e.g., 'amar'), or None for standalone
|
"""Parse .gitignore and return set of patterns to ignore."""
|
||||||
"""
|
patterns = set()
|
||||||
room = cfg_name or "standalone"
|
if not gitignore_path.exists():
|
||||||
|
return patterns
|
||||||
|
|
||||||
|
for line in gitignore_path.read_text().splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
# Skip comments and empty lines
|
||||||
|
if not line or line.startswith("#"):
|
||||||
|
continue
|
||||||
|
# Remove trailing slashes (directory indicators)
|
||||||
|
pattern = line.rstrip("/")
|
||||||
|
# Skip negation patterns (we don't support them)
|
||||||
|
if pattern.startswith("!"):
|
||||||
|
continue
|
||||||
|
patterns.add(pattern)
|
||||||
|
return patterns
|
||||||
|
|
||||||
|
|
||||||
|
def copy_repo(source: Path, target: Path):
|
||||||
|
"""Copy a repo directory, excluding .git and gitignored files."""
|
||||||
|
if not source.exists():
|
||||||
|
log.warning(f"Repo not found: {source}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Always ignore these
|
||||||
|
always_ignore = {".git", "__pycache__", "node_modules", ".venv", "venv", ".env"}
|
||||||
|
|
||||||
|
# Parse .gitignore from repo root
|
||||||
|
gitignore_patterns = parse_gitignore(source / ".gitignore")
|
||||||
|
|
||||||
|
def ignore_patterns(directory, files):
|
||||||
|
ignored = set()
|
||||||
|
rel_dir = Path(directory).relative_to(source)
|
||||||
|
|
||||||
|
for f in files:
|
||||||
|
# Always ignore these
|
||||||
|
if f in always_ignore:
|
||||||
|
ignored.add(f)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check gitignore patterns
|
||||||
|
rel_path = rel_dir / f if str(rel_dir) != "." else Path(f)
|
||||||
|
|
||||||
|
for pattern in gitignore_patterns:
|
||||||
|
# Simple pattern matching (filename or extension)
|
||||||
|
if pattern.startswith("*."):
|
||||||
|
# Extension pattern like *.pyc
|
||||||
|
if f.endswith(pattern[1:]):
|
||||||
|
ignored.add(f)
|
||||||
|
break
|
||||||
|
elif pattern == f or pattern == str(rel_path):
|
||||||
|
# Exact match
|
||||||
|
ignored.add(f)
|
||||||
|
break
|
||||||
|
elif "/" not in pattern and f == pattern:
|
||||||
|
# Simple name match anywhere
|
||||||
|
ignored.add(f)
|
||||||
|
break
|
||||||
|
|
||||||
|
return ignored
|
||||||
|
|
||||||
|
shutil.copytree(source, target, ignore=ignore_patterns, symlinks=False)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def build_managed(output_dir: Path, cfg_name: str, config: dict):
|
||||||
|
"""Build managed folder with repos + ctrl."""
|
||||||
|
managed = config.get("managed", {})
|
||||||
|
managed_name = managed.get("name", cfg_name)
|
||||||
|
repos = managed.get("repos", {})
|
||||||
|
|
||||||
|
managed_dir = output_dir / managed_name
|
||||||
|
ensure_dir(managed_dir)
|
||||||
|
|
||||||
|
log.info(f"Building managed ({managed_name})...")
|
||||||
|
|
||||||
|
# Copy repos
|
||||||
|
for repo_name, repo_path in repos.items():
|
||||||
|
source = Path(repo_path)
|
||||||
|
target = managed_dir / repo_name
|
||||||
|
if copy_repo(source, target):
|
||||||
|
log.info(f" {repo_name}/")
|
||||||
|
|
||||||
|
room_cfg = SPR_ROOT / "cfg" / cfg_name
|
||||||
|
|
||||||
|
# Docker files from room root -> managed root
|
||||||
|
for item in room_cfg.iterdir():
|
||||||
|
if item.is_file() and (
|
||||||
|
item.name.startswith("Dockerfile") or item.name.startswith("docker-compose")
|
||||||
|
):
|
||||||
|
copy_path(item, managed_dir / item.name)
|
||||||
|
|
||||||
|
# Scripts from ctrl/ -> managed/ctrl/
|
||||||
|
room_ctrl = room_cfg / "ctrl"
|
||||||
|
if room_ctrl.exists():
|
||||||
|
ctrl_dir = managed_dir / "ctrl"
|
||||||
|
ensure_dir(ctrl_dir)
|
||||||
|
for item in room_ctrl.iterdir():
|
||||||
|
if item.is_file():
|
||||||
|
copy_path(item, ctrl_dir / item.name)
|
||||||
|
|
||||||
|
|
||||||
|
def build_link(output_dir: Path, cfg_name: str):
|
||||||
|
"""Build link folder."""
|
||||||
|
room_cfg = SPR_ROOT / "cfg" / cfg_name
|
||||||
|
link_source = room_cfg / "link"
|
||||||
|
|
||||||
|
if not link_source.exists():
|
||||||
|
return
|
||||||
|
|
||||||
|
log.info("Building link...")
|
||||||
|
link_dir = output_dir / "link"
|
||||||
|
copy_path(link_source, link_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_models(output_dir: Path, room: str):
|
||||||
|
"""Generate models using modelgen tool."""
|
||||||
config_path = SPR_ROOT / "cfg" / room / "config.json"
|
config_path = SPR_ROOT / "cfg" / room / "config.json"
|
||||||
|
|
||||||
if not config_path.exists():
|
if not config_path.exists():
|
||||||
log.warning(f"Config not found at {config_path}")
|
log.warning(f"Config not found: {config_path}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Soleprint-specific: models go in models/pydantic/__init__.py
|
|
||||||
models_file = output_dir / "models" / "pydantic" / "__init__.py"
|
models_file = output_dir / "models" / "pydantic" / "__init__.py"
|
||||||
models_file.parent.mkdir(parents=True, exist_ok=True)
|
models_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
# Run modelgen as subprocess
|
|
||||||
cmd = [
|
cmd = [
|
||||||
sys.executable,
|
sys.executable,
|
||||||
"-m",
|
"-m",
|
||||||
"station.tools.modelgen",
|
"soleprint.station.tools.modelgen",
|
||||||
"from-config",
|
"from-config",
|
||||||
"--config",
|
"--config",
|
||||||
str(config_path),
|
str(config_path),
|
||||||
@@ -113,309 +230,139 @@ def generate_models(output_dir: Path, cfg_name: str | None = None):
|
|||||||
return result.returncode == 0
|
return result.returncode == 0
|
||||||
|
|
||||||
|
|
||||||
def copy_cfg(output_dir: Path, cfg_name: str | None):
|
def copy_cfg(output_dir: Path, room: str):
|
||||||
"""Copy configuration files to output directory.
|
"""Copy room configuration files to output directory."""
|
||||||
|
|
||||||
Args:
|
|
||||||
output_dir: Target directory
|
|
||||||
cfg_name: Name of room config (e.g., 'amar'), or None for standalone
|
|
||||||
"""
|
|
||||||
room = cfg_name or "standalone"
|
|
||||||
room_cfg = SPR_ROOT / "cfg" / room
|
room_cfg = SPR_ROOT / "cfg" / room
|
||||||
|
|
||||||
if not room_cfg.exists():
|
if not room_cfg.exists():
|
||||||
log.warning(f"Room config '{room}' not found at {room_cfg}")
|
log.warning(f"Room config not found: {room}")
|
||||||
return
|
return
|
||||||
|
|
||||||
log.info(f"\nCopying {room} room config...")
|
log.info(f"Copying {room} config...")
|
||||||
|
|
||||||
# Copy config.json to cfg/
|
# config.json -> cfg/
|
||||||
cfg_dir = output_dir / "cfg"
|
cfg_dir = output_dir / "cfg"
|
||||||
ensure_dir(cfg_dir)
|
ensure_dir(cfg_dir)
|
||||||
room_config = room_cfg / "config.json"
|
if (room_cfg / "config.json").exists():
|
||||||
if room_config.exists():
|
copy_path(room_cfg / "config.json", cfg_dir / "config.json")
|
||||||
copy_path(room_config, cfg_dir / "config.json")
|
|
||||||
|
|
||||||
# Copy data/ to output data/
|
# data/ -> data/
|
||||||
room_data = room_cfg / "data"
|
if (room_cfg / "data").exists():
|
||||||
if room_data.exists():
|
copy_path(room_cfg / "data", output_dir / "data")
|
||||||
log.info(f" Copying {room} data files...")
|
|
||||||
copy_path(room_data, output_dir / "data")
|
|
||||||
|
|
||||||
# Copy .env.example to output root
|
# .env.example
|
||||||
env_example = room_cfg / ".env.example"
|
if (room_cfg / ".env.example").exists():
|
||||||
if env_example.exists():
|
copy_path(room_cfg / ".env.example", output_dir / ".env.example")
|
||||||
copy_path(env_example, output_dir / ".env.example")
|
|
||||||
|
|
||||||
# Copy room-specific databrowse depot if exists
|
# Room-specific soleprint config (docker-compose.yml, etc)
|
||||||
room_databrowse = room_cfg / "databrowse" / "depot"
|
# Now in cfg/<room>/soleprint/
|
||||||
if room_databrowse.exists():
|
|
||||||
log.info(f" Copying {room} databrowse depot...")
|
|
||||||
target = output_dir / "station" / "monitors" / "databrowse" / "depot"
|
|
||||||
copy_path(room_databrowse, target)
|
|
||||||
|
|
||||||
# Copy room-specific tester tests if exists
|
|
||||||
room_tests = room_cfg / "tester" / "tests"
|
|
||||||
if room_tests.exists():
|
|
||||||
log.info(f" Copying {room} tester tests...")
|
|
||||||
target = output_dir / "station" / "tools" / "tester" / "tests"
|
|
||||||
copy_path(room_tests, target)
|
|
||||||
|
|
||||||
# Copy room-specific monitors if exists
|
|
||||||
room_monitors = room_cfg / "monitors"
|
|
||||||
if room_monitors.exists():
|
|
||||||
log.info(f" Copying {room} monitors...")
|
|
||||||
for monitor in room_monitors.iterdir():
|
|
||||||
if monitor.is_dir():
|
|
||||||
target = output_dir / "station" / "monitors" / monitor.name
|
|
||||||
copy_path(monitor, target)
|
|
||||||
|
|
||||||
# Copy room-specific models if exists
|
|
||||||
room_models = room_cfg / "models"
|
|
||||||
if room_models.exists():
|
|
||||||
log.info(f" Copying {room} models...")
|
|
||||||
target = output_dir / "models" / room
|
|
||||||
copy_path(room_models, target)
|
|
||||||
|
|
||||||
# Copy room-specific soleprint config (docker-compose.yml, etc)
|
|
||||||
room_soleprint = room_cfg / "soleprint"
|
room_soleprint = room_cfg / "soleprint"
|
||||||
if room_soleprint.exists():
|
if room_soleprint.exists():
|
||||||
log.info(f" Copying {room} soleprint config...")
|
|
||||||
for item in room_soleprint.iterdir():
|
for item in room_soleprint.iterdir():
|
||||||
|
if item.is_file():
|
||||||
copy_path(item, output_dir / item.name)
|
copy_path(item, output_dir / item.name)
|
||||||
|
|
||||||
|
# Merge room-specific system configs from soleprint subfolder
|
||||||
|
for system in ["artery", "atlas", "station"]:
|
||||||
|
room_system = room_soleprint / system
|
||||||
|
if room_system.exists():
|
||||||
|
log.info(f" Merging {room} {system}...")
|
||||||
|
merge_into(room_system, output_dir / system)
|
||||||
|
|
||||||
def build_dev(output_dir: Path, cfg_name: str | None = None):
|
|
||||||
"""
|
|
||||||
Build for development using copies (Docker-compatible).
|
|
||||||
|
|
||||||
Structure:
|
def build_soleprint(output_dir: Path, room: str):
|
||||||
gen/standalone/ or gen/<room>/
|
"""Build soleprint folder with core + room config merged."""
|
||||||
├── main.py
|
soleprint = SPR_ROOT / "soleprint"
|
||||||
├── run.py
|
|
||||||
├── index.html
|
|
||||||
├── requirements.txt
|
|
||||||
├── Dockerfile
|
|
||||||
├── dataloader/
|
|
||||||
├── artery/
|
|
||||||
├── atlas/
|
|
||||||
├── station/
|
|
||||||
├── data/
|
|
||||||
├── cfg/
|
|
||||||
├── .env.example # From cfg/<room>/.env.example
|
|
||||||
└── models/ # Generated
|
|
||||||
|
|
||||||
After editing source files, re-run `python build.py` to update.
|
|
||||||
"""
|
|
||||||
log.info("\n=== Building DEV environment ===")
|
|
||||||
log.info(f"SPR root: {SPR_ROOT}")
|
|
||||||
log.info(f"Output: {output_dir}")
|
|
||||||
if cfg_name:
|
|
||||||
log.info(f"Room cfg: {cfg_name}")
|
|
||||||
|
|
||||||
ensure_dir(output_dir)
|
|
||||||
|
|
||||||
# Soleprint core files
|
# Soleprint core files
|
||||||
log.info("\nCopying soleprint files...")
|
log.info("Copying soleprint core...")
|
||||||
soleprint = SPR_ROOT / "soleprint"
|
for name in [
|
||||||
copy_path(soleprint / "main.py", output_dir / "main.py")
|
"main.py",
|
||||||
copy_path(soleprint / "run.py", output_dir / "run.py")
|
"run.py",
|
||||||
copy_path(soleprint / "index.html", output_dir / "index.html")
|
"index.html",
|
||||||
copy_path(soleprint / "requirements.txt", output_dir / "requirements.txt")
|
"requirements.txt",
|
||||||
|
"Dockerfile",
|
||||||
|
]:
|
||||||
|
if (soleprint / name).exists():
|
||||||
|
copy_path(soleprint / name, output_dir / name)
|
||||||
copy_path(soleprint / "dataloader", output_dir / "dataloader")
|
copy_path(soleprint / "dataloader", output_dir / "dataloader")
|
||||||
if (soleprint / "Dockerfile").exists():
|
|
||||||
copy_path(soleprint / "Dockerfile", output_dir / "Dockerfile")
|
|
||||||
|
|
||||||
# System directories
|
# System directories
|
||||||
log.info("\nCopying systems...")
|
log.info("Copying systems...")
|
||||||
for system in ["artery", "atlas", "station"]:
|
for system in ["artery", "atlas", "station"]:
|
||||||
source = SPR_ROOT / system
|
source = soleprint / system
|
||||||
if source.exists():
|
if source.exists():
|
||||||
copy_path(source, output_dir / system)
|
copy_path(source, output_dir / system)
|
||||||
|
|
||||||
# Config (includes data/ from room)
|
# Room config (includes merging room-specific artery/atlas/station)
|
||||||
log.info("\nCopying config...")
|
copy_cfg(output_dir, room)
|
||||||
copy_cfg(output_dir, cfg_name)
|
|
||||||
|
|
||||||
# Models (generated)
|
# Generate models
|
||||||
log.info("\nGenerating models...")
|
log.info("Generating models...")
|
||||||
if not generate_models(output_dir, cfg_name):
|
if not generate_models(output_dir, room):
|
||||||
log.warning("Model generation failed, you may need to run it manually")
|
log.warning("Model generation failed")
|
||||||
|
|
||||||
log.info("\n✓ Dev build complete!")
|
|
||||||
log.info(f"\nTo run:")
|
|
||||||
log.info(f" cd {output_dir}")
|
|
||||||
log.info(f" python3 -m venv .venv")
|
|
||||||
log.info(f" .venv/bin/pip install -r requirements.txt")
|
|
||||||
log.info(f" .venv/bin/python run.py # Single-port bare-metal dev")
|
|
||||||
if cfg_name:
|
|
||||||
log.info(
|
|
||||||
f"\nAfter editing source, rebuild with: python build.py --cfg {cfg_name}"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
log.info(f"\nAfter editing source, rebuild with: python build.py")
|
|
||||||
|
|
||||||
|
|
||||||
def build_deploy(output_dir: Path, cfg_name: str | None = None):
|
def build(output_dir: Path, cfg_name: str | None = None):
|
||||||
"""
|
"""Build complete room instance."""
|
||||||
Build for deployment by copying all files (no symlinks).
|
|
||||||
"""
|
|
||||||
log.info("\n=== Building DEPLOY package ===")
|
|
||||||
log.info(f"SPR root: {SPR_ROOT}")
|
|
||||||
log.info(f"Output: {output_dir}")
|
|
||||||
if cfg_name:
|
|
||||||
log.info(f"Room cfg: {cfg_name}")
|
|
||||||
|
|
||||||
if output_dir.exists():
|
|
||||||
response = input(f"\nOutput directory exists. Overwrite? [y/N] ")
|
|
||||||
if response.lower() != "y":
|
|
||||||
log.info("Aborted.")
|
|
||||||
return
|
|
||||||
shutil.rmtree(output_dir)
|
|
||||||
|
|
||||||
ensure_dir(output_dir)
|
|
||||||
|
|
||||||
# Soleprint core files (copy)
|
|
||||||
log.info("\nCopying soleprint files...")
|
|
||||||
soleprint = SPR_ROOT / "soleprint"
|
|
||||||
copy_path(soleprint / "main.py", output_dir / "main.py")
|
|
||||||
copy_path(soleprint / "run.py", output_dir / "run.py")
|
|
||||||
copy_path(soleprint / "index.html", output_dir / "index.html")
|
|
||||||
copy_path(soleprint / "requirements.txt", output_dir / "requirements.txt")
|
|
||||||
copy_path(soleprint / "dataloader", output_dir / "dataloader")
|
|
||||||
if (soleprint / "Dockerfile").exists():
|
|
||||||
copy_path(soleprint / "Dockerfile", output_dir / "Dockerfile")
|
|
||||||
|
|
||||||
# System directories (copy)
|
|
||||||
log.info("\nCopying systems...")
|
|
||||||
for system in ["artery", "atlas", "station"]:
|
|
||||||
source = SPR_ROOT / system
|
|
||||||
if source.exists():
|
|
||||||
copy_path(source, output_dir / system)
|
|
||||||
|
|
||||||
# Config (includes data/ from room)
|
|
||||||
log.info("\nCopying config...")
|
|
||||||
copy_cfg(output_dir, cfg_name)
|
|
||||||
|
|
||||||
# Models (generate fresh) - pass output_dir, modelgen adds models/pydantic
|
|
||||||
log.info("\nGenerating models...")
|
|
||||||
if not generate_models(output_dir, cfg_name):
|
|
||||||
# Fallback: copy from gen if exists
|
|
||||||
room = cfg_name or "standalone"
|
room = cfg_name or "standalone"
|
||||||
existing = SPR_ROOT / "gen" / room / "models"
|
config = load_config(cfg_name)
|
||||||
if existing.exists():
|
managed = config.get("managed")
|
||||||
log.info(" Using existing models from gen/")
|
|
||||||
copy_path(existing, output_dir / "models")
|
|
||||||
|
|
||||||
# Copy schema.json for reference
|
log.info(f"\n=== Building {room} ===")
|
||||||
log.info("\nCopying schema...")
|
|
||||||
copy_path(SPR_ROOT / "schema.json", output_dir / "schema.json")
|
|
||||||
|
|
||||||
# Create run script
|
|
||||||
run_script = output_dir / "start.sh"
|
|
||||||
run_script.write_text("""#!/bin/bash
|
|
||||||
# Soleprint runner
|
|
||||||
cd "$(dirname "$0")"
|
|
||||||
|
|
||||||
if [ ! -d ".venv" ]; then
|
|
||||||
echo "Creating virtual environment..."
|
|
||||||
python3 -m venv .venv
|
|
||||||
.venv/bin/pip install -r requirements.txt
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Starting soleprint on http://localhost:12000"
|
|
||||||
.venv/bin/python main.py
|
|
||||||
""")
|
|
||||||
run_script.chmod(0o755)
|
|
||||||
log.info(" Created: start.sh")
|
|
||||||
|
|
||||||
total_files = count_files(output_dir)
|
|
||||||
log.info(f"\n✓ Deploy build complete! ({total_files} files)")
|
|
||||||
log.info(f"\nTo run:")
|
|
||||||
log.info(f" cd {output_dir}")
|
|
||||||
log.info(f" ./start.sh")
|
|
||||||
log.info(f"\nOr deploy to server:")
|
|
||||||
log.info(f" rsync -av {output_dir}/ server:/app/soleprint/")
|
|
||||||
log.info(f" ssh server 'cd /app/soleprint && ./start.sh'")
|
|
||||||
|
|
||||||
|
|
||||||
def build_models():
|
|
||||||
"""Only regenerate models."""
|
|
||||||
log.info("\n=== Generating models only ===")
|
|
||||||
|
|
||||||
output_dir = SPR_ROOT / "gen"
|
|
||||||
ensure_dir(output_dir)
|
ensure_dir(output_dir)
|
||||||
|
|
||||||
if generate_models(output_dir):
|
if managed:
|
||||||
log.info("\n✓ Models generated!")
|
# 3-folder structure: <managed>/, link/, soleprint/
|
||||||
|
build_managed(output_dir, room, config)
|
||||||
|
build_link(output_dir, room)
|
||||||
|
|
||||||
|
soleprint_name = config.get("framework", {}).get("name", "soleprint")
|
||||||
|
soleprint_dir = output_dir / soleprint_name
|
||||||
|
ensure_dir(soleprint_dir)
|
||||||
|
build_soleprint(soleprint_dir, room)
|
||||||
|
else:
|
||||||
|
# Standalone: everything in output_dir
|
||||||
|
build_soleprint(output_dir, room)
|
||||||
|
|
||||||
|
log.info(f"\n✓ Built: {output_dir}")
|
||||||
|
|
||||||
|
|
||||||
|
def build_models_only():
|
||||||
|
"""Only regenerate models."""
|
||||||
|
log.info("Generating models...")
|
||||||
|
if generate_models(SPR_ROOT / "gen"):
|
||||||
|
log.info("✓ Models generated")
|
||||||
else:
|
else:
|
||||||
log.error("Model generation failed")
|
log.error("Model generation failed")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(description="Soleprint Build Tool")
|
||||||
description="Soleprint Build Tool",
|
|
||||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
||||||
epilog=__doc__,
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument("--output", "-o", type=Path, help="Output directory")
|
||||||
"--output",
|
parser.add_argument("--cfg", "-c", type=str, help="Room config name")
|
||||||
"-o",
|
parser.add_argument("--all", action="store_true", help="Build all rooms")
|
||||||
type=Path,
|
parser.add_argument("--models", action="store_true", help="Only regenerate models")
|
||||||
default=None,
|
|
||||||
help="Output directory (default: gen/standalone/ or gen/<cfg>/)",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--cfg",
|
|
||||||
"-c",
|
|
||||||
type=str,
|
|
||||||
default=None,
|
|
||||||
help="Room config to include (e.g., 'myroom')",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--all",
|
|
||||||
action="store_true",
|
|
||||||
help="Build all configs (standalone + all rooms in cfg/)",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--deploy",
|
|
||||||
action="store_true",
|
|
||||||
help="Build for deployment (creates start.sh, schema.json)",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--models",
|
|
||||||
action="store_true",
|
|
||||||
help="Only regenerate models",
|
|
||||||
)
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.models:
|
if args.models:
|
||||||
build_models()
|
build_models_only()
|
||||||
elif args.deploy:
|
|
||||||
if not args.output:
|
|
||||||
log.error("--deploy requires --output")
|
|
||||||
sys.exit(1)
|
|
||||||
build_deploy(args.output.resolve(), args.cfg)
|
|
||||||
elif args.all:
|
elif args.all:
|
||||||
# Build standalone
|
build(SPR_ROOT / "gen" / "standalone", None)
|
||||||
build_dev(SPR_ROOT / "gen" / "standalone", None)
|
for room in (SPR_ROOT / "cfg").iterdir():
|
||||||
# Build all room configs
|
|
||||||
cfg_dir = SPR_ROOT / "cfg"
|
|
||||||
for room in cfg_dir.iterdir():
|
|
||||||
if room.is_dir() and room.name not in ("__pycache__", "standalone"):
|
if room.is_dir() and room.name not in ("__pycache__", "standalone"):
|
||||||
build_dev(SPR_ROOT / "gen" / room.name, room.name)
|
build(SPR_ROOT / "gen" / room.name, room.name)
|
||||||
else:
|
else:
|
||||||
# Determine output directory
|
|
||||||
if args.output:
|
if args.output:
|
||||||
output_dir = args.output.resolve()
|
output_dir = args.output.resolve()
|
||||||
elif args.cfg:
|
elif args.cfg:
|
||||||
output_dir = SPR_ROOT / "gen" / args.cfg
|
output_dir = SPR_ROOT / "gen" / args.cfg
|
||||||
else:
|
else:
|
||||||
output_dir = SPR_ROOT / "gen" / "standalone"
|
output_dir = SPR_ROOT / "gen" / "standalone"
|
||||||
build_dev(output_dir, args.cfg)
|
build(output_dir, args.cfg)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -36,11 +36,8 @@ DB_DUMP=test.sql
|
|||||||
BACKEND_PORT=8000
|
BACKEND_PORT=8000
|
||||||
FRONTEND_PORT=3000
|
FRONTEND_PORT=3000
|
||||||
|
|
||||||
# Soleprint ports
|
# Soleprint port
|
||||||
SOLEPRINT_PORT=12000
|
SOLEPRINT_PORT=12000
|
||||||
ARTERY_PORT=12001
|
|
||||||
ATLAS_PORT=12002
|
|
||||||
STATION_PORT=12003
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# BACKEND SERVER (Uvicorn)
|
# BACKEND SERVER (Uvicorn)
|
||||||
|
|||||||
@@ -8,6 +8,13 @@
|
|||||||
"icon": "👣",
|
"icon": "👣",
|
||||||
"hub_port": 12000
|
"hub_port": 12000
|
||||||
},
|
},
|
||||||
|
"managed": {
|
||||||
|
"name": "amar",
|
||||||
|
"repos": {
|
||||||
|
"backend": "/home/mariano/wdir/ama/amar_django_back",
|
||||||
|
"frontend": "/home/mariano/wdir/ama/amar_frontend"
|
||||||
|
}
|
||||||
|
},
|
||||||
"systems": [
|
"systems": [
|
||||||
{
|
{
|
||||||
"key": "data_flow",
|
"key": "data_flow",
|
||||||
@@ -15,7 +22,6 @@
|
|||||||
"slug": "artery",
|
"slug": "artery",
|
||||||
"title": "Artery",
|
"title": "Artery",
|
||||||
"tagline": "Todo lo vital",
|
"tagline": "Todo lo vital",
|
||||||
"port": 12001,
|
|
||||||
"icon": "💉"
|
"icon": "💉"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24,7 +30,6 @@
|
|||||||
"slug": "atlas",
|
"slug": "atlas",
|
||||||
"title": "Atlas",
|
"title": "Atlas",
|
||||||
"tagline": "Documentación accionable",
|
"tagline": "Documentación accionable",
|
||||||
"port": 12002,
|
|
||||||
"icon": "🗺️"
|
"icon": "🗺️"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -33,7 +38,6 @@
|
|||||||
"slug": "station",
|
"slug": "station",
|
||||||
"title": "Station",
|
"title": "Station",
|
||||||
"tagline": "Monitores, Entornos y Herramientas",
|
"tagline": "Monitores, Entornos y Herramientas",
|
||||||
"port": 12003,
|
|
||||||
"icon": "🎛️"
|
"icon": "🎛️"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,156 +0,0 @@
|
|||||||
"""
|
|
||||||
Pawprint Models - Platform Agnostic Definitions
|
|
||||||
|
|
||||||
Portable to: TypeScript, Pydantic, Django, SQLAlchemy, etc.
|
|
||||||
|
|
||||||
Hierarchy:
|
|
||||||
pawprint (abstract)
|
|
||||||
├── artery → Pulse = Vein + Nest + Larder
|
|
||||||
├── album → Book = Template + Larder
|
|
||||||
└── ward → Table = Tools + Nest + Larder
|
|
||||||
|
|
||||||
Shared components: Nest, Larder
|
|
||||||
System-specific: Vein (artery), Template (album), Tools (ward)
|
|
||||||
|
|
||||||
Rules:
|
|
||||||
- Larder in album generated from Template = "Book (written)"
|
|
||||||
- Same Larder exists independently in ward/artery
|
|
||||||
- Nest contains runtime configs, credentials, targets
|
|
||||||
- Larder contains data, provisions, stored content
|
|
||||||
"""
|
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
|
||||||
from typing import Optional, List
|
|
||||||
from enum import Enum
|
|
||||||
|
|
||||||
|
|
||||||
class Status(Enum):
|
|
||||||
PENDING = "pending"
|
|
||||||
PLANNED = "planned"
|
|
||||||
BUILDING = "building"
|
|
||||||
DEV = "dev"
|
|
||||||
LIVE = "live"
|
|
||||||
READY = "ready"
|
|
||||||
|
|
||||||
|
|
||||||
class System(Enum):
|
|
||||||
ARTERY = "artery"
|
|
||||||
ALBUM = "album"
|
|
||||||
WARD = "ward"
|
|
||||||
|
|
||||||
|
|
||||||
# === Shared Components ===
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Nest:
|
|
||||||
"""Runtime environment configuration.
|
|
||||||
|
|
||||||
Contains: credentials, targets, runtime configs.
|
|
||||||
Shared across: artery, ward
|
|
||||||
"""
|
|
||||||
name: str
|
|
||||||
status: Status = Status.PENDING
|
|
||||||
# References to actual config files/secrets
|
|
||||||
config_path: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Larder:
|
|
||||||
"""Data storage / provisions.
|
|
||||||
|
|
||||||
Contains: data, transforms, parsers, dumps.
|
|
||||||
Shared across: artery, album, ward
|
|
||||||
|
|
||||||
Note: When generated from Template in album, appears as "Book (written)"
|
|
||||||
but exists as independent Larder in ward/artery.
|
|
||||||
"""
|
|
||||||
name: str
|
|
||||||
status: Status = Status.PENDING
|
|
||||||
# Optional source template (if generated)
|
|
||||||
source_template: Optional[str] = None
|
|
||||||
# Path to data
|
|
||||||
data_path: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
# === System-Specific Components ===
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Vein:
|
|
||||||
"""Connector (artery-specific).
|
|
||||||
|
|
||||||
Single responsibility data connector.
|
|
||||||
Examples: jira, google, slack, whatsapp, cash, vnc
|
|
||||||
"""
|
|
||||||
name: str
|
|
||||||
status: Status = Status.PENDING
|
|
||||||
system: System = field(default=System.ARTERY, init=False)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Template:
|
|
||||||
"""Documentation template (album-specific).
|
|
||||||
|
|
||||||
Gherkin, BDD patterns, generators.
|
|
||||||
Examples: feature-form, gherkin
|
|
||||||
"""
|
|
||||||
name: str
|
|
||||||
status: Status = Status.PENDING
|
|
||||||
system: System = field(default=System.ALBUM, init=False)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Tool:
|
|
||||||
"""Execution tool (ward-specific).
|
|
||||||
|
|
||||||
Test runners, seeders, scripts.
|
|
||||||
"""
|
|
||||||
name: str
|
|
||||||
status: Status = Status.PENDING
|
|
||||||
system: System = field(default=System.WARD, init=False)
|
|
||||||
|
|
||||||
|
|
||||||
# === Composed Types ===
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Pulse:
|
|
||||||
"""Composed data flow (artery).
|
|
||||||
|
|
||||||
Pulse = Vein + Nest + Larder
|
|
||||||
"""
|
|
||||||
name: str
|
|
||||||
status: Status = Status.PENDING
|
|
||||||
vein: Optional[Vein] = None
|
|
||||||
nest: Optional[Nest] = None
|
|
||||||
larder: Optional[Larder] = None
|
|
||||||
system: System = field(default=System.ARTERY, init=False)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Book:
|
|
||||||
"""Composed documentation (album).
|
|
||||||
|
|
||||||
Book = Template + Larder
|
|
||||||
|
|
||||||
Note: Output larder can be referenced independently in other systems.
|
|
||||||
"""
|
|
||||||
name: str
|
|
||||||
status: Status = Status.PENDING
|
|
||||||
template: Optional[Template] = None
|
|
||||||
larder: Optional[Larder] = None
|
|
||||||
# If this book produces a larder, it's tracked here
|
|
||||||
output_larder: Optional[Larder] = None
|
|
||||||
system: System = field(default=System.ALBUM, init=False)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Table:
|
|
||||||
"""Composed execution bundle (ward).
|
|
||||||
|
|
||||||
Table = Tools + Nest + Larder
|
|
||||||
"""
|
|
||||||
name: str
|
|
||||||
status: Status = Status.PENDING
|
|
||||||
tools: List[Tool] = field(default_factory=list)
|
|
||||||
nest: Optional[Nest] = None
|
|
||||||
larder: Optional[Larder] = None
|
|
||||||
system: System = field(default=System.WARD, init=False)
|
|
||||||
@@ -1,191 +0,0 @@
|
|||||||
"""
|
|
||||||
Django models - Generated from schema.json
|
|
||||||
|
|
||||||
DO NOT EDIT MANUALLY - Regenerate from schema.json
|
|
||||||
"""
|
|
||||||
|
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
|
|
||||||
class Status(models.TextChoices):
|
|
||||||
PENDING = "pending", "Pending"
|
|
||||||
PLANNED = "planned", "Planned"
|
|
||||||
BUILDING = "building", "Building"
|
|
||||||
DEV = "dev", "Dev"
|
|
||||||
LIVE = "live", "Live"
|
|
||||||
READY = "ready", "Ready"
|
|
||||||
|
|
||||||
|
|
||||||
class System(models.TextChoices):
|
|
||||||
ARTERY = "artery", "Artery"
|
|
||||||
ALBUM = "album", "Album"
|
|
||||||
WARD = "ward", "Ward"
|
|
||||||
|
|
||||||
|
|
||||||
# === Shared Components ===
|
|
||||||
|
|
||||||
class Nest(models.Model):
|
|
||||||
"""Runtime environment configuration. Shared across artery, ward."""
|
|
||||||
name = models.CharField(max_length=255, help_text="Unique identifier")
|
|
||||||
slug = models.SlugField(max_length=255, unique=True, help_text="URL-friendly identifier")
|
|
||||||
title = models.CharField(max_length=255, help_text="Display title for UI")
|
|
||||||
status = models.ForeignKey(Status, on_delete=models.SET_NULL, blank=True, null=True, related_name="nests")
|
|
||||||
config_path = models.CharField(max_length=255, blank=True, null=True)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
db_table = "pawprint_nest"
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.title
|
|
||||||
|
|
||||||
|
|
||||||
class Larder(models.Model):
|
|
||||||
"""Data storage. When generated from Template = 'Book (written)'. Independent in ward/artery."""
|
|
||||||
name = models.CharField(max_length=255, help_text="Unique identifier")
|
|
||||||
slug = models.SlugField(max_length=255, unique=True, help_text="URL-friendly identifier")
|
|
||||||
title = models.CharField(max_length=255, help_text="Display title for UI")
|
|
||||||
status = models.ForeignKey(Status, on_delete=models.SET_NULL, blank=True, null=True, related_name="larders")
|
|
||||||
source_template = models.CharField(max_length=255, blank=True, null=True, help_text="Template name if generated")
|
|
||||||
data_path = models.CharField(max_length=255, blank=True, null=True, help_text="Path to data files")
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
db_table = "pawprint_larder"
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.title
|
|
||||||
|
|
||||||
|
|
||||||
# === System-Specific Components ===
|
|
||||||
|
|
||||||
class Vein(models.Model):
|
|
||||||
"""Connector (artery). Single responsibility."""
|
|
||||||
name = models.CharField(max_length=255, help_text="Unique identifier")
|
|
||||||
slug = models.SlugField(max_length=255, unique=True, help_text="URL-friendly identifier")
|
|
||||||
title = models.CharField(max_length=255, help_text="Display title for UI")
|
|
||||||
status = models.ForeignKey(Status, on_delete=models.SET_NULL, blank=True, null=True, related_name="veins")
|
|
||||||
system = models.CharField(max_length=20, default="artery", editable=False)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
db_table = "pawprint_vein"
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.title
|
|
||||||
|
|
||||||
|
|
||||||
class Template(models.Model):
|
|
||||||
"""Documentation template (album). Gherkin, BDD patterns."""
|
|
||||||
name = models.CharField(max_length=255, help_text="Unique identifier")
|
|
||||||
slug = models.SlugField(max_length=255, unique=True, help_text="URL-friendly identifier")
|
|
||||||
title = models.CharField(max_length=255, help_text="Display title for UI")
|
|
||||||
status = models.ForeignKey(Status, on_delete=models.SET_NULL, blank=True, null=True, related_name="templates")
|
|
||||||
template_path = models.CharField(max_length=255, blank=True, null=True, help_text="Path to template files")
|
|
||||||
system = models.CharField(max_length=20, default="album", editable=False)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
db_table = "pawprint_template"
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.title
|
|
||||||
|
|
||||||
|
|
||||||
class Tool(models.Model):
|
|
||||||
"""Execution tool (ward). Test runners, seeders."""
|
|
||||||
name = models.CharField(max_length=255, help_text="Unique identifier")
|
|
||||||
slug = models.SlugField(max_length=255, unique=True, help_text="URL-friendly identifier")
|
|
||||||
title = models.CharField(max_length=255, help_text="Display title for UI")
|
|
||||||
status = models.ForeignKey(Status, on_delete=models.SET_NULL, blank=True, null=True, related_name="tools")
|
|
||||||
system = models.CharField(max_length=20, default="ward", editable=False)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
db_table = "pawprint_tool"
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.title
|
|
||||||
|
|
||||||
|
|
||||||
class Monitor(models.Model):
|
|
||||||
"""Service monitor (ward). Health checks, status watchers."""
|
|
||||||
name = models.CharField(max_length=255, help_text="Unique identifier")
|
|
||||||
slug = models.SlugField(max_length=255, unique=True, help_text="URL-friendly identifier")
|
|
||||||
title = models.CharField(max_length=255, help_text="Display title for UI")
|
|
||||||
status = models.ForeignKey(Status, on_delete=models.SET_NULL, blank=True, null=True, related_name="monitors")
|
|
||||||
system = models.CharField(max_length=20, default="ward", editable=False)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
db_table = "pawprint_monitor"
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.title
|
|
||||||
|
|
||||||
|
|
||||||
class Cabinet(models.Model):
|
|
||||||
"""Tool cabinet (ward). Contains 0+ tools."""
|
|
||||||
name = models.CharField(max_length=255, help_text="Unique identifier")
|
|
||||||
slug = models.SlugField(max_length=255, unique=True, help_text="URL-friendly identifier")
|
|
||||||
title = models.CharField(max_length=255, help_text="Display title for UI")
|
|
||||||
status = models.ForeignKey(Status, on_delete=models.SET_NULL, blank=True, null=True, related_name="cabinets")
|
|
||||||
tools = models.ManyToManyField(Tool, blank=True)
|
|
||||||
system = models.CharField(max_length=20, default="ward", editable=False)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
db_table = "pawprint_cabinet"
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.title
|
|
||||||
|
|
||||||
|
|
||||||
# === Composed Types ===
|
|
||||||
|
|
||||||
class Pulse(models.Model):
|
|
||||||
"""Composed data flow (artery). Pulse = Vein + Nest + Larder."""
|
|
||||||
name = models.CharField(max_length=255, help_text="Unique identifier")
|
|
||||||
slug = models.SlugField(max_length=255, unique=True, help_text="URL-friendly identifier")
|
|
||||||
title = models.CharField(max_length=255, help_text="Display title for UI")
|
|
||||||
status = models.ForeignKey(Status, on_delete=models.SET_NULL, blank=True, null=True, related_name="pulses")
|
|
||||||
vein = models.ForeignKey(Vein, on_delete=models.SET_NULL, blank=True, null=True, related_name="pulses")
|
|
||||||
nest = models.ForeignKey(Nest, on_delete=models.SET_NULL, blank=True, null=True, related_name="pulses")
|
|
||||||
larder = models.ForeignKey(Larder, on_delete=models.SET_NULL, blank=True, null=True, related_name="pulses")
|
|
||||||
system = models.CharField(max_length=20, default="artery", editable=False)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
db_table = "pawprint_pulse"
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.title
|
|
||||||
|
|
||||||
|
|
||||||
class Book(models.Model):
|
|
||||||
"""Composed documentation (album). Book = Template + Larder."""
|
|
||||||
name = models.CharField(max_length=255, help_text="Unique identifier")
|
|
||||||
slug = models.SlugField(max_length=255, unique=True, help_text="URL-friendly identifier")
|
|
||||||
title = models.CharField(max_length=255, help_text="Display title for UI")
|
|
||||||
status = models.ForeignKey(Status, on_delete=models.SET_NULL, blank=True, null=True, related_name="books")
|
|
||||||
template = models.ForeignKey(Template, on_delete=models.SET_NULL, blank=True, null=True, related_name="books")
|
|
||||||
larder = models.ForeignKey(Larder, on_delete=models.SET_NULL, blank=True, null=True, related_name="books")
|
|
||||||
output_larder = models.ForeignKey(Larder, on_delete=models.SET_NULL, blank=True, null=True, related_name="books")
|
|
||||||
system = models.CharField(max_length=20, default="album", editable=False)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
db_table = "pawprint_book"
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.title
|
|
||||||
|
|
||||||
|
|
||||||
class Table(models.Model):
|
|
||||||
"""Composed execution bundle (ward). Table = Cabinet + Nest + Larders."""
|
|
||||||
name = models.CharField(max_length=255, help_text="Unique identifier")
|
|
||||||
slug = models.SlugField(max_length=255, unique=True, help_text="URL-friendly identifier")
|
|
||||||
title = models.CharField(max_length=255, help_text="Display title for UI")
|
|
||||||
status = models.ForeignKey(Status, on_delete=models.SET_NULL, blank=True, null=True, related_name="tables")
|
|
||||||
cabinet = models.ForeignKey(Cabinet, on_delete=models.SET_NULL, blank=True, null=True, related_name="tables")
|
|
||||||
nest = models.ForeignKey(Nest, on_delete=models.SET_NULL, blank=True, null=True, related_name="tables")
|
|
||||||
larders = models.ManyToManyField(Larder, blank=True)
|
|
||||||
system = models.CharField(max_length=20, default="ward", editable=False)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
db_table = "pawprint_table"
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.title
|
|
||||||
|
|
||||||
@@ -1,213 +0,0 @@
|
|||||||
// Prisma schema - Generated from schema.json
|
|
||||||
//
|
|
||||||
// DO NOT EDIT MANUALLY - Regenerate from schema.json
|
|
||||||
|
|
||||||
generator client {
|
|
||||||
provider = "prisma-client-js"
|
|
||||||
}
|
|
||||||
|
|
||||||
datasource db {
|
|
||||||
provider = "postgresql"
|
|
||||||
url = env("DATABASE_URL")
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Status {
|
|
||||||
PENDING
|
|
||||||
PLANNED
|
|
||||||
BUILDING
|
|
||||||
DEV
|
|
||||||
LIVE
|
|
||||||
READY
|
|
||||||
}
|
|
||||||
|
|
||||||
enum System {
|
|
||||||
ARTERY
|
|
||||||
ALBUM
|
|
||||||
WARD
|
|
||||||
}
|
|
||||||
|
|
||||||
// === Shared Components ===
|
|
||||||
|
|
||||||
/// Runtime environment configuration. Shared across artery, ward.
|
|
||||||
model Nest {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
|
|
||||||
name String @unique
|
|
||||||
slug String @unique
|
|
||||||
title String
|
|
||||||
status Status? @relation(fields: [statusId], references: [id])
|
|
||||||
statusId Int?
|
|
||||||
config_path String?
|
|
||||||
|
|
||||||
@@map("pawprint_nest")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Data storage. When generated from Template = 'Book (written)'. Independent in ward/artery.
|
|
||||||
model Larder {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
|
|
||||||
name String @unique
|
|
||||||
slug String @unique
|
|
||||||
title String
|
|
||||||
status Status? @relation(fields: [statusId], references: [id])
|
|
||||||
statusId Int?
|
|
||||||
source_template String?
|
|
||||||
data_path String?
|
|
||||||
|
|
||||||
@@map("pawprint_larder")
|
|
||||||
}
|
|
||||||
|
|
||||||
// === System-Specific Components ===
|
|
||||||
|
|
||||||
/// Connector (artery). Single responsibility.
|
|
||||||
model Vein {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
|
|
||||||
name String @unique
|
|
||||||
slug String @unique
|
|
||||||
title String
|
|
||||||
status Status? @relation(fields: [statusId], references: [id])
|
|
||||||
statusId Int?
|
|
||||||
system String @default("artery")
|
|
||||||
|
|
||||||
@@map("pawprint_vein")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Documentation template (album). Gherkin, BDD patterns.
|
|
||||||
model Template {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
|
|
||||||
name String @unique
|
|
||||||
slug String @unique
|
|
||||||
title String
|
|
||||||
status Status? @relation(fields: [statusId], references: [id])
|
|
||||||
statusId Int?
|
|
||||||
template_path String?
|
|
||||||
system String @default("album")
|
|
||||||
|
|
||||||
@@map("pawprint_template")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Execution tool (ward). Test runners, seeders.
|
|
||||||
model Tool {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
|
|
||||||
name String @unique
|
|
||||||
slug String @unique
|
|
||||||
title String
|
|
||||||
status Status? @relation(fields: [statusId], references: [id])
|
|
||||||
statusId Int?
|
|
||||||
system String @default("ward")
|
|
||||||
|
|
||||||
@@map("pawprint_tool")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Service monitor (ward). Health checks, status watchers.
|
|
||||||
model Monitor {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
|
|
||||||
name String @unique
|
|
||||||
slug String @unique
|
|
||||||
title String
|
|
||||||
status Status? @relation(fields: [statusId], references: [id])
|
|
||||||
statusId Int?
|
|
||||||
system String @default("ward")
|
|
||||||
|
|
||||||
@@map("pawprint_monitor")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tool cabinet (ward). Contains 0+ tools.
|
|
||||||
model Cabinet {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
|
|
||||||
name String @unique
|
|
||||||
slug String @unique
|
|
||||||
title String
|
|
||||||
status Status? @relation(fields: [statusId], references: [id])
|
|
||||||
statusId Int?
|
|
||||||
tools Tool[]
|
|
||||||
system String @default("ward")
|
|
||||||
|
|
||||||
@@map("pawprint_cabinet")
|
|
||||||
}
|
|
||||||
|
|
||||||
// === Composed Types ===
|
|
||||||
|
|
||||||
/// Composed data flow (artery). Pulse = Vein + Nest + Larder.
|
|
||||||
model Pulse {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
|
|
||||||
name String @unique
|
|
||||||
slug String @unique
|
|
||||||
title String
|
|
||||||
status Status? @relation(fields: [statusId], references: [id])
|
|
||||||
statusId Int?
|
|
||||||
vein Vein? @relation(fields: [veinId], references: [id])
|
|
||||||
veinId Int?
|
|
||||||
nest Nest? @relation(fields: [nestId], references: [id])
|
|
||||||
nestId Int?
|
|
||||||
larder Larder? @relation(fields: [larderId], references: [id])
|
|
||||||
larderId Int?
|
|
||||||
system String @default("artery")
|
|
||||||
|
|
||||||
@@map("pawprint_pulse")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Composed documentation (album). Book = Template + Larder.
|
|
||||||
model Book {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
|
|
||||||
name String @unique
|
|
||||||
slug String @unique
|
|
||||||
title String
|
|
||||||
status Status? @relation(fields: [statusId], references: [id])
|
|
||||||
statusId Int?
|
|
||||||
template Template? @relation(fields: [templateId], references: [id])
|
|
||||||
templateId Int?
|
|
||||||
larder Larder? @relation(fields: [larderId], references: [id])
|
|
||||||
larderId Int?
|
|
||||||
output_larder Larder? @relation(fields: [output_larderId], references: [id])
|
|
||||||
output_larderId Int?
|
|
||||||
system String @default("album")
|
|
||||||
|
|
||||||
@@map("pawprint_book")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Composed execution bundle (ward). Table = Cabinet + Nest + Larders.
|
|
||||||
model Table {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
|
|
||||||
name String @unique
|
|
||||||
slug String @unique
|
|
||||||
title String
|
|
||||||
status Status? @relation(fields: [statusId], references: [id])
|
|
||||||
statusId Int?
|
|
||||||
cabinet Cabinet? @relation(fields: [cabinetId], references: [id])
|
|
||||||
cabinetId Int?
|
|
||||||
nest Nest? @relation(fields: [nestId], references: [id])
|
|
||||||
nestId Int?
|
|
||||||
larders Larder[]
|
|
||||||
system String @default("ward")
|
|
||||||
|
|
||||||
@@map("pawprint_table")
|
|
||||||
}
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
"""
|
|
||||||
Pydantic models - Generated from schema.json
|
|
||||||
|
|
||||||
DO NOT EDIT MANUALLY - Regenerate from schema.json
|
|
||||||
"""
|
|
||||||
|
|
||||||
from enum import Enum
|
|
||||||
from typing import Optional, List, Literal
|
|
||||||
from pydantic import BaseModel, Field
|
|
||||||
|
|
||||||
|
|
||||||
class Status(str, Enum):
|
|
||||||
PENDING = "pending"
|
|
||||||
PLANNED = "planned"
|
|
||||||
BUILDING = "building"
|
|
||||||
DEV = "dev"
|
|
||||||
LIVE = "live"
|
|
||||||
READY = "ready"
|
|
||||||
|
|
||||||
|
|
||||||
class System(str, Enum):
|
|
||||||
ARTERY = "artery"
|
|
||||||
ALBUM = "album"
|
|
||||||
WARD = "ward"
|
|
||||||
|
|
||||||
|
|
||||||
class ToolType(str, Enum):
|
|
||||||
APP = "app"
|
|
||||||
CLI = "cli"
|
|
||||||
|
|
||||||
|
|
||||||
# === Shared Components ===
|
|
||||||
|
|
||||||
class Nest(BaseModel):
|
|
||||||
"""Runtime environment configuration. Shared across artery, ward."""
|
|
||||||
name: str # Unique identifier
|
|
||||||
slug: str # URL-friendly identifier
|
|
||||||
title: str # Display title for UI
|
|
||||||
status: Optional[Status] = None
|
|
||||||
config_path: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class Larder(BaseModel):
|
|
||||||
"""Data storage. When generated from Template = 'Book (written)'. Independent in ward/artery."""
|
|
||||||
name: str # Unique identifier
|
|
||||||
slug: str # URL-friendly identifier
|
|
||||||
title: str # Display title for UI
|
|
||||||
status: Optional[Status] = None
|
|
||||||
source_template: Optional[str] = None
|
|
||||||
data_path: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
# === System-Specific Components ===
|
|
||||||
|
|
||||||
class Vein(BaseModel):
|
|
||||||
"""Connector (artery). Single responsibility."""
|
|
||||||
name: str # Unique identifier
|
|
||||||
slug: str # URL-friendly identifier
|
|
||||||
title: str # Display title for UI
|
|
||||||
status: Optional[Status] = None
|
|
||||||
system: Literal["artery"] = "artery"
|
|
||||||
mock: Optional[bool] = None
|
|
||||||
description: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class Template(BaseModel):
|
|
||||||
"""Documentation template (album). Gherkin, BDD patterns."""
|
|
||||||
name: str # Unique identifier
|
|
||||||
slug: str # URL-friendly identifier
|
|
||||||
title: str # Display title for UI
|
|
||||||
status: Optional[Status] = None
|
|
||||||
template_path: Optional[str] = None
|
|
||||||
system: Literal["album"] = "album"
|
|
||||||
|
|
||||||
|
|
||||||
class Tool(BaseModel):
|
|
||||||
"""Execution tool (ward). Test runners, seeders."""
|
|
||||||
name: str # Unique identifier
|
|
||||||
slug: str # URL-friendly identifier
|
|
||||||
title: str # Display title for UI
|
|
||||||
status: Optional[Status] = None
|
|
||||||
system: Literal["ward"] = "ward"
|
|
||||||
type: Optional[ToolType] = None
|
|
||||||
description: Optional[str] = None
|
|
||||||
path: Optional[str] = None
|
|
||||||
url: Optional[str] = None
|
|
||||||
cli: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class Monitor(BaseModel):
|
|
||||||
"""Service monitor (ward). Health checks, status watchers."""
|
|
||||||
name: str # Unique identifier
|
|
||||||
slug: str # URL-friendly identifier
|
|
||||||
title: str # Display title for UI
|
|
||||||
status: Optional[Status] = None
|
|
||||||
system: Literal["ward"] = "ward"
|
|
||||||
|
|
||||||
|
|
||||||
class Cabinet(BaseModel):
|
|
||||||
"""Tool cabinet (ward). Contains 0+ tools."""
|
|
||||||
name: str # Unique identifier
|
|
||||||
slug: str # URL-friendly identifier
|
|
||||||
title: str # Display title for UI
|
|
||||||
status: Optional[Status] = None
|
|
||||||
tools: List[Tool] = Field(default_factory=list)
|
|
||||||
system: Literal["ward"] = "ward"
|
|
||||||
|
|
||||||
|
|
||||||
# === Composed Types ===
|
|
||||||
|
|
||||||
class Pulse(BaseModel):
|
|
||||||
"""Composed data flow (artery). Pulse = Vein + Nest + Larder."""
|
|
||||||
name: str # Unique identifier
|
|
||||||
slug: str # URL-friendly identifier
|
|
||||||
title: str # Display title for UI
|
|
||||||
status: Optional[Status] = None
|
|
||||||
vein: Optional[Vein] = None
|
|
||||||
nest: Optional[Nest] = None
|
|
||||||
larder: Optional[Larder] = None
|
|
||||||
system: Literal["artery"] = "artery"
|
|
||||||
|
|
||||||
|
|
||||||
class Book(BaseModel):
|
|
||||||
"""Composed documentation (album). Book = Template + Larder."""
|
|
||||||
name: str # Unique identifier
|
|
||||||
slug: str # URL-friendly identifier
|
|
||||||
title: str # Display title for UI
|
|
||||||
status: Optional[Status] = None
|
|
||||||
template: Optional[Template] = None
|
|
||||||
larder: Optional[Larder] = None
|
|
||||||
output_larder: Optional[Larder] = None
|
|
||||||
system: Literal["album"] = "album"
|
|
||||||
|
|
||||||
|
|
||||||
class Table(BaseModel):
|
|
||||||
"""Composed execution bundle (ward). Table = Cabinet + Nest + Larders."""
|
|
||||||
name: str # Unique identifier
|
|
||||||
slug: str # URL-friendly identifier
|
|
||||||
title: str # Display title for UI
|
|
||||||
status: Optional[Status] = None
|
|
||||||
cabinet: Optional[Cabinet] = None
|
|
||||||
nest: Optional[Nest] = None
|
|
||||||
larders: List[Larder] = Field(default_factory=list)
|
|
||||||
system: Literal["ward"] = "ward"
|
|
||||||
|
|
||||||
|
|
||||||
# === Collection wrappers for JSON files ===
|
|
||||||
|
|
||||||
class NestCollection(BaseModel):
|
|
||||||
items: List[Nest] = Field(default_factory=list)
|
|
||||||
|
|
||||||
|
|
||||||
class LarderCollection(BaseModel):
|
|
||||||
items: List[Larder] = Field(default_factory=list)
|
|
||||||
|
|
||||||
|
|
||||||
class VeinCollection(BaseModel):
|
|
||||||
items: List[Vein] = Field(default_factory=list)
|
|
||||||
|
|
||||||
|
|
||||||
class TemplateCollection(BaseModel):
|
|
||||||
items: List[Template] = Field(default_factory=list)
|
|
||||||
|
|
||||||
|
|
||||||
class ToolCollection(BaseModel):
|
|
||||||
items: List[Tool] = Field(default_factory=list)
|
|
||||||
|
|
||||||
|
|
||||||
class MonitorCollection(BaseModel):
|
|
||||||
items: List[Monitor] = Field(default_factory=list)
|
|
||||||
|
|
||||||
|
|
||||||
class CabinetCollection(BaseModel):
|
|
||||||
items: List[Cabinet] = Field(default_factory=list)
|
|
||||||
|
|
||||||
|
|
||||||
class PulseCollection(BaseModel):
|
|
||||||
items: List[Pulse] = Field(default_factory=list)
|
|
||||||
|
|
||||||
|
|
||||||
class BookCollection(BaseModel):
|
|
||||||
items: List[Book] = Field(default_factory=list)
|
|
||||||
|
|
||||||
|
|
||||||
class TableCollection(BaseModel):
|
|
||||||
items: List[Table] = Field(default_factory=list)
|
|
||||||
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
||||||
"title": "Pawprint Models",
|
|
||||||
"description": "Platform-agnostic model definitions. Portable to TypeScript, Pydantic, Django, Prisma.",
|
|
||||||
"definitions": {
|
|
||||||
"Status": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": ["pending", "planned", "building", "dev", "live", "ready"]
|
|
||||||
},
|
|
||||||
"System": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": ["artery", "album", "ward"]
|
|
||||||
},
|
|
||||||
"Nest": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Runtime environment configuration. Shared across artery, ward.",
|
|
||||||
"properties": {
|
|
||||||
"name": { "type": "string", "description": "Unique identifier" },
|
|
||||||
"slug": { "type": "string", "description": "URL-friendly identifier" },
|
|
||||||
"title": { "type": "string", "description": "Display title for UI" },
|
|
||||||
"status": { "$ref": "#/definitions/Status" },
|
|
||||||
"config_path": { "type": "string" }
|
|
||||||
},
|
|
||||||
"required": ["name", "slug", "title"]
|
|
||||||
},
|
|
||||||
"Larder": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Data storage. When generated from Template = 'Book (written)'. Independent in ward/artery.",
|
|
||||||
"properties": {
|
|
||||||
"name": { "type": "string", "description": "Unique identifier" },
|
|
||||||
"slug": { "type": "string", "description": "URL-friendly identifier" },
|
|
||||||
"title": { "type": "string", "description": "Display title for UI" },
|
|
||||||
"status": { "$ref": "#/definitions/Status" },
|
|
||||||
"source_template": { "type": "string", "description": "Template name if generated" },
|
|
||||||
"data_path": { "type": "string", "description": "Path to data files" }
|
|
||||||
},
|
|
||||||
"required": ["name", "slug", "title"]
|
|
||||||
},
|
|
||||||
"Vein": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Connector (artery). Single responsibility.",
|
|
||||||
"properties": {
|
|
||||||
"name": { "type": "string", "description": "Unique identifier" },
|
|
||||||
"slug": { "type": "string", "description": "URL-friendly identifier" },
|
|
||||||
"title": { "type": "string", "description": "Display title for UI" },
|
|
||||||
"status": { "$ref": "#/definitions/Status" },
|
|
||||||
"system": { "const": "artery" }
|
|
||||||
},
|
|
||||||
"required": ["name", "slug", "title"]
|
|
||||||
},
|
|
||||||
"Template": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Documentation template (album). Gherkin, BDD patterns.",
|
|
||||||
"properties": {
|
|
||||||
"name": { "type": "string", "description": "Unique identifier" },
|
|
||||||
"slug": { "type": "string", "description": "URL-friendly identifier" },
|
|
||||||
"title": { "type": "string", "description": "Display title for UI" },
|
|
||||||
"status": { "$ref": "#/definitions/Status" },
|
|
||||||
"template_path": { "type": "string", "description": "Path to template files" },
|
|
||||||
"system": { "const": "album" }
|
|
||||||
},
|
|
||||||
"required": ["name", "slug", "title"]
|
|
||||||
},
|
|
||||||
"ToolType": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": ["app", "cli"],
|
|
||||||
"description": "Type of tool: app (web UI) or cli (command line)"
|
|
||||||
},
|
|
||||||
"Tool": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Execution tool (ward). Test runners, seeders.",
|
|
||||||
"properties": {
|
|
||||||
"name": { "type": "string", "description": "Unique identifier" },
|
|
||||||
"slug": { "type": "string", "description": "URL-friendly identifier" },
|
|
||||||
"title": { "type": "string", "description": "Display title for UI" },
|
|
||||||
"status": { "$ref": "#/definitions/Status" },
|
|
||||||
"system": { "const": "ward" },
|
|
||||||
"type": { "$ref": "#/definitions/ToolType" },
|
|
||||||
"description": { "type": "string", "description": "Human-readable description" },
|
|
||||||
"path": { "type": "string", "description": "Path to tool source" },
|
|
||||||
"url": { "type": "string", "description": "URL path for app tools" },
|
|
||||||
"cli": { "type": "string", "description": "CLI command for cli tools" }
|
|
||||||
},
|
|
||||||
"required": ["name", "slug", "title"]
|
|
||||||
},
|
|
||||||
"Monitor": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Service monitor (ward). Health checks, status watchers.",
|
|
||||||
"properties": {
|
|
||||||
"name": { "type": "string", "description": "Unique identifier" },
|
|
||||||
"slug": { "type": "string", "description": "URL-friendly identifier" },
|
|
||||||
"title": { "type": "string", "description": "Display title for UI" },
|
|
||||||
"status": { "$ref": "#/definitions/Status" },
|
|
||||||
"system": { "const": "ward" }
|
|
||||||
},
|
|
||||||
"required": ["name", "slug", "title"]
|
|
||||||
},
|
|
||||||
"Cabinet": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Tool cabinet (ward). Contains 0+ tools.",
|
|
||||||
"properties": {
|
|
||||||
"name": { "type": "string", "description": "Unique identifier" },
|
|
||||||
"slug": { "type": "string", "description": "URL-friendly identifier" },
|
|
||||||
"title": { "type": "string", "description": "Display title for UI" },
|
|
||||||
"status": { "$ref": "#/definitions/Status" },
|
|
||||||
"tools": {
|
|
||||||
"type": "array",
|
|
||||||
"items": { "$ref": "#/definitions/Tool" }
|
|
||||||
},
|
|
||||||
"system": { "const": "ward" }
|
|
||||||
},
|
|
||||||
"required": ["name", "slug", "title"]
|
|
||||||
},
|
|
||||||
"Pulse": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Composed data flow (artery). Pulse = Vein + Nest + Larder.",
|
|
||||||
"properties": {
|
|
||||||
"name": { "type": "string", "description": "Unique identifier" },
|
|
||||||
"slug": { "type": "string", "description": "URL-friendly identifier" },
|
|
||||||
"title": { "type": "string", "description": "Display title for UI" },
|
|
||||||
"status": { "$ref": "#/definitions/Status" },
|
|
||||||
"vein": { "$ref": "#/definitions/Vein" },
|
|
||||||
"nest": { "$ref": "#/definitions/Nest" },
|
|
||||||
"larder": { "$ref": "#/definitions/Larder" },
|
|
||||||
"system": { "const": "artery" }
|
|
||||||
},
|
|
||||||
"required": ["name", "slug", "title"]
|
|
||||||
},
|
|
||||||
"Book": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Composed documentation (album). Book = Template + Larder.",
|
|
||||||
"properties": {
|
|
||||||
"name": { "type": "string", "description": "Unique identifier" },
|
|
||||||
"slug": { "type": "string", "description": "URL-friendly identifier" },
|
|
||||||
"title": { "type": "string", "description": "Display title for UI" },
|
|
||||||
"status": { "$ref": "#/definitions/Status" },
|
|
||||||
"template": { "$ref": "#/definitions/Template" },
|
|
||||||
"larder": { "$ref": "#/definitions/Larder" },
|
|
||||||
"output_larder": { "$ref": "#/definitions/Larder" },
|
|
||||||
"system": { "const": "album" }
|
|
||||||
},
|
|
||||||
"required": ["name", "slug", "title"]
|
|
||||||
},
|
|
||||||
"Table": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Composed execution bundle (ward). Table = Cabinet + Nest + Larders.",
|
|
||||||
"properties": {
|
|
||||||
"name": { "type": "string", "description": "Unique identifier" },
|
|
||||||
"slug": { "type": "string", "description": "URL-friendly identifier" },
|
|
||||||
"title": { "type": "string", "description": "Display title for UI" },
|
|
||||||
"status": { "$ref": "#/definitions/Status" },
|
|
||||||
"cabinet": { "$ref": "#/definitions/Cabinet" },
|
|
||||||
"nest": { "$ref": "#/definitions/Nest" },
|
|
||||||
"larders": {
|
|
||||||
"type": "array",
|
|
||||||
"items": { "$ref": "#/definitions/Larder" }
|
|
||||||
},
|
|
||||||
"system": { "const": "ward" }
|
|
||||||
},
|
|
||||||
"required": ["name", "slug", "title"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -21,9 +21,6 @@ SOLEPRINT_BARE_PATH=/home/mariano/wdir/spr/gen
|
|||||||
# PORTS
|
# PORTS
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
SOLEPRINT_PORT=12000
|
SOLEPRINT_PORT=12000
|
||||||
ARTERY_PORT=12001
|
|
||||||
ATLAS_PORT=12002
|
|
||||||
STATION_PORT=12003
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# DATABASE (amar's DB for station tools)
|
# DATABASE (amar's DB for station tools)
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |