From e4052374db254538762a2f3ebda1c4aae2b4196e Mon Sep 17 00:00:00 2001 From: buenosairesam Date: Tue, 20 Jan 2026 05:31:26 -0300 Subject: [PATCH] major restructure --- CLAUDE.md | 183 ++-- README.md | 24 +- .../amar-arch/01-backend-architecture.dot | 184 ---- .../amar-arch/01-backend-architecture.svg | 585 ----------- .../amar-arch/02-frontend-architecture.dot | 184 ---- .../amar-arch/02-frontend-architecture.svg | 595 ----------- atlas/books/amar-arch/03-data-model.dot | 234 ----- atlas/books/amar-arch/03-data-model.svg | 976 ------------------ .../books/amar-arch/04-data-model-simple.dot | 195 ---- .../books/amar-arch/04-data-model-simple.svg | 540 ---------- atlas/books/amar-arch/graph.html | 120 --- atlas/books/amar-arch/index.html | 245 ----- atlas/books/amar-arch/styles.css | 565 ---------- .../media-analyzer/architecture_diagram.svg | 645 ------------ atlas/books/media-analyzer/asciidiagram.md | 153 --- atlas/books/media-analyzer/convo | 172 --- atlas/books/media-analyzer/diagram.md | 33 - .../media-analyzer/system_architecture.dot | 224 ---- .../architecture/01-system-overview.dot | 78 -- .../architecture/01-system-overview.svg | 193 ---- .../sysmonstm/architecture/02-data-flow.dot | 83 -- .../sysmonstm/architecture/02-data-flow.svg | 217 ---- .../sysmonstm/architecture/03-deployment.dot | 95 -- .../sysmonstm/architecture/03-deployment.svg | 221 ---- .../architecture/04-grpc-services.dot | 67 -- .../architecture/04-grpc-services.svg | 171 --- atlas/books/sysmonstm/architecture/graph.html | 130 --- atlas/books/sysmonstm/architecture/index.html | 262 ----- atlas/books/sysmonstm/architecture/styles.css | 343 ------ .../images/01-architecture-overview.svg | 106 -- .../explainer/images/02-grpc-streaming.svg | 83 -- .../explainer/images/03-storage-tiers.svg | 83 -- .../explainer/images/04-event-driven.svg | 84 -- .../explainer/images/05-domain-mapping.svg | 80 -- .../images/06-deskmeter-enhancement.svg | 125 --- .../sysmonstm/explainer/other-applications.md | 282 ----- .../sysmonstm-from-start-to-finish.md | 421 -------- atlas/books/sysmonstm/explainer/viewer.html | 425 -------- atlas/books/sysmonstm/index.html | 393 ------- .../sysmonstm/static/prism/prism-bash.min.js | 1 - .../sysmonstm/static/prism/prism-json.min.js | 1 - .../static/prism/prism-protobuf.min.js | 1 - .../static/prism/prism-python.min.js | 1 - atlas/static/prism/prism-gherkin.min.js | 1 - atlas/static/prism/prism-line-numbers.min.css | 1 - atlas/static/prism/prism-line-numbers.min.js | 1 - atlas/static/prism/prism-tomorrow.min.css | 1 - atlas/static/prism/prism.min.js | 1 - build.py | 526 +++++----- .../amar/artery}/shunts/amar/.env.example | 0 .../amar/artery}/shunts/amar/README.md | 0 .../amar/artery}/shunts/amar/__init__.py | 0 .../amar/artery}/shunts/amar/api/__init__.py | 0 .../amar/artery}/shunts/amar/api/routes.py | 0 .../amar/artery}/shunts/amar/core/__init__.py | 0 .../amar/artery}/shunts/amar/core/config.py | 0 .../amar/artery}/shunts/amar/main.py | 0 .../amar/artery}/shunts/amar/requirements.txt | 0 .../amar/artery}/shunts/amar/run.py | 0 .../artery}/shunts/amar/templates/index.html | 0 .../books/arch}/01-backend-architecture.dot | 0 .../books/arch}/01-backend-architecture.svg | 0 .../books/arch}/02-frontend-architecture.dot | 0 .../books/arch}/02-frontend-architecture.svg | 0 .../amar/atlas/books/arch}/03-data-model.dot | 0 .../amar/atlas/books/arch}/03-data-model.svg | 0 .../books/arch}/04-data-model-simple.dot | 0 .../books/arch}/04-data-model-simple.svg | 0 .../amar/atlas/books/arch}/graph.html | 0 .../amar/atlas/books/arch}/index.html | 0 .../amar/atlas/books/arch}/styles.css | 0 .../amar/atlas/books}/drive-index/index.html | 0 .../amar/atlas/books}/drive-index/index.md | 0 .../books}/feature-form-samples/CLAUDE.md | 0 .../books}/feature-form-samples/detail.html | 0 .../feature-form-samples/feature-form/.larder | 0 .../backoffice/01-gestion-solicitudes.md | 0 .../backoffice/02-gestion-usuarios.md | 0 .../backoffice/03-gestion-servicios.md | 0 .../feature-form/backoffice/04-reembolsos.md | 0 .../feature-form/backoffice/05-reportes.md | 0 .../feature-form/pet-owner/01-registro.md | 0 .../pet-owner/02-reservar-turno.md | 0 .../pet-owner/03-gestion-mascotas.md | 0 .../feature-form/pet-owner/04-pago-turno.md | 0 .../pet-owner/05-historial-medico.md | 0 .../veterinarian/01-aceptar-solicitud.md | 0 .../veterinarian/02-gestion-agenda.md | 0 .../veterinarian/03-realizar-visita.md | 0 .../veterinarian/04-zonas-cobertura.md | 0 .../veterinarian/05-historial-pacientes.md | 0 .../books}/feature-form-samples/index.html | 0 .../template/feature-form.md | 0 .../atlas/books}/gherkin-samples/CLAUDE.md | 0 .../atlas/books}/gherkin-samples/detail.html | 0 .../backoffice/01-gestion-solicitudes.feature | 0 .../en/backoffice/02-gestion-usuarios.feature | 0 .../backoffice/03-gestion-servicios.feature | 0 .../en/backoffice/04-reembolsos.feature | 0 .../en/backoffice/05-reportes.feature | 0 .../en/pet-owner/01-registro.feature | 0 .../en/pet-owner/02-reservar-turno.feature | 0 .../en/pet-owner/03-gestion-mascotas.feature | 0 .../en/pet-owner/04-pago-turno.feature | 0 .../en/pet-owner/05-historial-medico.feature | 0 .../veterinarian/01-aceptar-solicitud.feature | 0 .../en/veterinarian/02-gestion-agenda.feature | 0 .../veterinarian/03-realizar-visita.feature | 0 .../veterinarian/04-zonas-cobertura.feature | 0 .../05-historial-pacientes.feature | 0 .../backoffice/01-gestion-solicitudes.feature | 0 .../es/backoffice/02-gestion-usuarios.feature | 0 .../backoffice/03-gestion-servicios.feature | 0 .../es/backoffice/04-reembolsos.feature | 0 .../es/backoffice/05-reportes.feature | 0 .../es/pet-owner/01-registro.feature | 0 .../es/pet-owner/02-reservar-turno.feature | 0 .../es/pet-owner/03-gestion-mascotas.feature | 0 .../es/pet-owner/04-pago-turno.feature | 0 .../es/pet-owner/05-historial-medico.feature | 0 .../veterinarian/01-aceptar-solicitud.feature | 0 .../es/veterinarian/02-gestion-agenda.feature | 0 .../veterinarian/03-realizar-visita.feature | 0 .../veterinarian/04-zonas-cobertura.feature | 0 .../05-historial-pacientes.feature | 0 .../atlas/books}/gherkin-samples/index.html | 0 cfg/amar/config.json | 7 + .../{ => managed/ctrl}/Dockerfile.backend | 0 .../{ => managed/ctrl}/Dockerfile.frontend | 0 .../{ => managed/ctrl}/docker-compose.yml | 0 .../{ => station}/monitors/turnos/__init__.py | 0 .../{ => station}/monitors/turnos/index.html | 0 .../{ => station}/monitors/turnos/list.html | 0 .../{ => station}/monitors/turnos/main.py | 0 .../tools}/databrowse/depot/scenarios.json | 0 .../tools}/databrowse/depot/schema.json | 0 .../tools}/databrowse/depot/views.json | 0 .../amar/station}/tools/datagen/amar.py | 0 .../tools}/tester/tests/README.md | 0 .../tools}/tester/tests/__init__.py | 0 .../{ => station/tools}/tester/tests/base.py | 0 .../tools}/tester/tests/conftest.py | 0 .../tools}/tester/tests/endpoints.py | 0 .../tools}/tester/tests/helpers.py | 0 .../tools}/tester/tests/mascotas/__init__.py | 0 .../tester/tests/mascotas/test_coverage.py | 0 .../tester/tests/mascotas/test_pet_owners.py | 0 .../tools}/tester/tests/mascotas/test_pets.py | 0 .../tools}/tester/tests/productos/__init__.py | 0 .../tester/tests/productos/test_cart.py | 0 .../tester/tests/productos/test_categories.py | 0 .../tester/tests/productos/test_services.py | 0 .../tester/tests/solicitudes/__init__.py | 0 .../solicitudes/test_service_requests.py | 0 .../tools}/tester/tests/workflows/__init__.py | 0 .../tests/workflows/test_turnero_general.py | 0 {artery => soleprint/artery}/__init__.py | 0 {artery => soleprint/artery}/index.html | 0 {artery => soleprint/artery}/room/README.md | 0 {artery => soleprint/artery}/room/__init__.py | 0 .../artery}/room/ctrl/build.sh | 0 .../artery}/room/ctrl/logs.sh | 0 .../artery}/room/ctrl/start.sh | 0 .../artery}/room/ctrl/status.sh | 0 .../artery}/room/ctrl/stop.sh | 0 .../artery}/shunts/__init__.py | 0 .../artery}/shunts/example/README.md | 0 .../shunts/example/depot/responses.json | 0 .../artery}/shunts/example/main.py | 0 .../artery}/shunts/mercadopago/.env.example | 0 .../artery}/shunts/mercadopago/README.md | 0 .../artery}/shunts/mercadopago/__init__.py | 0 .../shunts/mercadopago/api/__init__.py | 0 .../artery}/shunts/mercadopago/api/routes.py | 0 .../shunts/mercadopago/core/__init__.py | 0 .../artery}/shunts/mercadopago/core/config.py | 0 .../artery}/shunts/mercadopago/main.py | 0 .../shunts/mercadopago/requirements.txt | 0 .../artery}/shunts/mercadopago/run.py | 0 .../shunts/mercadopago/templates/index.html | 0 .../artery}/veins/PATTERNS.md | 0 .../artery}/veins/__init__.py | 0 {artery => soleprint/artery}/veins/base.py | 0 .../artery}/veins/google/.env.example | 0 .../artery}/veins/google/README.md | 0 .../artery}/veins/google/__init__.py | 0 .../artery}/veins/google/api/__init__.py | 0 .../artery}/veins/google/api/routes.py | 0 .../artery}/veins/google/core/__init__.py | 0 .../artery}/veins/google/core/config.py | 0 .../artery}/veins/google/core/oauth.py | 0 .../artery}/veins/google/core/sheets.py | 0 .../artery}/veins/google/main.py | 0 .../artery}/veins/google/models/__init__.py | 0 .../artery}/veins/google/models/formatter.py | 0 .../veins/google/models/spreadsheet.py | 0 .../artery}/veins/google/requirements.txt | 0 .../artery}/veins/google/run.py | 0 .../artery}/veins/google/storage/.gitignore | 0 .../artery}/veins/jira/.env.example | 0 .../artery}/veins/jira/README.md | 0 .../artery}/veins/jira/__init__.py | 0 .../artery}/veins/jira/api/__init__.py | 0 .../artery}/veins/jira/api/routes.py | 0 .../artery}/veins/jira/core/__init__.py | 0 .../artery}/veins/jira/core/auth.py | 0 .../artery}/veins/jira/core/client.py | 0 .../artery}/veins/jira/core/config.py | 0 .../artery}/veins/jira/core/query.py | 0 .../artery}/veins/jira/main.py | 0 .../artery}/veins/jira/models/__init__.py | 0 .../artery}/veins/jira/models/formatter.py | 0 .../artery}/veins/jira/models/ticket.py | 0 .../artery}/veins/jira/requirements.txt | 0 .../artery}/veins/jira/run.py | 0 {artery => soleprint/artery}/veins/oauth.py | 0 .../artery}/veins/slack/__init__.py | 0 .../artery}/veins/slack/api/__init__.py | 0 .../artery}/veins/slack/api/routes.py | 0 .../artery}/veins/slack/core/__init__.py | 0 .../artery}/veins/slack/core/auth.py | 0 .../artery}/veins/slack/core/client.py | 0 .../artery}/veins/slack/core/config.py | 0 .../artery}/veins/slack/main.py | 0 .../artery}/veins/slack/models/__init__.py | 0 .../artery}/veins/slack/models/formatter.py | 0 .../artery}/veins/slack/models/message.py | 0 .../artery}/veins/slack/requirements.txt | 0 .../artery}/veins/slack/run.py | 0 {atlas => soleprint/atlas}/.dockerignore | 0 {atlas => soleprint/atlas}/.gitignore | 0 {atlas => soleprint/atlas}/CLAUDE.md | 0 {atlas => soleprint/atlas}/__init__.py | 0 {atlas => soleprint/atlas}/book-template.html | 0 .../atlas/books}/feature-flow/CLAUDE.md | 0 .../atlas/books}/feature-flow/index-en.html | 0 .../atlas/books}/feature-flow/index-es.html | 0 {atlas => soleprint/atlas}/index.html | 0 {atlas => soleprint/atlas}/main.py | 0 {atlas => soleprint/atlas}/requirements.txt | 0 .../atlas}/static/prism/prism-gherkin.min.js | 0 .../static/prism/prism-line-numbers.min.css | 0 .../static/prism/prism-line-numbers.min.js | 0 .../static/prism/prism-tomorrow.min.css | 0 .../atlas}/static/prism/prism.min.js | 0 soleprint/generate.html | 512 +++++++++ soleprint/main.py | 183 +++- {station => soleprint/station}/__init__.py | 0 {station => soleprint/station}/index.html | 0 .../station}/monitors/__init__.py | 0 .../station}/monitors/databrowse/README.md | 0 .../station}/monitors/databrowse/depot/.depot | 0 .../monitors/databrowse/depot/scenarios.json | 0 .../monitors/databrowse/depot/schema.json | 0 .../monitors/databrowse/depot/views.json | 0 .../station}/monitors/databrowse/index.html | 0 .../station}/monitors/databrowse/main.py | 0 .../station}/monitors/databrowse/view.html | 0 .../station}/tools/__init__.py | 0 .../station}/tools/datagen/README.md | 0 .../station}/tools/datagen/__init__.py | 0 .../station}/tools/hub/README.md | 0 .../station}/tools/hub/firewalld.sh | 0 .../station}/tools/hub/iptables.sh | 0 .../station}/tools/hub/ports | 0 .../station}/tools/hub/ufw.sh | 0 .../station}/tools/hub/update-ports.sh | 0 .../station}/tools/infra/README.md | 0 .../station}/tools/infra/aws/Pulumi.yaml | 0 .../station}/tools/infra/aws/__main__.py | 0 .../station}/tools/infra/aws/requirements.txt | 0 .../tools/infra/digitalocean/Pulumi.yaml | 0 .../tools/infra/digitalocean/__main__.py | 0 .../tools/infra/digitalocean/requirements.txt | 0 .../station}/tools/infra/gcp/Pulumi.yaml | 0 .../station}/tools/infra/gcp/__main__.py | 0 .../station}/tools/infra/gcp/requirements.txt | 0 .../station}/tools/infra/shared/__init__.py | 0 .../station}/tools/infra/shared/config.py | 0 .../station}/tools/modelgen/__init__.py | 0 .../station}/tools/modelgen/__main__.py | 0 .../station}/tools/modelgen/config_loader.py | 0 .../tools/modelgen/model_generator.py | 0 .../station}/tools/sbwrapper/README.md | 0 .../station}/tools/sbwrapper/config.json | 0 .../station}/tools/sbwrapper/index.html | 0 .../station}/tools/sbwrapper/sidebar.css | 0 .../station}/tools/sbwrapper/sidebar.js | 0 .../station}/tools/tester/.env | 0 .../tools/tester/ENHANCEMENT_DESIGN.md | 0 .../station}/tools/tester/README.md | 0 .../tools/tester/SESSION_6_IMPLEMENTATION.md | 0 .../station}/tools/tester/__init__.py | 0 .../station}/tools/tester/__main__.py | 0 .../station}/tools/tester/api.py | 0 .../tools/tester/artifacts/.gitignore | 0 .../station}/tools/tester/base.py | 0 .../station}/tools/tester/cli.py | 0 .../station}/tools/tester/config.py | 0 .../station}/tools/tester/core.py | 0 .../station}/tools/tester/endpoints.py | 0 .../station}/tools/tester/environments.json | 0 .../station}/tools/tester/features/.gitignore | 0 .../station}/tools/tester/get-api-key.sh | 0 .../station}/tools/tester/gherkin/__init__.py | 0 .../station}/tools/tester/gherkin/mapper.py | 0 .../station}/tools/tester/gherkin/parser.py | 0 .../station}/tools/tester/gherkin/sync.py | 0 .../station}/tools/tester/helpers.py | 0 .../station}/tools/tester/index.py | 0 .../tools/tester/playwright/README.md | 0 .../tools/tester/playwright/__init__.py | 0 .../tools/tester/playwright/artifacts.py | 0 .../tools/tester/playwright/discovery.py | 0 .../tools/tester/playwright/runner.py | 0 .../tools/tester/templates/filters.html | 0 .../tools/tester/templates/filters_v2.html | 0 .../tools/tester/templates/index.html | 0 .../station}/tools/tester/tests/README.md | 0 .../station}/tools/tester/tests/__init__.py | 0 .../tools/tester/tests/_dev/__init__.py | 0 .../tools/tester/tests/_dev/test_health.py | 0 .../station}/tools/tester/tests/base.py | 0 .../station}/tools/tester/tests/conftest.py | 0 .../station}/tools/tester/tests/endpoints.py | 0 .../tools/tester/tests/example/__init__.py | 0 .../tools/tester/tests/example/test_health.py | 0 .../station}/tools/tester/tests/helpers.py | 0 328 files changed, 1018 insertions(+), 10018 deletions(-) delete mode 100644 atlas/books/amar-arch/01-backend-architecture.dot delete mode 100644 atlas/books/amar-arch/01-backend-architecture.svg delete mode 100644 atlas/books/amar-arch/02-frontend-architecture.dot delete mode 100644 atlas/books/amar-arch/02-frontend-architecture.svg delete mode 100644 atlas/books/amar-arch/03-data-model.dot delete mode 100644 atlas/books/amar-arch/03-data-model.svg delete mode 100644 atlas/books/amar-arch/04-data-model-simple.dot delete mode 100644 atlas/books/amar-arch/04-data-model-simple.svg delete mode 100644 atlas/books/amar-arch/graph.html delete mode 100644 atlas/books/amar-arch/index.html delete mode 100644 atlas/books/amar-arch/styles.css delete mode 100644 atlas/books/media-analyzer/architecture_diagram.svg delete mode 100644 atlas/books/media-analyzer/asciidiagram.md delete mode 100644 atlas/books/media-analyzer/convo delete mode 100644 atlas/books/media-analyzer/diagram.md delete mode 100644 atlas/books/media-analyzer/system_architecture.dot delete mode 100644 atlas/books/sysmonstm/architecture/01-system-overview.dot delete mode 100644 atlas/books/sysmonstm/architecture/01-system-overview.svg delete mode 100644 atlas/books/sysmonstm/architecture/02-data-flow.dot delete mode 100644 atlas/books/sysmonstm/architecture/02-data-flow.svg delete mode 100644 atlas/books/sysmonstm/architecture/03-deployment.dot delete mode 100644 atlas/books/sysmonstm/architecture/03-deployment.svg delete mode 100644 atlas/books/sysmonstm/architecture/04-grpc-services.dot delete mode 100644 atlas/books/sysmonstm/architecture/04-grpc-services.svg delete mode 100644 atlas/books/sysmonstm/architecture/graph.html delete mode 100644 atlas/books/sysmonstm/architecture/index.html delete mode 100644 atlas/books/sysmonstm/architecture/styles.css delete mode 100644 atlas/books/sysmonstm/explainer/images/01-architecture-overview.svg delete mode 100644 atlas/books/sysmonstm/explainer/images/02-grpc-streaming.svg delete mode 100644 atlas/books/sysmonstm/explainer/images/03-storage-tiers.svg delete mode 100644 atlas/books/sysmonstm/explainer/images/04-event-driven.svg delete mode 100644 atlas/books/sysmonstm/explainer/images/05-domain-mapping.svg delete mode 100644 atlas/books/sysmonstm/explainer/images/06-deskmeter-enhancement.svg delete mode 100644 atlas/books/sysmonstm/explainer/other-applications.md delete mode 100644 atlas/books/sysmonstm/explainer/sysmonstm-from-start-to-finish.md delete mode 100644 atlas/books/sysmonstm/explainer/viewer.html delete mode 100644 atlas/books/sysmonstm/index.html delete mode 100644 atlas/books/sysmonstm/static/prism/prism-bash.min.js delete mode 100644 atlas/books/sysmonstm/static/prism/prism-json.min.js delete mode 100644 atlas/books/sysmonstm/static/prism/prism-protobuf.min.js delete mode 100644 atlas/books/sysmonstm/static/prism/prism-python.min.js delete mode 100644 atlas/static/prism/prism-gherkin.min.js delete mode 100644 atlas/static/prism/prism-line-numbers.min.css delete mode 100644 atlas/static/prism/prism-line-numbers.min.js delete mode 100644 atlas/static/prism/prism-tomorrow.min.css delete mode 100644 atlas/static/prism/prism.min.js rename {artery => cfg/amar/artery}/shunts/amar/.env.example (100%) rename {artery => cfg/amar/artery}/shunts/amar/README.md (100%) rename {artery => cfg/amar/artery}/shunts/amar/__init__.py (100%) rename {artery => cfg/amar/artery}/shunts/amar/api/__init__.py (100%) rename {artery => cfg/amar/artery}/shunts/amar/api/routes.py (100%) rename {artery => cfg/amar/artery}/shunts/amar/core/__init__.py (100%) rename {artery => cfg/amar/artery}/shunts/amar/core/config.py (100%) rename {artery => cfg/amar/artery}/shunts/amar/main.py (100%) rename {artery => cfg/amar/artery}/shunts/amar/requirements.txt (100%) rename {artery => cfg/amar/artery}/shunts/amar/run.py (100%) rename {artery => cfg/amar/artery}/shunts/amar/templates/index.html (100%) rename {atlas/book/arch-model => cfg/amar/atlas/books/arch}/01-backend-architecture.dot (100%) rename {atlas/book/arch-model => cfg/amar/atlas/books/arch}/01-backend-architecture.svg (100%) rename {atlas/book/arch-model => cfg/amar/atlas/books/arch}/02-frontend-architecture.dot (100%) rename {atlas/book/arch-model => cfg/amar/atlas/books/arch}/02-frontend-architecture.svg (100%) rename {atlas/book/arch-model => cfg/amar/atlas/books/arch}/03-data-model.dot (100%) rename {atlas/book/arch-model => cfg/amar/atlas/books/arch}/03-data-model.svg (100%) rename {atlas/book/arch-model => cfg/amar/atlas/books/arch}/04-data-model-simple.dot (100%) rename {atlas/book/arch-model => cfg/amar/atlas/books/arch}/04-data-model-simple.svg (100%) rename {atlas/book/arch-model => cfg/amar/atlas/books/arch}/graph.html (100%) rename {atlas/book/arch-model => cfg/amar/atlas/books/arch}/index.html (100%) rename {atlas/book/arch-model => cfg/amar/atlas/books/arch}/styles.css (100%) rename {atlas/book => cfg/amar/atlas/books}/drive-index/index.html (100%) rename {atlas/book => cfg/amar/atlas/books}/drive-index/index.md (100%) rename {atlas/book => cfg/amar/atlas/books}/feature-form-samples/CLAUDE.md (100%) rename {atlas/book => cfg/amar/atlas/books}/feature-form-samples/detail.html (100%) rename {atlas/book => cfg/amar/atlas/books}/feature-form-samples/feature-form/.larder (100%) rename {atlas/book => cfg/amar/atlas/books}/feature-form-samples/feature-form/backoffice/01-gestion-solicitudes.md (100%) rename {atlas/book => cfg/amar/atlas/books}/feature-form-samples/feature-form/backoffice/02-gestion-usuarios.md (100%) rename {atlas/book => cfg/amar/atlas/books}/feature-form-samples/feature-form/backoffice/03-gestion-servicios.md (100%) rename {atlas/book => cfg/amar/atlas/books}/feature-form-samples/feature-form/backoffice/04-reembolsos.md (100%) rename {atlas/book => cfg/amar/atlas/books}/feature-form-samples/feature-form/backoffice/05-reportes.md (100%) rename {atlas/book => cfg/amar/atlas/books}/feature-form-samples/feature-form/pet-owner/01-registro.md (100%) rename {atlas/book => cfg/amar/atlas/books}/feature-form-samples/feature-form/pet-owner/02-reservar-turno.md (100%) rename {atlas/book => cfg/amar/atlas/books}/feature-form-samples/feature-form/pet-owner/03-gestion-mascotas.md (100%) rename {atlas/book => cfg/amar/atlas/books}/feature-form-samples/feature-form/pet-owner/04-pago-turno.md (100%) rename {atlas/book => cfg/amar/atlas/books}/feature-form-samples/feature-form/pet-owner/05-historial-medico.md (100%) rename {atlas/book => cfg/amar/atlas/books}/feature-form-samples/feature-form/veterinarian/01-aceptar-solicitud.md (100%) rename {atlas/book => cfg/amar/atlas/books}/feature-form-samples/feature-form/veterinarian/02-gestion-agenda.md (100%) rename {atlas/book => cfg/amar/atlas/books}/feature-form-samples/feature-form/veterinarian/03-realizar-visita.md (100%) rename {atlas/book => cfg/amar/atlas/books}/feature-form-samples/feature-form/veterinarian/04-zonas-cobertura.md (100%) rename {atlas/book => cfg/amar/atlas/books}/feature-form-samples/feature-form/veterinarian/05-historial-pacientes.md (100%) rename {atlas/book => cfg/amar/atlas/books}/feature-form-samples/index.html (100%) rename {atlas/book => cfg/amar/atlas/books}/feature-form-samples/template/feature-form.md (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/CLAUDE.md (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/detail.html (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/en/backoffice/01-gestion-solicitudes.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/en/backoffice/02-gestion-usuarios.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/en/backoffice/03-gestion-servicios.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/en/backoffice/04-reembolsos.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/en/backoffice/05-reportes.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/en/pet-owner/01-registro.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/en/pet-owner/02-reservar-turno.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/en/pet-owner/03-gestion-mascotas.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/en/pet-owner/04-pago-turno.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/en/pet-owner/05-historial-medico.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/en/veterinarian/01-aceptar-solicitud.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/en/veterinarian/02-gestion-agenda.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/en/veterinarian/03-realizar-visita.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/en/veterinarian/04-zonas-cobertura.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/en/veterinarian/05-historial-pacientes.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/es/backoffice/01-gestion-solicitudes.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/es/backoffice/02-gestion-usuarios.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/es/backoffice/03-gestion-servicios.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/es/backoffice/04-reembolsos.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/es/backoffice/05-reportes.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/es/pet-owner/01-registro.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/es/pet-owner/02-reservar-turno.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/es/pet-owner/03-gestion-mascotas.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/es/pet-owner/04-pago-turno.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/es/pet-owner/05-historial-medico.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/es/veterinarian/01-aceptar-solicitud.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/es/veterinarian/02-gestion-agenda.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/es/veterinarian/03-realizar-visita.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/es/veterinarian/04-zonas-cobertura.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/es/veterinarian/05-historial-pacientes.feature (100%) rename {atlas/book => cfg/amar/atlas/books}/gherkin-samples/index.html (100%) rename cfg/amar/{ => managed/ctrl}/Dockerfile.backend (100%) rename cfg/amar/{ => managed/ctrl}/Dockerfile.frontend (100%) rename cfg/amar/{ => managed/ctrl}/docker-compose.yml (100%) rename cfg/amar/{ => station}/monitors/turnos/__init__.py (100%) rename cfg/amar/{ => station}/monitors/turnos/index.html (100%) rename cfg/amar/{ => station}/monitors/turnos/list.html (100%) rename cfg/amar/{ => station}/monitors/turnos/main.py (100%) rename cfg/amar/{ => station/tools}/databrowse/depot/scenarios.json (100%) rename cfg/amar/{ => station/tools}/databrowse/depot/schema.json (100%) rename cfg/amar/{ => station/tools}/databrowse/depot/views.json (100%) rename {station => cfg/amar/station}/tools/datagen/amar.py (100%) rename cfg/amar/{ => station/tools}/tester/tests/README.md (100%) rename cfg/amar/{ => station/tools}/tester/tests/__init__.py (100%) rename cfg/amar/{ => station/tools}/tester/tests/base.py (100%) rename cfg/amar/{ => station/tools}/tester/tests/conftest.py (100%) rename cfg/amar/{ => station/tools}/tester/tests/endpoints.py (100%) rename cfg/amar/{ => station/tools}/tester/tests/helpers.py (100%) rename cfg/amar/{ => station/tools}/tester/tests/mascotas/__init__.py (100%) rename cfg/amar/{ => station/tools}/tester/tests/mascotas/test_coverage.py (100%) rename cfg/amar/{ => station/tools}/tester/tests/mascotas/test_pet_owners.py (100%) rename cfg/amar/{ => station/tools}/tester/tests/mascotas/test_pets.py (100%) rename cfg/amar/{ => station/tools}/tester/tests/productos/__init__.py (100%) rename cfg/amar/{ => station/tools}/tester/tests/productos/test_cart.py (100%) rename cfg/amar/{ => station/tools}/tester/tests/productos/test_categories.py (100%) rename cfg/amar/{ => station/tools}/tester/tests/productos/test_services.py (100%) rename cfg/amar/{ => station/tools}/tester/tests/solicitudes/__init__.py (100%) rename cfg/amar/{ => station/tools}/tester/tests/solicitudes/test_service_requests.py (100%) rename cfg/amar/{ => station/tools}/tester/tests/workflows/__init__.py (100%) rename cfg/amar/{ => station/tools}/tester/tests/workflows/test_turnero_general.py (100%) rename {artery => soleprint/artery}/__init__.py (100%) rename {artery => soleprint/artery}/index.html (100%) rename {artery => soleprint/artery}/room/README.md (100%) rename {artery => soleprint/artery}/room/__init__.py (100%) rename {artery => soleprint/artery}/room/ctrl/build.sh (100%) rename {artery => soleprint/artery}/room/ctrl/logs.sh (100%) rename {artery => soleprint/artery}/room/ctrl/start.sh (100%) rename {artery => soleprint/artery}/room/ctrl/status.sh (100%) rename {artery => soleprint/artery}/room/ctrl/stop.sh (100%) rename {artery => soleprint/artery}/shunts/__init__.py (100%) rename {artery => soleprint/artery}/shunts/example/README.md (100%) rename {artery => soleprint/artery}/shunts/example/depot/responses.json (100%) rename {artery => soleprint/artery}/shunts/example/main.py (100%) rename {artery => soleprint/artery}/shunts/mercadopago/.env.example (100%) rename {artery => soleprint/artery}/shunts/mercadopago/README.md (100%) rename {artery => soleprint/artery}/shunts/mercadopago/__init__.py (100%) rename {artery => soleprint/artery}/shunts/mercadopago/api/__init__.py (100%) rename {artery => soleprint/artery}/shunts/mercadopago/api/routes.py (100%) rename {artery => soleprint/artery}/shunts/mercadopago/core/__init__.py (100%) rename {artery => soleprint/artery}/shunts/mercadopago/core/config.py (100%) rename {artery => soleprint/artery}/shunts/mercadopago/main.py (100%) rename {artery => soleprint/artery}/shunts/mercadopago/requirements.txt (100%) rename {artery => soleprint/artery}/shunts/mercadopago/run.py (100%) rename {artery => soleprint/artery}/shunts/mercadopago/templates/index.html (100%) rename {artery => soleprint/artery}/veins/PATTERNS.md (100%) rename {artery => soleprint/artery}/veins/__init__.py (100%) rename {artery => soleprint/artery}/veins/base.py (100%) rename {artery => soleprint/artery}/veins/google/.env.example (100%) rename {artery => soleprint/artery}/veins/google/README.md (100%) rename {artery => soleprint/artery}/veins/google/__init__.py (100%) rename {artery => soleprint/artery}/veins/google/api/__init__.py (100%) rename {artery => soleprint/artery}/veins/google/api/routes.py (100%) rename {artery => soleprint/artery}/veins/google/core/__init__.py (100%) rename {artery => soleprint/artery}/veins/google/core/config.py (100%) rename {artery => soleprint/artery}/veins/google/core/oauth.py (100%) rename {artery => soleprint/artery}/veins/google/core/sheets.py (100%) rename {artery => soleprint/artery}/veins/google/main.py (100%) rename {artery => soleprint/artery}/veins/google/models/__init__.py (100%) rename {artery => soleprint/artery}/veins/google/models/formatter.py (100%) rename {artery => soleprint/artery}/veins/google/models/spreadsheet.py (100%) rename {artery => soleprint/artery}/veins/google/requirements.txt (100%) rename {artery => soleprint/artery}/veins/google/run.py (100%) rename {artery => soleprint/artery}/veins/google/storage/.gitignore (100%) rename {artery => soleprint/artery}/veins/jira/.env.example (100%) rename {artery => soleprint/artery}/veins/jira/README.md (100%) rename {artery => soleprint/artery}/veins/jira/__init__.py (100%) rename {artery => soleprint/artery}/veins/jira/api/__init__.py (100%) rename {artery => soleprint/artery}/veins/jira/api/routes.py (100%) rename {artery => soleprint/artery}/veins/jira/core/__init__.py (100%) rename {artery => soleprint/artery}/veins/jira/core/auth.py (100%) rename {artery => soleprint/artery}/veins/jira/core/client.py (100%) rename {artery => soleprint/artery}/veins/jira/core/config.py (100%) rename {artery => soleprint/artery}/veins/jira/core/query.py (100%) rename {artery => soleprint/artery}/veins/jira/main.py (100%) rename {artery => soleprint/artery}/veins/jira/models/__init__.py (100%) rename {artery => soleprint/artery}/veins/jira/models/formatter.py (100%) rename {artery => soleprint/artery}/veins/jira/models/ticket.py (100%) rename {artery => soleprint/artery}/veins/jira/requirements.txt (100%) rename {artery => soleprint/artery}/veins/jira/run.py (100%) rename {artery => soleprint/artery}/veins/oauth.py (100%) rename {artery => soleprint/artery}/veins/slack/__init__.py (100%) rename {artery => soleprint/artery}/veins/slack/api/__init__.py (100%) rename {artery => soleprint/artery}/veins/slack/api/routes.py (100%) rename {artery => soleprint/artery}/veins/slack/core/__init__.py (100%) rename {artery => soleprint/artery}/veins/slack/core/auth.py (100%) rename {artery => soleprint/artery}/veins/slack/core/client.py (100%) rename {artery => soleprint/artery}/veins/slack/core/config.py (100%) rename {artery => soleprint/artery}/veins/slack/main.py (100%) rename {artery => soleprint/artery}/veins/slack/models/__init__.py (100%) rename {artery => soleprint/artery}/veins/slack/models/formatter.py (100%) rename {artery => soleprint/artery}/veins/slack/models/message.py (100%) rename {artery => soleprint/artery}/veins/slack/requirements.txt (100%) rename {artery => soleprint/artery}/veins/slack/run.py (100%) rename {atlas => soleprint/atlas}/.dockerignore (100%) rename {atlas => soleprint/atlas}/.gitignore (100%) rename {atlas => soleprint/atlas}/CLAUDE.md (100%) rename {atlas => soleprint/atlas}/__init__.py (100%) rename {atlas => soleprint/atlas}/book-template.html (100%) rename {atlas/book => soleprint/atlas/books}/feature-flow/CLAUDE.md (100%) rename {atlas/book => soleprint/atlas/books}/feature-flow/index-en.html (100%) rename {atlas/book => soleprint/atlas/books}/feature-flow/index-es.html (100%) rename {atlas => soleprint/atlas}/index.html (100%) rename {atlas => soleprint/atlas}/main.py (100%) rename {atlas => soleprint/atlas}/requirements.txt (100%) rename {atlas/books/sysmonstm => soleprint/atlas}/static/prism/prism-gherkin.min.js (100%) rename {atlas/books/sysmonstm => soleprint/atlas}/static/prism/prism-line-numbers.min.css (100%) rename {atlas/books/sysmonstm => soleprint/atlas}/static/prism/prism-line-numbers.min.js (100%) rename {atlas/books/sysmonstm => soleprint/atlas}/static/prism/prism-tomorrow.min.css (100%) rename {atlas/books/sysmonstm => soleprint/atlas}/static/prism/prism.min.js (100%) create mode 100644 soleprint/generate.html rename {station => soleprint/station}/__init__.py (100%) rename {station => soleprint/station}/index.html (100%) rename {station => soleprint/station}/monitors/__init__.py (100%) rename {station => soleprint/station}/monitors/databrowse/README.md (100%) rename {station => soleprint/station}/monitors/databrowse/depot/.depot (100%) rename {station => soleprint/station}/monitors/databrowse/depot/scenarios.json (100%) rename {station => soleprint/station}/monitors/databrowse/depot/schema.json (100%) rename {station => soleprint/station}/monitors/databrowse/depot/views.json (100%) rename {station => soleprint/station}/monitors/databrowse/index.html (100%) rename {station => soleprint/station}/monitors/databrowse/main.py (100%) rename {station => soleprint/station}/monitors/databrowse/view.html (100%) rename {station => soleprint/station}/tools/__init__.py (100%) rename {station => soleprint/station}/tools/datagen/README.md (100%) rename {station => soleprint/station}/tools/datagen/__init__.py (100%) rename {station => soleprint/station}/tools/hub/README.md (100%) rename {station => soleprint/station}/tools/hub/firewalld.sh (100%) rename {station => soleprint/station}/tools/hub/iptables.sh (100%) rename {station => soleprint/station}/tools/hub/ports (100%) rename {station => soleprint/station}/tools/hub/ufw.sh (100%) rename {station => soleprint/station}/tools/hub/update-ports.sh (100%) rename {station => soleprint/station}/tools/infra/README.md (100%) rename {station => soleprint/station}/tools/infra/aws/Pulumi.yaml (100%) rename {station => soleprint/station}/tools/infra/aws/__main__.py (100%) rename {station => soleprint/station}/tools/infra/aws/requirements.txt (100%) rename {station => soleprint/station}/tools/infra/digitalocean/Pulumi.yaml (100%) rename {station => soleprint/station}/tools/infra/digitalocean/__main__.py (100%) rename {station => soleprint/station}/tools/infra/digitalocean/requirements.txt (100%) rename {station => soleprint/station}/tools/infra/gcp/Pulumi.yaml (100%) rename {station => soleprint/station}/tools/infra/gcp/__main__.py (100%) rename {station => soleprint/station}/tools/infra/gcp/requirements.txt (100%) rename {station => soleprint/station}/tools/infra/shared/__init__.py (100%) rename {station => soleprint/station}/tools/infra/shared/config.py (100%) rename {station => soleprint/station}/tools/modelgen/__init__.py (100%) rename {station => soleprint/station}/tools/modelgen/__main__.py (100%) rename {station => soleprint/station}/tools/modelgen/config_loader.py (100%) rename {station => soleprint/station}/tools/modelgen/model_generator.py (100%) rename {station => soleprint/station}/tools/sbwrapper/README.md (100%) rename {station => soleprint/station}/tools/sbwrapper/config.json (100%) rename {station => soleprint/station}/tools/sbwrapper/index.html (100%) rename {station => soleprint/station}/tools/sbwrapper/sidebar.css (100%) rename {station => soleprint/station}/tools/sbwrapper/sidebar.js (100%) rename {station => soleprint/station}/tools/tester/.env (100%) rename {station => soleprint/station}/tools/tester/ENHANCEMENT_DESIGN.md (100%) rename {station => soleprint/station}/tools/tester/README.md (100%) rename {station => soleprint/station}/tools/tester/SESSION_6_IMPLEMENTATION.md (100%) rename {station => soleprint/station}/tools/tester/__init__.py (100%) rename {station => soleprint/station}/tools/tester/__main__.py (100%) rename {station => soleprint/station}/tools/tester/api.py (100%) rename {station => soleprint/station}/tools/tester/artifacts/.gitignore (100%) rename {station => soleprint/station}/tools/tester/base.py (100%) rename {station => soleprint/station}/tools/tester/cli.py (100%) rename {station => soleprint/station}/tools/tester/config.py (100%) rename {station => soleprint/station}/tools/tester/core.py (100%) rename {station => soleprint/station}/tools/tester/endpoints.py (100%) rename {station => soleprint/station}/tools/tester/environments.json (100%) rename {station => soleprint/station}/tools/tester/features/.gitignore (100%) rename {station => soleprint/station}/tools/tester/get-api-key.sh (100%) rename {station => soleprint/station}/tools/tester/gherkin/__init__.py (100%) rename {station => soleprint/station}/tools/tester/gherkin/mapper.py (100%) rename {station => soleprint/station}/tools/tester/gherkin/parser.py (100%) rename {station => soleprint/station}/tools/tester/gherkin/sync.py (100%) rename {station => soleprint/station}/tools/tester/helpers.py (100%) rename {station => soleprint/station}/tools/tester/index.py (100%) rename {station => soleprint/station}/tools/tester/playwright/README.md (100%) rename {station => soleprint/station}/tools/tester/playwright/__init__.py (100%) rename {station => soleprint/station}/tools/tester/playwright/artifacts.py (100%) rename {station => soleprint/station}/tools/tester/playwright/discovery.py (100%) rename {station => soleprint/station}/tools/tester/playwright/runner.py (100%) rename {station => soleprint/station}/tools/tester/templates/filters.html (100%) rename {station => soleprint/station}/tools/tester/templates/filters_v2.html (100%) rename {station => soleprint/station}/tools/tester/templates/index.html (100%) rename {station => soleprint/station}/tools/tester/tests/README.md (100%) rename {station => soleprint/station}/tools/tester/tests/__init__.py (100%) rename {station => soleprint/station}/tools/tester/tests/_dev/__init__.py (100%) rename {station => soleprint/station}/tools/tester/tests/_dev/test_health.py (100%) rename {station => soleprint/station}/tools/tester/tests/base.py (100%) rename {station => soleprint/station}/tools/tester/tests/conftest.py (100%) rename {station => soleprint/station}/tools/tester/tests/endpoints.py (100%) rename {station => soleprint/station}/tools/tester/tests/example/__init__.py (100%) rename {station => soleprint/station}/tools/tester/tests/example/test_health.py (100%) rename {station => soleprint/station}/tools/tester/tests/helpers.py (100%) diff --git a/CLAUDE.md b/CLAUDE.md index 2e4b760..0198df0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,51 +17,58 @@ spr/ ├── schema.json # Source of truth for models ├── 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 │ ├── standalone/ # Base soleprint config -│ │ ├── config.json # Framework branding/terminology -│ │ └── data/ # Data files (veins.json, shunts.json, etc.) -│ └── amar/ # AMAR room config -│ ├── config.json # Can rebrand (e.g., "pawprint") -│ ├── data/ # Room-specific data files -│ ├── .env.example -│ ├── docker-compose.yml -│ ├── soleprint/ # Soleprint Docker config for this room -│ ├── databrowse/depot/ -│ ├── tester/tests/ +│ │ ├── config.json +│ │ ├── data/ +│ │ └── soleprint/ +│ │ +│ └── amar/ # Amar room config +│ ├── config.json +│ ├── data/ +│ ├── artery/ # Amar-specific (merged into output) +│ │ └── shunts/amar/ +│ ├── atlas/ # Amar-specific books +│ │ └── books/ +│ ├── station/ # Amar-specific tools config +│ │ └── tools/datagen/ +│ ├── link/ # Bridge to managed app +│ ├── soleprint/ # Soleprint docker config +│ ├── databrowse/ +│ ├── tester/ │ ├── monitors/ │ └── models/ │ ├── 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) - ├── standalone/ # python build.py dev - └── amar/ # python build.py dev --cfg amar + ├── standalone/ + └── amar/ ``` ## The Four Systems @@ -87,63 +94,47 @@ Vein ──────► Pulse ──────► Plexus 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 Each room in `cfg/` has: -- `config.json` - Framework branding/terminology (can rebrand soleprint) -- `data/` - Data files (veins.json, shunts.json, depots.json, etc.) -- Room-specific: databrowse depot, tester tests, monitors, models +- `config.json` - Framework branding/terminology +- `data/` - Data files (veins.json, shunts.json, etc.) -Managed rooms (like amar) also have: -- `docker-compose.yml` - Room's own services -- `soleprint/` - Soleprint Docker config for this room -- `.env.example` - Environment template +Room-specific system configs (merged into output): +- `artery/` - Room-specific shunts, pulses +- `atlas/` - Room-specific books +- `station/` - Room-specific tool configs (datagen, tester tests, etc.) ## Build & Run -### Commands ```bash # Build -python build.py dev # -> gen/standalone/ -python build.py dev --cfg amar # -> gen/amar/ -python build.py dev --all # -> both +python build.py # -> gen/standalone/ +python build.py --cfg amar # -> gen/amar/ +python build.py --all # -> all rooms + +# Run bare-metal +cd gen/standalone && python run.py # Using ctrl scripts -./ctrl/build.sh # Build standalone -./ctrl/build.sh amar # Build amar -./ctrl/build.sh --all # Build all - -./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 +./ctrl/build.sh [room] +./ctrl/start.sh [room] [-d] +./ctrl/stop.sh [room] ``` -### Adding a New Managed Room +## Adding a New Room + ```bash -# 1. Create room config -mkdir -p cfg/clientx/data +mkdir -p cfg/newroom/data +cp cfg/standalone/config.json cfg/newroom/ +cp -r cfg/standalone/data/* cfg/newroom/data/ -# 2. Copy base config -cp cfg/standalone/config.json cfg/clientx/ -cp -r cfg/standalone/data/* cfg/clientx/data/ +# Add room-specific configs as needed: +# cfg/newroom/artery/shunts/... +# cfg/newroom/atlas/books/... +# cfg/newroom/station/tools/... -# 3. Customize as needed (shunts, depots, branding) - -# 4. Build and run -python build.py dev --cfg clientx -./ctrl/start.sh clientx +python build.py --cfg newroom ``` ## Ports @@ -151,45 +142,25 @@ python build.py dev --cfg clientx | Service | Port | |---------|------| | Soleprint | 12000 | -| Amar Backend | 8000 | -| Amar Frontend | 3000 | ## Tools -| Tool | Location | Purpose | -|------|----------|---------| -| modelgen | station/tools/modelgen | Model generation | -| datagen | station/tools/datagen | Test data generation | -| tester | station/tools/tester | BDD/playwright test runner | -| sbwrapper | station/tools/sbwrapper | Sidebar wrapper UI | -| databrowse | station/monitors/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) +| Tool | Purpose | +|------|---------| +| modelgen | Generate models from config | +| datagen | Generate test data (uses faker) | +| tester | BDD/playwright test runner | +| graphgen | Generate navigable model graphs | +| databrowse | SQL data browser | ## External Paths | What | Path | |------|------| -| Amar Backend | /home/mariano/wdir/ama/amar_django_back | -| Amar Frontend | /home/mariano/wdir/ama/amar_frontend | | Pipelines | /home/mariano/wdir/ppl | ## Files Ignored -- `gen/` - Regenerate with `python build.py dev` +- `gen/` - Regenerate with `python build.py` - `fails/`, `def/` - Drafts - `__pycache__/`, `.venv/` diff --git a/README.md b/README.md index e138646..1c6e5bc 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # Soleprint -> Cada paso deja huella / Each step leaves a mark - Development workflow platform. Run, test, and document everything in one place. ## Quick Start @@ -23,16 +21,16 @@ cd gen/amar && .venv/bin/python run.py ```bash # Build python build.py dev # → gen/standalone/ -python build.py dev --cfg amar # → gen/amar/ +python build.py dev --cfg # → gen// python build.py dev --all # → both # ctrl scripts ./ctrl/build.sh # Build standalone -./ctrl/build.sh amar # Build amar +./ctrl/build.sh # Build ./ctrl/build.sh --all # Build all ./ctrl/start.sh # Start standalone (Docker) -./ctrl/start.sh amar # Start amar +./ctrl/start.sh # Start amar ./ctrl/start.sh -d # Detached ./ctrl/stop.sh # Stop ./ctrl/logs.sh # View logs @@ -42,12 +40,12 @@ python build.py dev --all # → both 1. Create room config directory: ```bash -mkdir -p cfg/clientx +mkdir -p cfg/ ``` 2. Add required files: ``` -cfg/clientx/ +cfg// ├── .env.example # Environment template ├── docker-compose.yml # Room services (optional) ├── databrowse/depot/ # Database schemas (optional) @@ -61,8 +59,8 @@ cfg/clientx/ 3. Build and run: ```bash -python build.py dev --cfg clientx -./ctrl/start.sh clientx +python build.py dev --cfg +./ctrl/start.sh ``` ## Systems @@ -70,9 +68,9 @@ python build.py dev --cfg clientx | | 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) | +| 💉 | **Artery** | Connectors to external services | +| 🗺️ | **Atlas** | Actionable documentation | +| 🎛️ | **Station** | Tools and monitors | ## Structure @@ -146,5 +144,3 @@ Shunt ── Fake connector for testing | google | OAuth2 | --- - -*Built for small teams who need to move fast without breaking things.* diff --git a/atlas/books/amar-arch/01-backend-architecture.dot b/atlas/books/amar-arch/01-backend-architecture.dot deleted file mode 100644 index 7c6da1c..0000000 --- a/atlas/books/amar-arch/01-backend-architecture.dot +++ /dev/null @@ -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"] -} diff --git a/atlas/books/amar-arch/01-backend-architecture.svg b/atlas/books/amar-arch/01-backend-architecture.svg deleted file mode 100644 index e8754ae..0000000 --- a/atlas/books/amar-arch/01-backend-architecture.svg +++ /dev/null @@ -1,585 +0,0 @@ - - - - - - -BackendArchitecture - -AMAR Mascotas - Backend Architecture (Django) - -cluster_django_core - -Django Core - - -cluster_mascotas - -mascotas (Pets & Veterinarians) - - -cluster_productos - -productos (Services & Pricing) - - -cluster_solicitudes - -solicitudes (Service Requests) - - -cluster_common - -common (Shared Models) - - -cluster_payments - -payments (Payment Processing) - - -cluster_external - -External Integrations - - -cluster_afip - -django_afip (Invoicing) - - - -auth_user - -auth.User -(Django Auth) - - - -petowner - -PetOwner -(Cliente/Tutor) - - - -auth_user->petowner - - -1:1 optional - - - -veterinarian - -Veterinarian -(Profesional) - - - -auth_user->veterinarian - - -1:1 - - - -django_admin - -Django Admin -Interface - - - -drf - -Django REST -Framework - - - -jwt_auth - -JWT Authentication -(SimpleJWT) - - - -drf->jwt_auth - - - - - -pet - -Pet -(Mascota) - - - -petowner->pet - - -1:N - - - -servicerequest - -ServiceRequest -(Solicitud) - - - -petowner->servicerequest - - -1:N - - - -neighborhood - -Neighborhood / -Province / Locality - - - -petowner->neighborhood - - -N:1 - - - -vetvisit - -VetVisit -(Consulta) - - - -pet->vetvisit - - -N:M - - - -pet_health - -PetVaccine / -PetStudy - - - -pet->pet_health - - -1:N - - - -veterinarian->vetvisit - - -1:N - - - -availability - -Availability / -Unavailability - - - -veterinarian->availability - - -1:N - - - -vetasked - -VeterinarianAsked -(Vet Consultado) - - - -veterinarian->vetasked - - -N:1 - - - -specialty - -Specialty -(Especialidades) - - - -veterinarian->specialty - - -N:M - - - -veterinarian->neighborhood - - -N:M coverage - - - -turnfee - -IndividualTurnFee -Group - - - -veterinarian->turnfee - - -N:M - - - -vetvisitreport - -VetVisitReport -(Informe Clinico) - - - -vetvisit->vetvisitreport - - -1:N - - - -vetvisit->servicerequest - - -1:1 - - - -google_cal - -Google Calendar -(Agenda) - - - -vetvisit->google_cal - - -sync - - - -receipt - -Receipt -(Comprobante) - - - -vetvisit->receipt - - -1:1 optional - - - -medication - -Medication -(Medicamentos) - - - -vetvisitreport->medication - - -references - - - -service - -Service -(Servicio) - - - -category - -Category / Group -(Categorias) - - - -service->category - - -N:1 - - - -prices - -Prices -(Precios) - - - -service->prices - - -1:N - - - -discounts - -Discounts -(Descuentos) - - - -service->discounts - - -1:N - - - -cart - -Cart / CartItem -(Carrito) - - - -service->cart - - -via CartItem - - - -cart->petowner - - -N:1 - - - -cart->veterinarian - - -N:1 optional - - - -combo - -ServiceCombo -(Paquetes) - - - -combo->service - - -contains - - - -servicerequest->cart - - -1:1 - - - -statehistory - -StateHistory -(Historial) - - - -servicerequest->statehistory - - -1:N - - - -servicerequest->vetasked - - -1:N - - - -campaign - -Campaign -(Marketing) - - - -servicerequest->campaign - - -N:1 optional - - - -tag - -Tag -(Etiquetas) - - - -servicerequest->tag - - -N:M - - - -mercadopago - -MercadoPago -Account - - - -servicerequest->mercadopago - - -payment - - - -mercately - -Mercately -(WhatsApp) - - - -servicerequest->mercately - - -notify - - - -reminders - -Reminders -(Recordatorios) - - - -vetasked->reminders - - -1:N - - - -celery - -Celery -(Async Tasks) - - - -reminders->celery - - -scheduled - - - -breed - -PetBreed / Vaccine -/ Study - - - -mpnotification - -MP Notification -(Webhooks) - - - -mpnotification->servicerequest - - -confirms - - - -google_sheets - -Google Sheets -(Exports) - - - -afip - -AFIP -(Facturacion) - - - -taxpayer - -TaxPayer -(Contribuyente) - - - -receipt->taxpayer - - -N:1 - - - -pos - -PointOfSales -(Punto de Venta) - - - -receipt->pos - - -N:1 - - - diff --git a/atlas/books/amar-arch/02-frontend-architecture.dot b/atlas/books/amar-arch/02-frontend-architecture.dot deleted file mode 100644 index ff349aa..0000000 --- a/atlas/books/amar-arch/02-frontend-architecture.dot +++ /dev/null @@ -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] -} diff --git a/atlas/books/amar-arch/02-frontend-architecture.svg b/atlas/books/amar-arch/02-frontend-architecture.svg deleted file mode 100644 index dc235d7..0000000 --- a/atlas/books/amar-arch/02-frontend-architecture.svg +++ /dev/null @@ -1,595 +0,0 @@ - - - - - - -FrontendArchitecture - -AMAR Mascotas - Frontend Architecture (Next.js) - -cluster_nextjs - -Next.js 13+ (App Router) - - -cluster_public - -Public Frontend (/(frontend)) - - -cluster_backoffice - -Backoffice (/(backoffice)/admin) - - -cluster_components - -Shared Components (/components) - - -cluster_services - -Services Layer (/services) - - -cluster_state - -State Management (/redux, /contexts) - - -cluster_backend - -Django Backend API - - -cluster_users - -User Types - - - -app_router - -App Router -/app/* - - - -layout - -Layout Components -(RootLayout, etc) - - - -app_router->layout - - - - - -middleware - -Middleware -(Auth redirect) - - - -app_router->middleware - - - - - -home - -Home Page -/ - - - -services_page - -Services Catalog -/servicios - - - -home->services_page - - - - - -cart_page - -Cart -/carrito - - - -services_page->cart_page - - - - - -login_page - -Login/Register -/login - - - -cart_page->login_page - - -if not auth - - - -profile_page - -User Profile -/perfil - - - -login_page->profile_page - - - - - -pets_page - -My Pets -/mascotas - - - -profile_page->pets_page - - - - - -requests_page - -My Requests -/solicitudes - - - -profile_page->requests_page - - - - - -admin_dash - -Dashboard -/admin - - - -admin_visits - -Visits Management -/admin/visits - - - -admin_dash->admin_visits - - - - - -admin_pets - -Pets Overview -/admin/pets - - - -admin_dash->admin_pets - - - - - -admin_requests - -Service Requests -/admin/solicitudes - - - -admin_dash->admin_requests - - - - - -admin_calendar - -Calendar View -/admin/calendario - - - -admin_dash->admin_calendar - - - - - -sidebar - -Sidebar -Navigation - - - -admin_dash->sidebar - - - - - -navbar - -NavbarBackoffice -Top Bar - - - -admin_dash->navbar - - - - - -visits_section - -VisitsSection -(List + Actions) - - - -admin_visits->visits_section - - - - - -drawer - -VisitsDrawer -(Side Panel) - - - -admin_visits->drawer - - - - - -tables - -DataTable -Components - - - -forms - -Form Components -(Pet, Visit, etc) - - - -http_service - -HttpService -(Axios wrapper) - - - -auth_api - -authAPI -(login/register) - - - -http_service->auth_api - - - - - -visits_api - -visitsAPI -(CRUD visits) - - - -http_service->visits_api - - - - - -orders_api - -OrdersAPI -(service requests) - - - -http_service->orders_api - - - - - -petowners_api - -petOwnersAPI -(clients) - - - -http_service->petowners_api - - - - - -vets_api - -VeterinariansAPI -(professionals) - - - -http_service->vets_api - - - - - -services_api - -servicesAPI -(catalog) - - - -http_service->services_api - - - - - -cart_api - -CartAPI -(shopping) - - - -http_service->cart_api - - - - - -api_auth - -/api/token/ -(JWT Auth) - - - -http_service->api_auth - - -JWT refresh - - - -auth_api->api_auth - - -POST /api/token/ - - - -api_mascotas - -/mascotas/api/v1/ -(Pets, Vets, Visits) - - - -visits_api->api_mascotas - - -CRUD /vet-visits/ - - - -api_solicitudes - -/solicitudes/ -(Requests) - - - -orders_api->api_solicitudes - - -CRUD /service-requests/ - - - -petowners_api->api_mascotas - - -CRUD /pet-owners/ - - - -vets_api->api_mascotas - - -GET /veterinarians/ - - - -api_productos - -/productos/ -(Services, Prices) - - - -services_api->api_productos - - -GET /services/ - - - -cart_api->api_productos - - -CRUD /cart/ - - - -redux_store - -Redux Store -(Global State) - - - -auth_slice - -Auth Slice -(user, token) - - - -auth_slice->redux_store - - - - - -visits_slice - -Visits Slice -(visit data) - - - -visits_slice->redux_store - - - - - -cart_slice - -Cart Slice -(items) - - - -cart_slice->redux_store - - - - - -auth_context - -AuthContext -(Provider) - - - -auth_context->redux_store - - - - - -api_payments - -/payments/ -(MercadoPago) - - - -petowner_user - -PetOwner -(Cliente) - - - -petowner_user->home - - -public access - - - -petowner_user->profile_page - - -authenticated - - - -vet_user - -Veterinarian -(Profesional) - - - -vet_user->admin_dash - - -backoffice - - - -admin_user - -Admin/Staff -(Interno) - - - -admin_user->admin_dash - - -full access - - - diff --git a/atlas/books/amar-arch/03-data-model.dot b/atlas/books/amar-arch/03-data-model.dot deleted file mode 100644 index bb2a092..0000000 --- a/atlas/books/amar-arch/03-data-model.dot +++ /dev/null @@ -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"] -} diff --git a/atlas/books/amar-arch/03-data-model.svg b/atlas/books/amar-arch/03-data-model.svg deleted file mode 100644 index ab40dcc..0000000 --- a/atlas/books/amar-arch/03-data-model.svg +++ /dev/null @@ -1,976 +0,0 @@ - - - - - - -DataModel - -AMAR Mascotas - Data Model (Entity Relationships) - -cluster_auth - -Users & Authentication - - -cluster_mascotas_owners - -Pet Owners & Pets - - -cluster_veterinarians - -Veterinarians - - -cluster_productos - -Services & Pricing - - -cluster_cart - -Cart & Checkout - - -cluster_solicitudes - -Service Requests (Workflow) - - -cluster_vetvisits - -Veterinary Visits - - -cluster_reference - -Reference Data - - -cluster_afip - -AFIP Invoicing - - - -auth_user - -auth.User - -id: PK -user name: str -email: str -is_staff: bool -is_superuser: bool - - - -petowner - -PetOwner - -id: PK -email: str (unique) -phone: str -first_name: str -last_name: str -neighborhood_id: FK -campaign_id: FK (opt) -geo_latitude: float -geo_longitude: float -address: str - - - -auth_user->petowner - - -1:1 opt - - - -veterinarian - -Veterinarian - -id: PK -user_id: FK -license: str -email: str -phone: str - - - -auth_user->veterinarian - - -1:1 - - - -pet - -Pet - -id: PK -owner_id: FK -name: str -pet_type: DOG/CAT -breed_id: FK (opt) -gender: M/F -age: int -weight: decimal -height: decimal -birth_date: date -allergies: text -neutered: bool -is_deceased: bool -state: puppy/adult/... -profile_picture: file - - - -petowner->pet - - -1:N owns - - - -cart - -Cart - -id: PK -petowner_id: FK -veterinarian_id: FK (opt) -use_vet_prices: bool -apply_turn_fee: bool - - - -petowner->cart - - -1:N - - - -servicerequest - -ServiceRequest - -id: PK -petowner_id: FK -cart_id: FK -veterinarian_id: FK (opt) -state: pending/vet_asked/ - vet_accepted/coordinated/ - payed/Confirmado/... -reason: text -days_requested: JSON -date_coordinated: datetime -hour_coordinated: time -pay_number: str -campaign_id: FK (opt) -attended_by_id: FK (opt) - - - -petowner->servicerequest - - -1:N requests - - - -vetvisit - -VetVisit - -id: PK -service_request_id: FK (opt) -owner_id: FK -veterinarian_id: FK -date: date -hour: time -visit_type: clinical/ - vaccination/ - telemedicina -visit_state: PENDING/ - IN_PROGRESS/ - COMPLETED/ - NO_REPORT/ - CANCELLED -reason: text -observations: text -price: decimal -deposit: decimal -vet_fee: decimal -pay_transaction: str -google_event_id: str -afip_receipt_id: FK (opt) - - - -petowner->vetvisit - - -1:N as owner - - - -neighborhood - -Neighborhood - -id: PK -name: str -distance_coefficient: decimal -coverage_area: GIS Polygon - - - -petowner->neighborhood - - -N:1 lives in - - - -campaign - -Campaign - -id: PK -name: str -utm_source: str -utm_medium: str -is_active: bool - - - -petowner->campaign - - -N:1 opt - - - -petvaccine - -PetVaccine - -id: PK -pet_id: FK -vaccine_id: FK -application_date: date -next_application: date - - - -pet->petvaccine - - -1:N - - - -petstudy - -PetStudy - -id: PK -pet_id: FK -study_id: FK -date: date -result: text -images: files - - - -pet->petstudy - - -1:N - - - -petbreed - -PetBreed - -id: PK -name: str -pet_type: DOG/CAT - - - -pet->petbreed - - -N:1 opt - - - -vaccine - -Vaccine - -id: PK -name: str -pet_type: DOG/CAT -periodicity: int (months) - - - -petvaccine->vaccine - - -N:1 - - - -study - -Study - -id: PK -name: str -pet_type: DOG/CAT -group_id: FK (opt) - - - -petstudy->study - - -N:1 - - - -availability - -Availability - -id: PK -veterinarian_id: FK -start_day: 0-6 -end_day: 0-6 -start_time: time -end_time: time - - - -veterinarian->availability - - -1:N - - - -unavailability - -Unavailability - -id: PK -veterinarian_id: FK -start_date: date -end_date: date -start_time: time -end_time: time -reason: str - - - -veterinarian->unavailability - - -1:N - - - -vet_specialty - -{M2M: Vet-Specialty|veterinarian_id: FK -specialty_id: FK -} - - - -veterinarian->vet_specialty - - -1:N - - - -vet_neighborhood - -{M2M: Vet-Neighborhood|veterinarian_id: FK -neighborhood_id: FK -} - - - -veterinarian->vet_neighborhood - - -1:N coverage - - - -turnfeegroup - -IndividualTurnFeeGroup - -id: PK -name: str -fee_percentage: decimal - - - -veterinarian->turnfeegroup - - -N:M - - - -specialty - -Specialty - -id: PK -name: str - - - -vet_specialty->specialty - - -N:1 - - - -vet_neighborhood->neighborhood - - -N:1 - - - -grupo - -Group - -id: PK -name: str -description: text - - - -category - -Category - -id: PK -group_id: FK -name: str -description: text -value: int (order) - - - -grupo->category - - -1:N - - - -service - -Service - -id: PK -name: str -description: text -specialty_id: FK -category_id: FK -duration: int (min) -modality: onsite/online -payment_sign_req: bool -pet_type_filter: str -age_filter: str -weight_range: str - - - -category->service - - -1:N - - - -prices - -Prices - -id: PK -service_id: FK -veterinarian_id: FK (opt) -price: decimal -professional_fee: decimal -payment_sign: decimal -from_date: date -to_date: date -active: bool - - - -service->prices - - -1:N - - - -discounts - -Discounts - -id: PK -service_id: FK -discount: decimal (%) -from_date: date -to_date: date -active: bool - - - -service->discounts - - -1:N - - - -service->specialty - - -N:1 opt - - - -prices->veterinarian - - -N:1 opt -(vet-specific) - - - -servicecombo - -ServiceCombo - -id: PK -name: str -description: text -discount_percent: decimal -discount_fixed: decimal - - - -cart->veterinarian - - -N:1 opt -(assigned vet) - - - -cartitem - -CartItem - -id: PK -cart_id: FK -pet_id: FK (opt) -service_id: FK -price: decimal -quantity: int -total: decimal (calc) - - - -cart->cartitem - - -1:N - - - -cartresumeitem - -CartResumeItem - -id: PK -cart_id: FK -concept: SUBTOTAL/ - DESCUENTO/ - ADELANTO/ - TOTAL/ - COSTO_SERVICIO -amount: decimal -order: int - - - -cart->cartresumeitem - - -1:N - - - -cartpetreason - -CartPetReason - -id: PK -cart_id: FK -pet_id: FK -reason: text - - - -cart->cartpetreason - - -1:N - - - -cartitem->pet - - -N:1 opt - - - -cartitem->service - - -N:1 - - - -cartpetreason->pet - - -N:1 - - - -servicerequest->veterinarian - - -N:1 opt -(assigned) - - - -servicerequest->cart - - -1:1 - - - -statehistory - -StateHistory - -id: PK -service_request_id: FK -state: str -additional_data: JSON -created_at: datetime -user_id: FK - - - -servicerequest->statehistory - - -1:N audit - - - -vetasked - -VeterinarianAsked - -id: PK -service_request_id: FK -veterinarian_id: FK -date_asked: datetime -date_answered: datetime -accepted: bool - - - -servicerequest->vetasked - - -1:N - - - -payreminder - -PaymentReminder - -id: PK -service_request_id: FK -scheduled_for: datetime -processed_at: datetime -status: pending/sent/... - - - -servicerequest->payreminder - - -1:N - - - -servicerequest->campaign - - -N:1 opt - - - -tag - -Tag - -id: PK -name: str (unique) - - - -servicerequest->tag - - -N:M - - - -vetasked->veterinarian - - -N:1 - - - -vetreminder - -ScheduledVetReminder - -id: PK -vet_asked_id: FK -scheduled_for: datetime -processed_at: datetime -status: pending/sent/... -celery_task_id: str - - - -vetasked->vetreminder - - -1:N - - - -vetvisit->veterinarian - - -N:1 - - - -vetvisit->servicerequest - - -1:1 opt -(from request) - - - -vetvisitreport - -VetVisitReport - -id: PK -visit_id: FK -pet_id: FK -reason: text -physical_exam: text -diagnosis: text -treatment: text -pdf_file: file - - - -vetvisit->vetvisitreport - - -1:N - - - -vetvisitpetreason - -VetVisitPetReason - -id: PK -visit_id: FK -pet_id: FK -reason: text - - - -vetvisit->vetvisitpetreason - - -1:N - - - -visit_pets - -{M2M: Visit-Pets|vetvisit_id: FK -pet_id: FK -} - - - -vetvisit->visit_pets - - -1:N - - - -receipt - -Receipt - -id: PK -document_number: bigint -receipt_number: int -issued_date: date -total_amount: decimal -net_taxed: decimal -cae: str -cae_expiration: date - - - -vetvisit->receipt - - -1:1 opt -(invoice) - - - -vetvisitreport->pet - - -N:1 - - - -vetvisitfollowup - -VetVisitFollowUp - -id: PK -report_id: FK -date: date -description: text - - - -vetvisitreport->vetvisitfollowup - - -1:N - - - -vetvisitpetreason->pet - - -N:1 - - - -visit_pets->pet - - -N:1 - - - -province - -Province - -id: PK -name: str - - - -locality - -Locality - -id: PK -province_id: FK -name: str - - - -province->locality - - -1:N - - - -medication - -Medication - -id: PK -name: str -general_name_id: FK -type_id: FK -presentation_id: FK -pet_type: str - - - diff --git a/atlas/books/amar-arch/04-data-model-simple.dot b/atlas/books/amar-arch/04-data-model-simple.dot deleted file mode 100644 index 9b4002f..0000000 --- a/atlas/books/amar-arch/04-data-model-simple.dot +++ /dev/null @@ -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] -} diff --git a/atlas/books/amar-arch/04-data-model-simple.svg b/atlas/books/amar-arch/04-data-model-simple.svg deleted file mode 100644 index 601d83b..0000000 --- a/atlas/books/amar-arch/04-data-model-simple.svg +++ /dev/null @@ -1,540 +0,0 @@ - - - - - - -DataModelSimple - -AMAR Mascotas - Data Model Overview - -cluster_auth - -Users & Auth - - -cluster_mascotas - -Pet Owners & Pets - - -cluster_vets - -Veterinarians - - -cluster_productos - -Services & Pricing - - -cluster_cart - -Cart & Checkout - - -cluster_solicitudes - -Service Requests - - -cluster_visits - -Veterinary Visits - - -cluster_reference - -Reference Data - - - -auth_user - -auth.User - - - -petowner - -PetOwner - - - -auth_user->petowner - - -1:1 opt - - - -veterinarian - -Veterinarian - - - -auth_user->veterinarian - - -1:1 - - - -pet - -Pet - - - -petowner->pet - - -1:N - - - -cart - -Cart - - - - -petowner->cart - - -1:N - - - -servicerequest - -ServiceRequest - - - -petowner->servicerequest - - -1:N - - - -petvaccine - -PetVaccine - - - -pet->petvaccine - - -1:N - - - -petstudy - -PetStudy - - - -pet->petstudy - - -1:N - - - -vaccine - -Vaccine - - - -petvaccine->vaccine - - - - - -study - -Study - - - -petstudy->study - - - - - -availability - -Availability - - - -veterinarian->availability - - - - - -unavailability - -Unavailability - - - -veterinarian->unavailability - - - - - -vet_specialty - -Vet-Specialty - - - -veterinarian->vet_specialty - - - - - -vet_neighborhood - -Vet-Neighborhood - - - -veterinarian->vet_neighborhood - - - - - - -specialty - -Specialty - - - -vet_specialty->specialty - - - - - -neighborhood - -Neighborhood - - - -vet_neighborhood->neighborhood - - - - - -grupo - -Group - - - -category - -Category - - - -grupo->category - - -1:N - - - -service - -Service - - - -category->service - - -1:N - - - -prices - -Prices - - - -service->prices - - -1:N - - - -discounts - -Discounts - - - -service->discounts - - -1:N - - - -vetvisit - -VetVisit - - - - -cartitem - -CartItem - - - -cart->cartitem - - -1:N - - - -cartresumeitem - -CartResumeItem - - - -cart->cartresumeitem - - - - - -cartpetreason - -CartPetReason - - - -cart->cartpetreason - - - - - - -cartitem->service - - - - - -servicerequest->cart - - -1:1 - - - -statehistory - -StateHistory - - - -servicerequest->statehistory - - -1:N - - - -vetasked - -VeterinarianAsked - - - -servicerequest->vetasked - - -1:N - - - -payreminder - -PayReminder - - - -servicerequest->payreminder - - - - - -campaign - -Campaign - - - - -vetasked->veterinarian - - - - - -vetreminder - -VetReminder - - - -vetasked->vetreminder - - - - - -vetvisit->servicerequest - - - - - -vetvisitreport - -VetVisitReport - - - -vetvisit->vetvisitreport - - -1:N - - - -vetvisitpetreason - -VisitPetReason - - - -vetvisit->vetvisitpetreason - - - - - -visit_pets - -Visit-Pets - - - -vetvisit->visit_pets - - - - - -receipt - -Receipt -(AFIP) - - - -vetvisit->receipt - - - - - -turnfeegroup - -TurnFeeGroup - - - - -vetvisitfollowup - -FollowUp - - - -vetvisitreport->vetvisitfollowup - - - - - - -province - -Province - - - -locality - -Locality - - - -province->locality - - - - - -petbreed - -PetBreed - - - -tag - -Tag - - - -medication - -Medication - - - diff --git a/atlas/books/amar-arch/graph.html b/atlas/books/amar-arch/graph.html deleted file mode 100644 index 999c0f1..0000000 --- a/atlas/books/amar-arch/graph.html +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - Graph Viewer - AMAR Mascotas - - - -
- ← Index - -

Loading...

-
- - - - - -
-
- -
- Graph -
- - - - diff --git a/atlas/books/amar-arch/index.html b/atlas/books/amar-arch/index.html deleted file mode 100644 index 9c88bb0..0000000 --- a/atlas/books/amar-arch/index.html +++ /dev/null @@ -1,245 +0,0 @@ - - - - - - AMAR Mascotas - Architecture & Data Models - - - -
-

AMAR Mascotas

-

Architecture & Data Model Documentation

-
- -
- -
-
-

Data Model Overview

- View Full -
- - Data Model Overview - -
-

High-level entity relationships without field details. Shows the main actors, workflow, and data flow.

-

Clusters

-
    -
  • Users & Auth: Django auth.User as central identity
  • -
  • Pet Owners & Pets: Clients, their pets, vaccines, studies
  • -
  • Veterinarians: Vets with availability, specialties, coverage areas
  • -
  • Services & Pricing: Service catalog with dynamic pricing
  • -
  • Cart & Checkout: Shopping cart workflow
  • -
  • Service Requests: Order lifecycle with state machine
  • -
  • Veterinary Visits: Scheduled visits, reports, AFIP invoicing
  • -
  • Reference Data: Lookups (specialties, neighborhoods, vaccines, etc.)
  • -
-
-
- -
-
-

Backend Architecture

- View Full -
- - Backend Architecture - -
-

Django apps structure: mascotas, productos, solicitudes, common, payments, and external integrations.

- -

Celery Tasks

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TaskAppPurpose
send_veterinarian_followupsolicitudesRe-sends availability request to vet if still pending
run_payment_reminder_cronsolicitudesCron job to send payment reminders
create_vetvisit_in_sheetmascotasCreates row in Google Sheets for visit
update_vetvisit_in_sheetmascotasUpdates Google Sheets row
create_event_calendar_vetvisitmascotasCreates Google Calendar event
update_event_calendar_vetvisitmascotasUpdates Google Calendar event
create_user_ownermascotasCreates Django user for PetOwner + welcome email
generate_vetvisit_invoicemascotasGenerates AFIP invoice and PDF
fetch_mp_notification_detailspaymentsFetches MercadoPago webhook details
-

Celery handles async operations: external APIs (Google, MercadoPago, AFIP), scheduled reminders, and heavy processing (invoices, emails).

-
-
- -
-
-

Frontend Architecture

- View Full -
- - Frontend Architecture - -
-

Next.js 13+ App Router structure with public pages, backoffice, shared components, and services layer.

-

Key Areas

-
    -
  • Public Pages: Landing, service catalog, booking flow
  • -
  • Backoffice: Role-based dashboards (admin, vet, petowner)
  • -
  • Services Layer: API clients for backend communication
  • -
  • State Management: Redux store for cart, auth, UI state
  • -
  • Shared Components: Forms, tables, modals, navigation
  • -
-
-
- -
-
-

Detailed Data Model

- View Full -
- - Detailed Data Model - -
-

Complete entity-relationship diagram with all fields, types, and relationships.

-

Data Patterns

-
    -
  • Soft delete: deleted flag on most models
  • -
  • Audit trail: StateHistory for service requests
  • -
  • Geographic: PostGIS polygons for coverage areas
  • -
  • Versioned pricing: Date ranges on Prices
  • -
-
-
- - -
-

Key Findings

- -
-
-

User Types

-

All users connect to auth.User:

-
    -
  • PetOwner: Optional 1:1 link (created lazily)
  • -
  • Veterinarian: Required 1:1 link to User
  • -
  • Staff: Direct Django users with is_staff=True
  • -
-
- -
-

Core Workflow

-
- PetOwner → Cart → ServiceRequest → VeterinarianAsked → VetVisit → Report -
-

State Machine:

-
- pending → vet_asked → vet_accepted → coordinated → payed → Confirmado -
-
- -
-

Pricing Logic

-
    -
  • Base price × neighborhood.distance_coefficient
  • -
  • Optional vet-specific pricing via Prices.veterinarian_id
  • -
  • Turn fee surcharge via IndividualTurnFeeGroup
  • -
  • Time-based discounts via Discounts model
  • -
-
- -
-

External Integrations

-
    -
  • MercadoPago: Payment processing
  • -
  • Google Calendar: Visit synchronization
  • -
  • Google Sheets: Visit tracking spreadsheet
  • -
  • Mercately: WhatsApp notifications
  • -
  • AFIP: Argentine tax invoicing
  • -
-
-
-
- - -
-

Technology Stack

-
-
-

Backend

-
    -
  • Django 4.x
  • -
  • Django REST Framework
  • -
  • PostgreSQL + PostGIS
  • -
  • Celery (Redis)
  • -
  • JWT Authentication
  • -
  • django-afip
  • -
-
-
-

Frontend

-
    -
  • Next.js 13+ (App Router)
  • -
  • React 18+
  • -
  • TypeScript
  • -
  • Redux
  • -
  • Axios
  • -
  • Tailwind CSS
  • -
-
-
-

Infrastructure

-
    -
  • Docker
  • -
  • Nginx
  • -
  • AWS S3 (storage)
  • -
  • MercadoPago API
  • -
  • Google APIs
  • -
  • WhatsApp Business
  • -
-
-
-
-
- -
-

AMAR Mascotas Architecture Documentation

-

Generated:

-
- - diff --git a/atlas/books/amar-arch/styles.css b/atlas/books/amar-arch/styles.css deleted file mode 100644 index 851ca04..0000000 --- a/atlas/books/amar-arch/styles.css +++ /dev/null @@ -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; - } -} diff --git a/atlas/books/media-analyzer/architecture_diagram.svg b/atlas/books/media-analyzer/architecture_diagram.svg deleted file mode 100644 index c356e1d..0000000 --- a/atlas/books/media-analyzer/architecture_diagram.svg +++ /dev/null @@ -1,645 +0,0 @@ - - - - - - -MediaAnalyzer - - -cluster_legend - -Legend - - -cluster_sources - -Video Sources - - -cluster_adapters - -Source Adapters Pattern - - -cluster_core - -Core Platform - - -cluster_execution - -Execution Strategies Pattern - - -cluster_event_sources - -Event Source Pattern - - -cluster_events - -Event-Driven Processing - - -cluster_workers - -Celery Workers - - -cluster_ai_adapters - -Analysis Adapters Pattern - - -cluster_storage - -Media Storage - - -cluster_frontend - -Frontend - - -cluster_cloud - -GCP Services - - - -implemented - -✅ Implemented - - - -planned - -⏳ Planned - - - - -code_pattern - -📐 Code Pattern - - - -service_arch - -⚡ Service/Architecture - - - - -webcam - -Webcam - - - -webcam_adapter - -WebcamAdapter - - - -webcam->webcam_adapter - - - - - -rtmp - -RTMP/OBS - - - -rtmp_adapter - -RtmpAdapter - - - -rtmp->rtmp_adapter - - - - - -files - -File Upload - - - -file_adapter - -FileAdapter - - - -files->file_adapter - - - - - -base_adapter - -BaseSourceAdapter -(Abstract) - - - -base_adapter->webcam_adapter - - - - - -base_adapter->rtmp_adapter - - - - - -base_adapter->file_adapter - - - - - -django - -Django API -+ Channels -:8000 - - - -webcam_adapter->django - - - - - -rtmp_adapter->django - - - - - -file_adapter->django - - - - - -postgres - -PostgreSQL -Database - - - -django->postgres - - - - - -redis - -Redis -Cache & Broker - - - -django->redis - - - - - -local_storage - -Local Files -(nginx-served) - - - -django->local_storage - - - - - -gcs_storage - -Google Cloud -Storage - - - -django->gcs_storage - - - - - -angular - -Angular 17 SPA -+ WebSocket -:4200 - - - -django->angular - - -WebSocket -API - - - -logo_worker - -Logo Detection -Worker -(logo_queue) - - - -redis->logo_worker - - - - - -visual_worker - -Visual Properties -Worker -(visual_queue) - - - -redis->visual_worker - - - - - -audio_worker - -Audio Transcript -Worker -(audio_queue) - - - -redis->audio_worker - - - - - -text_worker - -Text Recognition -Worker -(text_queue) - - - -redis->text_worker - - - - - -nginx - -NGINX -Reverse Proxy -:80 - - - -nginx->angular - - - - - -base_strategy - -BaseExecutionStrategy -(Abstract) - - - -local_strategy - -LocalStrategy - - - -base_strategy->local_strategy - - - - - -lan_strategy - -LANStrategy - - - -base_strategy->lan_strategy - - - - - -cloud_strategy - -CloudStrategy - - - -base_strategy->cloud_strategy - - - - - -clip_adapter - -CLIPAdapter -(Local) - - - -local_strategy->clip_adapter - - - - - -gcp_vision - -GCPVisionAdapter -(Cloud) - - - -local_strategy->gcp_vision - - - - - -yolo_adapter - -YOLOAdapter -(Planned) - - - -lan_strategy->yolo_adapter - - - - - -speech_api - -Speech-to-Text API -(Audio Transcript) - - - -cloud_strategy->speech_api - - - - - -base_event_source - -SegmentEventSource -(Abstract) - - - -file_watcher_source - -FileWatcherEventSource -(Local/LAN) - - - -base_event_source->file_watcher_source - - - - - -cloud_storage_source - -CloudStorageEventSource -(GCS Pub/Sub) - - - -base_event_source->cloud_storage_source - - - - - -webhook_source - -WebhookEventSource -(External) - - - -base_event_source->webhook_source - - - - - -event_source_manager - -EventSourceManager -(Environment-based) - - - -file_watcher_source->event_source_manager - - - - - -cloud_storage_source->event_source_manager - - - - - -webhook_source->event_source_manager - - - - - -redis_events - -Redis Event Queue -(segment_events) - - - -event_source_manager->redis_events - - -publishes -events - - - -event_processor - -Event Processor -(triggers analysis) - - - -redis_events->event_processor - - -consumes -events - - - -event_processor->logo_worker - - -triggers -analysis - - - -logo_worker->django - - -analysis -results - - - -logo_worker->local_strategy - - - - - -visual_worker->lan_strategy - - - - - -audio_worker->cloud_strategy - - - - - -text_worker->cloud_strategy - - - - - -base_ai - -DetectionAdapter -(Abstract) - - - -base_ai->clip_adapter - - - - - -base_ai->gcp_vision - - - - - -base_ai->yolo_adapter - - - - - -vision_api - -Cloud Vision API -(Logo Detection) - - - -gcp_vision->vision_api - - - - - -local_storage->file_watcher_source - - -monitors -HLS segments - - - -gcs_storage->cloud_storage_source - - -storage -events - - - -hls_player - -HLS.js Player -+ Canvas Overlays - - - -angular->hls_player - - - - - diff --git a/atlas/books/media-analyzer/asciidiagram.md b/atlas/books/media-analyzer/asciidiagram.md deleted file mode 100644 index 7c1f2c0..0000000 --- a/atlas/books/media-analyzer/asciidiagram.md +++ /dev/null @@ -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. diff --git a/atlas/books/media-analyzer/convo b/atlas/books/media-analyzer/convo deleted file mode 100644 index bda2bbb..0000000 --- a/atlas/books/media-analyzer/convo +++ /dev/null @@ -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. diff --git a/atlas/books/media-analyzer/diagram.md b/atlas/books/media-analyzer/diagram.md deleted file mode 100644 index bf757b8..0000000 --- a/atlas/books/media-analyzer/diagram.md +++ /dev/null @@ -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 \ No newline at end of file diff --git a/atlas/books/media-analyzer/system_architecture.dot b/atlas/books/media-analyzer/system_architecture.dot deleted file mode 100644 index ae90c30..0000000 --- a/atlas/books/media-analyzer/system_architecture.dot +++ /dev/null @@ -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]; -} \ No newline at end of file diff --git a/atlas/books/sysmonstm/architecture/01-system-overview.dot b/atlas/books/sysmonstm/architecture/01-system-overview.dot deleted file mode 100644 index c9bc4b0..0000000 --- a/atlas/books/sysmonstm/architecture/01-system-overview.dot +++ /dev/null @@ -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]; -} diff --git a/atlas/books/sysmonstm/architecture/01-system-overview.svg b/atlas/books/sysmonstm/architecture/01-system-overview.svg deleted file mode 100644 index aefe71c..0000000 --- a/atlas/books/sysmonstm/architecture/01-system-overview.svg +++ /dev/null @@ -1,193 +0,0 @@ - - - - - - -SystemOverview - -System Monitoring Platform - Architecture Overview - -cluster_external - -External - - -cluster_services - -Application Services - - -cluster_data - -Data Layer - - -cluster_events - -Event Stream - - - -browser - -Browser -(Dashboard) - - - -gateway - -Gateway -(FastAPI) - - - -browser->gateway - - -WebSocket -REST - - - -machines - - - - -Monitored -Machines - - - -collector - -Collector -(gRPC Client) - - - -machines->collector - - -psutil - - - -aggregator - -Aggregator -(gRPC Server) - - - -gateway->aggregator - - -gRPC - - - -redis - - -Redis -(Pub/Sub + State) - - - -gateway->redis - - -State -Query - - - -timescale - - -TimescaleDB -(Time-series) - - - -gateway->timescale - - -Historical -Query - - - -aggregator->redis - - -Current -State - - - -aggregator->timescale - - -Store -Metrics - - - -events - -Redis Pub/Sub -(Events) - - - -aggregator->events - - -Publish - - - -alerts - -Alerts -Service - - - -alerts->timescale - - -Store -Alerts - - - -collector->aggregator - - -gRPC -Stream - - - -events->gateway - - -Subscribe - - - -events->alerts - - -Subscribe - - - diff --git a/atlas/books/sysmonstm/architecture/02-data-flow.dot b/atlas/books/sysmonstm/architecture/02-data-flow.dot deleted file mode 100644 index ac77851..0000000 --- a/atlas/books/sysmonstm/architecture/02-data-flow.dot +++ /dev/null @@ -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]; -} diff --git a/atlas/books/sysmonstm/architecture/02-data-flow.svg b/atlas/books/sysmonstm/architecture/02-data-flow.svg deleted file mode 100644 index 5735a45..0000000 --- a/atlas/books/sysmonstm/architecture/02-data-flow.svg +++ /dev/null @@ -1,217 +0,0 @@ - - - - - - -DataFlow - -Metrics Data Flow Pipeline - -cluster_collect - -Collection (5s) - - -cluster_ingest - -Ingestion - - -cluster_hot - -Hot Path (Real-time) - - -cluster_warm - -Warm Path (Historical) - - -cluster_consume - -Consumers - - - -psutil - - - -psutil -(CPU, Mem, Disk) - - - -collector - -Collector -Service - - - -psutil->collector - - -Metrics - - - -aggregator - -Aggregator -(gRPC) - - - -collector->aggregator - - -gRPC -Stream - - - -validate - -Validate & -Normalize - - - -aggregator->validate - - - - - -redis_state - - -Redis -Current State - - - -validate->redis_state - - -Upsert - - - -redis_pubsub - -Redis -Pub/Sub - - - -validate->redis_pubsub - - -Publish - - - -raw - - -metrics_raw -(5s, 24h) - - - -validate->raw - - -Insert - - - -alerts - -Alert -Service - - - -redis_pubsub->alerts - - -metrics.* - - - -gateway - -Gateway -(WebSocket) - - - -redis_pubsub->gateway - - -metrics.* - - - -agg_1m - - -metrics_1m -(1m, 7d) - - - -raw->agg_1m - - -Continuous -Aggregate - - - -lambda - -Lambda -Aggregator - - - -raw->lambda - - -SQS -Trigger - - - -agg_1h - - -metrics_1h -(1h, 90d) - - - -agg_1m->agg_1h - - -Hourly -Job - - - -lambda->agg_1m - - -Batch -Write - - - diff --git a/atlas/books/sysmonstm/architecture/03-deployment.dot b/atlas/books/sysmonstm/architecture/03-deployment.dot deleted file mode 100644 index fe3b29d..0000000 --- a/atlas/books/sysmonstm/architecture/03-deployment.dot +++ /dev/null @@ -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]; -} diff --git a/atlas/books/sysmonstm/architecture/03-deployment.svg b/atlas/books/sysmonstm/architecture/03-deployment.svg deleted file mode 100644 index cc1cf45..0000000 --- a/atlas/books/sysmonstm/architecture/03-deployment.svg +++ /dev/null @@ -1,221 +0,0 @@ - - - - - - -Deployment - -Deployment Architecture - -cluster_local - -Local Development - - -cluster_kind - -Kind Cluster - - -cluster_aws - -AWS (sysmonstm.mcrn.ar) - - -cluster_ec2 - -EC2 t2.small - - -cluster_lambda - -Lambda (Data Processing) - - -cluster_cicd - -CI/CD - - -cluster_collectors - -Monitored Machines - - - -tilt - - - -Tilt -(Live Reload) - - - -k8s_local - -K8s Pods -(via Kustomize) - - - - -compose - -Docker Compose -(Alternative) - - - -compose_ec2 - -Docker Compose -(All Services) - - - -sqs - -SQS -(Buffer) - - - -compose_ec2->sqs - - -Events - - - -nginx - -Nginx -(SSL Termination) - - - -nginx->compose_ec2 - - -Proxy - - - -lambda_agg - -Aggregator -Lambda - - - -lambda_compact - -Compactor -Lambda - - - -s3 - - -S3 -(Backup) - - - -lambda_compact->s3 - - -Archive - - - -sqs->lambda_agg - - -Trigger - - - -woodpecker - -Woodpecker CI - - - -registry - - -Container -Registry - - - -woodpecker->registry - - -Push - - - -registry->k8s_local - - -Pull - - - -registry->compose_ec2 - - -Pull - - - -coll1 - -Collector -(Machine 1) - - - -coll1->compose_ec2 - - -gRPC - - - -coll2 - -Collector -(Machine 2) - - - -coll2->compose_ec2 - - -gRPC - - - -coll3 - -Collector -(Machine N) - - - -coll3->compose_ec2 - - -gRPC - - - diff --git a/atlas/books/sysmonstm/architecture/04-grpc-services.dot b/atlas/books/sysmonstm/architecture/04-grpc-services.dot deleted file mode 100644 index 9b06929..0000000 --- a/atlas/books/sysmonstm/architecture/04-grpc-services.dot +++ /dev/null @@ -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]; -} diff --git a/atlas/books/sysmonstm/architecture/04-grpc-services.svg b/atlas/books/sysmonstm/architecture/04-grpc-services.svg deleted file mode 100644 index d4af478..0000000 --- a/atlas/books/sysmonstm/architecture/04-grpc-services.svg +++ /dev/null @@ -1,171 +0,0 @@ - - - - - - -GrpcServices - -gRPC Service Definitions - -cluster_metrics - -MetricsService - - -cluster_control - -ControlService - - -cluster_config - -ConfigService - - -cluster_enums - -Enums - - - -metrics_svc - -MetricsService - -+ StreamMetrics(stream Metric) → StreamAck -+ GetCurrentState(StateRequest) → MachineState -+ GetAllStates(Empty) → AllMachinesState - - - -metric_msg - -Metric - -machine_id: string -hostname: string -timestamp_ms: int64 -type: MetricType -value: double -labels: map - - - -metrics_svc->metric_msg - - - - - -machine_state - -MachineState - -machine_id: string -hostname: string -last_seen_ms: int64 -current_metrics: Metric[] -health: HealthStatus -metadata: map - - - -metrics_svc->machine_state - - - - - -metric_type - -MetricType - -CPU_PERCENT -MEMORY_PERCENT -DISK_PERCENT -NETWORK_* -LOAD_AVG_* -... - - - -metric_msg->metric_type - - - - - -health_status - -HealthStatus - -HEALTHY -WARNING -CRITICAL -UNKNOWN -OFFLINE - - - -machine_state->health_status - - - - - -control_svc - -ControlService - -+ Control(stream Command) → stream Response - - - -commands - -ControlCommand - -command_id: string - -UpdateIntervalCommand -RestartCollectionCommand -ShutdownCommand - - - -control_svc->commands - - - - - -config_svc - -ConfigService - -+ GetConfig(ConfigRequest) → CollectorConfig -+ WatchConfig(ConfigRequest) → stream CollectorConfig - - - -collector_config - -CollectorConfig - -collection_interval_seconds: int32 -enabled_metrics: MetricType[] -labels: map -thresholds: ThresholdConfig[] - - - -config_svc->collector_config - - - - - diff --git a/atlas/books/sysmonstm/architecture/graph.html b/atlas/books/sysmonstm/architecture/graph.html deleted file mode 100644 index ba3bc7b..0000000 --- a/atlas/books/sysmonstm/architecture/graph.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - Graph Viewer - System Monitor - - - -
- ← Index - -

Loading...

-
- - - - - -
-
- -
- Graph -
- - - - diff --git a/atlas/books/sysmonstm/architecture/index.html b/atlas/books/sysmonstm/architecture/index.html deleted file mode 100644 index 362437b..0000000 --- a/atlas/books/sysmonstm/architecture/index.html +++ /dev/null @@ -1,262 +0,0 @@ - - - - - - - System Monitor - Redirecting... - - - -
-

System Monitoring Platform

-

Architecture & Design Documentation

-
- -
-
-
-

System Overview

- View Full -
- - System Overview - -
-

- High-level architecture showing all services, data - stores, and communication patterns. -

-

Key Components

-
    -
  • - Collector: Runs on each monitored - machine, streams metrics via gRPC -
  • -
  • - Aggregator: Central gRPC server, - receives streams, normalizes data -
  • -
  • - Gateway: FastAPI service, WebSocket - for browser, REST for queries -
  • -
  • - Alerts: Subscribes to events, - evaluates thresholds, triggers actions -
  • -
-
-
- -
-
-

Data Flow Pipeline

- View Full -
- - Data Flow - -
-

- How metrics flow from collection through storage with - different retention tiers. -

-

Storage Tiers

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TierResolutionRetentionUse Case
Hot (Redis)5s5 minCurrent state, live dashboard
Raw (TimescaleDB)5s24hRecent detailed analysis
1-min Aggregates1m7dWeek view, trends
1-hour Aggregates1h90dLong-term analysis
-
-
- -
-
-

Deployment Architecture

- View Full -
- - Deployment - -
-

- Deployment options from local development to AWS - production. -

-

Environments

-
    -
  • - Local Dev: Kind + Tilt for K8s, or - Docker Compose -
  • -
  • - Demo (EC2): Docker Compose on - t2.small at sysmonstm.mcrn.ar -
  • -
  • - Lambda Pipeline: SQS-triggered - aggregation for data processing experience -
  • -
-
-
- -
-
-

gRPC Service Definitions

- View Full -
- - gRPC Services - -
-

Protocol Buffer service and message definitions.

-

Services

-
    -
  • - MetricsService: Client-side - streaming for metrics ingestion -
  • -
  • - ControlService: Bidirectional - streaming for collector control -
  • -
  • - ConfigService: Server-side - streaming for config updates -
  • -
-
-
- -
-

Interview Talking Points

-
-
-

Domain Mapping

-
    -
  • Machine = Payment Processor
  • -
  • Metrics Stream = Transaction Stream
  • -
  • Thresholds = Fraud Detection
  • -
  • Aggregator = Payment Hub
  • -
-
-
-

gRPC Patterns

-
    -
  • Client streaming (metrics)
  • -
  • Server streaming (config)
  • -
  • Bidirectional (control)
  • -
  • Health checking
  • -
-
-
-

Event-Driven

-
    -
  • Redis Pub/Sub (current)
  • -
  • Abstraction for Kafka switch
  • -
  • Decoupled alert processing
  • -
  • Real-time WebSocket push
  • -
-
-
-

Resilience

-
    -
  • Collectors are independent
  • -
  • Graceful degradation
  • -
  • Retry with backoff
  • -
  • Health checks everywhere
  • -
-
-
-
- -
-

Technology Stack

-
-
-

Core

-
    -
  • Python 3.11+
  • -
  • FastAPI
  • -
  • gRPC / protobuf
  • -
  • asyncio
  • -
-
-
-

Data

-
    -
  • TimescaleDB
  • -
  • Redis
  • -
  • Redis Pub/Sub
  • -
-
-
-

Infrastructure

-
    -
  • Docker
  • -
  • Kubernetes
  • -
  • Kind + Tilt
  • -
  • Terraform
  • -
-
-
-

CI/CD

-
    -
  • Woodpecker CI
  • -
  • Kustomize
  • -
  • Container Registry
  • -
-
-
-
-
- -
-

System Monitoring Platform - Architecture Documentation

-

- Generated: -

-
- - diff --git a/atlas/books/sysmonstm/architecture/styles.css b/atlas/books/sysmonstm/architecture/styles.css deleted file mode 100644 index 4f251b9..0000000 --- a/atlas/books/sysmonstm/architecture/styles.css +++ /dev/null @@ -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; -} diff --git a/atlas/books/sysmonstm/explainer/images/01-architecture-overview.svg b/atlas/books/sysmonstm/explainer/images/01-architecture-overview.svg deleted file mode 100644 index a8bb34f..0000000 --- a/atlas/books/sysmonstm/explainer/images/01-architecture-overview.svg +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - - - - - - - - - - - - System Architecture Overview - - - - - Machine 1 - Collector - - - - - Machine 2 - Collector - - - - - Machine N - Collector - - - - - - Aggregator - gRPC Server - Batch Processing - - - - - - Redis - Hot Data - - - - - TimescaleDB - Historical - - - - - - Gateway - FastAPI - WebSocket - - - - - - Browser - Dashboard - - - - - - Alerts - Threshold Rules - - - - - - - - - - - - - - - - - - - - - - gRPC Stream - Events - WS - Pub/Sub - diff --git a/atlas/books/sysmonstm/explainer/images/02-grpc-streaming.svg b/atlas/books/sysmonstm/explainer/images/02-grpc-streaming.svg deleted file mode 100644 index 5361755..0000000 --- a/atlas/books/sysmonstm/explainer/images/02-grpc-streaming.svg +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - - - - - - gRPC Client-Side Streaming - - - - - Collector - - - - - CPU: 45% - - - - Memory: 62% - - - - Disk: 78% - - - - - - - - - - - - M1 - - - - M2 - - - - M3 - - - Continuous stream of metrics - - - - - - Aggregator - - - - - Batch: 20 metrics - Flush to storage - - - - - - Redis - - - - Timescale - - - - - - One persistent connection. Metrics flow continuously. No polling overhead. - - diff --git a/atlas/books/sysmonstm/explainer/images/03-storage-tiers.svg b/atlas/books/sysmonstm/explainer/images/03-storage-tiers.svg deleted file mode 100644 index 8863c59..0000000 --- a/atlas/books/sysmonstm/explainer/images/03-storage-tiers.svg +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - Tiered Storage Architecture - - - - - - HOT - Redis - 5 second resolution - 5 minute retention - - - - - - - WARM - TimescaleDB - 5 second resolution - 24 hour retention - - - - - - - COLD - Aggregates - 1 min / 1 hour resolution - 7 / 90 day retention - - - - - - Dashboard - "What's the CPU now?" - Fast in-memory reads - - - - - Recent Graphs - "Last hour of metrics" - Hypertable queries - - - - - Trends / Reports - "Weekly CPU average" - Pre-aggregated data - - - - - - - Fastest - Access Time - Slowest - - diff --git a/atlas/books/sysmonstm/explainer/images/04-event-driven.svg b/atlas/books/sysmonstm/explainer/images/04-event-driven.svg deleted file mode 100644 index ea1a4ef..0000000 --- a/atlas/books/sysmonstm/explainer/images/04-event-driven.svg +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - - - - - - - - - - - - Event-Driven Architecture - - - - - Aggregator - Publisher - - - - - - Event Stream - Redis Pub/Sub - - - - - metrics.raw - - - - alerts.* - - - - - - - Gateway - WebSocket push - - - - - Alerts - Threshold check - - - - - - - - - - - Benefits: - - - - Decoupled services - can restart independently - - - - Easy to add new subscribers without changing publisher - - - - Abstraction allows switching backends (Kafka) - - - - Natural audit trail of all events - - - diff --git a/atlas/books/sysmonstm/explainer/images/05-domain-mapping.svg b/atlas/books/sysmonstm/explainer/images/05-domain-mapping.svg deleted file mode 100644 index b0c5759..0000000 --- a/atlas/books/sysmonstm/explainer/images/05-domain-mapping.svg +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - Domain Mapping: Monitoring to Payments - - - - - System Monitoring - - - - - - Payment Processing - - - - - - Machine - workstation, laptop, server - - - - - Payment Processor - Stripe, PayPal, bank API - - - - - - Metrics Stream - CPU, memory, disk usage - - - - - Transaction Stream - payments, refunds, disputes - - - - - - Alert Thresholds - CPU > 80%, disk > 90% - - - - - Fraud Detection - amount > $10k, velocity checks - - - - - - Aggregator - normalize, store, publish - - - - - Payment Hub - normalize, ledger, audit - - - - Same architecture, different domain vocabulary - diff --git a/atlas/books/sysmonstm/explainer/images/06-deskmeter-enhancement.svg b/atlas/books/sysmonstm/explainer/images/06-deskmeter-enhancement.svg deleted file mode 100644 index 40bc3d7..0000000 --- a/atlas/books/sysmonstm/explainer/images/06-deskmeter-enhancement.svg +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - - - - - - - - - - Deskmeter: Current vs Enhanced Architecture - - - - CURRENT - - - - - dmcore - polls wmctrl - - - - - - MongoDB - all data - - - - - - Flask - AJAX polling - - - - - - - - - - - enhance - - - - - ENHANCED - - - - - Machine 1 - - - - Machine 2 - - - - - - Events - Pub/Sub - - - - - - Gateway - WebSocket - real-time - - - - - - Focus Alerts - thresholds - - - - - - - - - - - - - What sysmonstm Patterns Add to Deskmeter - - - - Real-time updates - WebSocket push instead of AJAX polling every 5 seconds - - - - - Multi-machine tracking - Monitor workstation + laptop productivity in one dashboard - - - - - Focus alerts - Notify when context-switching too often or idle too long - - - - - Event-driven architecture - Decoupled services, easy to add new subscribers - - - diff --git a/atlas/books/sysmonstm/explainer/other-applications.md b/atlas/books/sysmonstm/explainer/other-applications.md deleted file mode 100644 index c2f778d..0000000 --- a/atlas/books/sysmonstm/explainer/other-applications.md +++ /dev/null @@ -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. - -![Domain Mapping Overview](images/05-domain-mapping.svg) - -## 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. - -![Deskmeter Enhancement](images/06-deskmeter-enhancement.svg) - -### 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/") -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. diff --git a/atlas/books/sysmonstm/explainer/sysmonstm-from-start-to-finish.md b/atlas/books/sysmonstm/explainer/sysmonstm-from-start-to-finish.md deleted file mode 100644 index 9f08b9c..0000000 --- a/atlas/books/sysmonstm/explainer/sysmonstm-from-start-to-finish.md +++ /dev/null @@ -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. - -![System Architecture Overview](images/01-architecture-overview.svg) - -## 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. - -![gRPC Streaming Pattern](images/02-grpc-streaming.svg) - -### 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. - -![Storage Tiers](images/03-storage-tiers.svg) - -### 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. - -![Event-Driven Architecture](images/04-event-driven.svg) - -## 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. diff --git a/atlas/books/sysmonstm/explainer/viewer.html b/atlas/books/sysmonstm/explainer/viewer.html deleted file mode 100644 index 89d0f52..0000000 --- a/atlas/books/sysmonstm/explainer/viewer.html +++ /dev/null @@ -1,425 +0,0 @@ - - - - - - Loading... - System Monitor Docs - - - - - - -
-

System Monitoring Platform

-

Documentation

-
- -
- ← Back to Index - - - -
-
Loading article
-
-
- -
-

System Monitoring Platform - Documentation

-
- - - - - - - - - - - - - - - diff --git a/atlas/books/sysmonstm/index.html b/atlas/books/sysmonstm/index.html deleted file mode 100644 index fb9651e..0000000 --- a/atlas/books/sysmonstm/index.html +++ /dev/null @@ -1,393 +0,0 @@ - - - - - - System Monitor - Documentation - - - - -
-

System Monitoring Platform

-

Documentation

-
- -
- - - -
- - -
-
-

System Overview

- View Full -
- - System Overview - -
-

- High-level architecture showing all services, data - stores, and communication patterns. -

-

Key Components

-
    -
  • - Collector: Runs on each monitored - machine, streams metrics via gRPC -
  • -
  • - Aggregator: Central gRPC server, - receives streams, normalizes data -
  • -
  • - Gateway: FastAPI service, WebSocket - for browser, REST for queries -
  • -
  • - Alerts: Subscribes to events, - evaluates thresholds, triggers actions -
  • -
-
-
- -
-
-

Data Flow Pipeline

- View Full -
- - Data Flow - -
-

- How metrics flow from collection through storage with - different retention tiers. -

-

Storage Tiers

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TierResolutionRetentionUse Case
Hot (Redis)5s5 minCurrent state, live dashboard
Raw (TimescaleDB)5s24hRecent detailed analysis
1-min Aggregates1m7dWeek view, trends
1-hour Aggregates1h90dLong-term analysis
-
-
- -
-
-

Deployment Architecture

- View Full -
- - Deployment - -
-

- Deployment options from local development to AWS - production. -

-

Environments

-
    -
  • - Local Dev: Kind + Tilt for K8s, or - Docker Compose -
  • -
  • - Demo (EC2): Docker Compose on - t2.small at sysmonstm.mcrn.ar -
  • -
  • - Lambda Pipeline: SQS-triggered - aggregation for data processing experience -
  • -
-
-
- -
-
-

gRPC Service Definitions

- View Full -
- - gRPC Services - -
-

Protocol Buffer service and message definitions.

-

Services

-
    -
  • - MetricsService: Client-side - streaming for metrics ingestion -
  • -
  • - ControlService: Bidirectional - streaming for collector control -
  • -
  • - ConfigService: Server-side - streaming for config updates -
  • -
-
-
- -
- -
-

Interview Talking Points

-
-
-

Domain Mapping

-
    -
  • Machine = Payment Processor
  • -
  • Metrics Stream = Transaction Stream
  • -
  • Thresholds = Fraud Detection
  • -
  • Aggregator = Payment Hub
  • -
-
-
-

gRPC Patterns

-
    -
  • Client streaming (metrics)
  • -
  • Server streaming (config)
  • -
  • Bidirectional (control)
  • -
  • Health checking
  • -
-
-
-

Event-Driven

-
    -
  • Redis Pub/Sub (current)
  • -
  • Abstraction for Kafka switch
  • -
  • Decoupled alert processing
  • -
  • Real-time WebSocket push
  • -
-
-
-

Resilience

-
    -
  • Collectors are independent
  • -
  • Graceful degradation
  • -
  • Retry with backoff
  • -
  • Health checks everywhere
  • -
-
-
-
- -
-

Technology Stack

-
-
-

Core

-
    -
  • Python 3.11+
  • -
  • FastAPI
  • -
  • gRPC / protobuf
  • -
  • asyncio
  • -
-
-
-

Data

-
    -
  • TimescaleDB
  • -
  • Redis
  • -
  • Redis Pub/Sub
  • -
-
-
-

Infrastructure

-
    -
  • Docker
  • -
  • Kubernetes
  • -
  • Kind + Tilt
  • -
  • Terraform
  • -
-
-
-

CI/CD

-
    -
  • Woodpecker CI
  • -
  • Kustomize
  • -
  • Container Registry
  • -
-
-
-
-
- -
-

System Monitoring Platform - Documentation

-

- Generated: -

-
- - diff --git a/atlas/books/sysmonstm/static/prism/prism-bash.min.js b/atlas/books/sysmonstm/static/prism/prism-bash.min.js deleted file mode 100644 index f1659f1..0000000 --- a/atlas/books/sysmonstm/static/prism/prism-bash.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e){var t="\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b",a={pattern:/(^(["']?)\w+\2)[ \t]+\S.*/,lookbehind:!0,alias:"punctuation",inside:null},n={bash:a,environment:{pattern:RegExp("\\$"+t),alias:"constant"},variable:[{pattern:/\$?\(\([\s\S]+?\)\)/,greedy:!0,inside:{variable:[{pattern:/(^\$\(\([\s\S]+)\)\)/,lookbehind:!0},/^\$\(\(/],number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,operator:/--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/,punctuation:/\(\(?|\)\)?|,|;/}},{pattern:/\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,greedy:!0,inside:{variable:/^\$\(|^`|\)$|`$/}},{pattern:/\$\{[^}]+\}/,greedy:!0,inside:{operator:/:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,punctuation:/[\[\]]/,environment:{pattern:RegExp("(\\{)"+t),lookbehind:!0,alias:"constant"}}},/\$(?:\w+|[#?*!@$])/],entity:/\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/};e.languages.bash={shebang:{pattern:/^#!\s*\/.*/,alias:"important"},comment:{pattern:/(^|[^"{\\$])#.*/,lookbehind:!0},"function-name":[{pattern:/(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/,lookbehind:!0,alias:"function"},{pattern:/\b[\w-]+(?=\s*\(\s*\)\s*\{)/,alias:"function"}],"for-or-select":{pattern:/(\b(?:for|select)\s+)\w+(?=\s+in\s)/,alias:"variable",lookbehind:!0},"assign-left":{pattern:/(^|[\s;|&]|[<>]\()\w+(?:\.\w+)*(?=\+?=)/,inside:{environment:{pattern:RegExp("(^|[\\s;|&]|[<>]\\()"+t),lookbehind:!0,alias:"constant"}},alias:"variable",lookbehind:!0},parameter:{pattern:/(^|\s)-{1,2}(?:\w+:[+-]?)?\w+(?:\.\w+)*(?=[=\s]|$)/,alias:"variable",lookbehind:!0},string:[{pattern:/((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/,lookbehind:!0,greedy:!0,inside:n},{pattern:/((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/,lookbehind:!0,greedy:!0,inside:{bash:a}},{pattern:/(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/,lookbehind:!0,greedy:!0,inside:n},{pattern:/(^|[^$\\])'[^']*'/,lookbehind:!0,greedy:!0},{pattern:/\$'(?:[^'\\]|\\[\s\S])*'/,greedy:!0,inside:{entity:n.entity}}],environment:{pattern:RegExp("\\$?"+t),alias:"constant"},variable:n.variable,function:{pattern:/(^|[\s;|&]|[<>]\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cargo|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|java|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|sysctl|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,lookbehind:!0},keyword:{pattern:/(^|[\s;|&]|[<>]\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\s;|&])/,lookbehind:!0},builtin:{pattern:/(^|[\s;|&]|[<>]\()(?:\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\s;|&])/,lookbehind:!0,alias:"class-name"},boolean:{pattern:/(^|[\s;|&]|[<>]\()(?:false|true)(?=$|[)\s;|&])/,lookbehind:!0},"file-descriptor":{pattern:/\B&\d\b/,alias:"important"},operator:{pattern:/\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/,inside:{"file-descriptor":{pattern:/^\d/,alias:"important"}}},punctuation:/\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,number:{pattern:/(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,lookbehind:!0}},a.inside=e.languages.bash;for(var s=["comment","function-name","for-or-select","assign-left","parameter","string","environment","function","keyword","builtin","boolean","file-descriptor","operator","punctuation","number"],o=n.variable[1].inside,i=0;i(?=\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); \ No newline at end of file diff --git a/atlas/books/sysmonstm/static/prism/prism-python.min.js b/atlas/books/sysmonstm/static/prism/prism-python.min.js deleted file mode 100644 index 96932b0..0000000 --- a/atlas/books/sysmonstm/static/prism/prism-python.min.js +++ /dev/null @@ -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:/![sra](?=[:}]$)/,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; \ No newline at end of file diff --git a/atlas/static/prism/prism-gherkin.min.js b/atlas/static/prism/prism-gherkin.min.js deleted file mode 100644 index 183dac7..0000000 --- a/atlas/static/prism/prism-gherkin.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(a){var n="(?:\r?\n|\r)[ \t]*\\|.+\\|(?:(?!\\|).)*";a.languages.gherkin={pystring:{pattern:/("""|''')[\s\S]+?\1/,alias:"string"},comment:{pattern:/(^[ \t]*)#.*/m,lookbehind:!0},tag:{pattern:/(^[ \t]*)@\S*/m,lookbehind:!0},feature:{pattern:/((?:^|\r?\n|\r)[ \t]*)(?:Ability|Ahoy matey!|Arwedd|Aspekt|Besigheid Behoefte|Business Need|Caracteristica|Característica|Egenskab|Egenskap|Eiginleiki|Feature|Fīča|Fitur|Fonctionnalité|Fonksyonalite|Funcionalidade|Funcionalitat|Functionalitate|Funcţionalitate|Funcționalitate|Functionaliteit|Fungsi|Funkcia|Funkcija|Funkcionalitāte|Funkcionalnost|Funkcja|Funksie|Funktionalität|Funktionalitéit|Funzionalità|Hwaet|Hwæt|Jellemző|Karakteristik|Lastnost|Mak|Mogucnost|laH|Mogućnost|Moznosti|Možnosti|OH HAI|Omadus|Ominaisuus|Osobina|Özellik|Potrzeba biznesowa|perbogh|poQbogh malja'|Požadavek|Požiadavka|Pretty much|Qap|Qu'meH 'ut|Savybė|Tính năng|Trajto|Vermoë|Vlastnosť|Właściwość|Značilnost|Δυνατότητα|Λειτουργία|Могућност|Мөмкинлек|Особина|Свойство|Үзенчәлеклелек|Функционал|Функционалност|Функция|Функціонал|תכונה|خاصية|خصوصیت|صلاحیت|کاروبار کی ضرورت|وِیژگی|रूप लेख|ਖਾਸੀਅਤ|ਨਕਸ਼ ਨੁਹਾਰ|ਮੁਹਾਂਦਰਾ|గుణము|ಹೆಚ್ಚಳ|ความต้องการทางธุรกิจ|ความสามารถ|โครงหลัก|기능|フィーチャ|功能|機能):(?:[^:\r\n]+(?:\r?\n|\r|$))*/,lookbehind:!0,inside:{important:{pattern:/(:)[^\r\n]+/,lookbehind:!0},keyword:/[^:\r\n]+:/}},scenario:{pattern:/(^[ \t]*)(?:Abstract Scenario|Abstrakt Scenario|Achtergrond|Aer|Ær|Agtergrond|All y'all|Antecedentes|Antecedents|Atburðarás|Atburðarásir|Awww, look mate|B4|Background|Baggrund|Bakgrund|Bakgrunn|Bakgrunnur|Beispiele|Beispiller|Bối cảnh|Cefndir|Cenario|Cenário|Cenario de Fundo|Cenário de Fundo|Cenarios|Cenários|Contesto|Context|Contexte|Contexto|Conto|Contoh|Contone|Dæmi|Dasar|Dead men tell no tales|Delineacao do Cenario|Delineação do Cenário|Dis is what went down|Dữ liệu|Dyagram Senaryo|Dyagram senaryo|Egzanp|Ejemplos|Eksempler|Ekzemploj|Enghreifftiau|Esbozo do escenario|Escenari|Escenario|Esempi|Esquema de l'escenari|Esquema del escenario|Esquema do Cenario|Esquema do Cenário|EXAMPLZ|Examples|Exempel|Exemple|Exemples|Exemplos|First off|Fono|Forgatókönyv|Forgatókönyv vázlat|Fundo|Geçmiş|Grundlage|Hannergrond|ghantoH|Háttér|Heave to|Istorik|Juhtumid|Keadaan|Khung kịch bản|Khung tình huống|Kịch bản|Koncept|Konsep skenario|Kontèks|Kontekst|Kontekstas|Konteksts|Kontext|Konturo de la scenaro|Latar Belakang|lut chovnatlh|lut|lutmey|Lýsing Atburðarásar|Lýsing Dæma|MISHUN SRSLY|MISHUN|Menggariskan Senario|mo'|Náčrt Scenára|Náčrt Scénáře|Náčrt Scenáru|Oris scenarija|Örnekler|Osnova|Osnova Scenára|Osnova scénáře|Osnutek|Ozadje|Paraugs|Pavyzdžiai|Példák|Piemēri|Plan du scénario|Plan du Scénario|Plan Senaryo|Plan senaryo|Plang vum Szenario|Pozadí|Pozadie|Pozadina|Príklady|Příklady|Primer|Primeri|Primjeri|Przykłady|Raamstsenaarium|Reckon it's like|Rerefons|Scenár|Scénář|Scenarie|Scenarij|Scenarijai|Scenarijaus šablonas|Scenariji|Scenārijs|Scenārijs pēc parauga|Scenarijus|Scenario|Scénario|Scenario Amlinellol|Scenario Outline|Scenario Template|Scenariomal|Scenariomall|Scenarios|Scenariu|Scenariusz|Scenaro|Schema dello scenario|Se ðe|Se the|Se þe|Senario|Senaryo Deskripsyon|Senaryo deskripsyon|Senaryo|Senaryo taslağı|Shiver me timbers|Situācija|Situai|Situasie Uiteensetting|Situasie|Skenario konsep|Skenario|Skica|Structura scenariu|Structură scenariu|Struktura scenarija|Stsenaarium|Swa hwaer swa|Swa|Swa hwær swa|Szablon scenariusza|Szenario|Szenariogrundriss|Tapaukset|Tapaus|Tapausaihio|Taust|Tausta|Template Keadaan|Template Senario|Template Situai|The thing of it is|Tình huống|Variantai|Voorbeelde|Voorbeelden|Wharrimean is|Yo-ho-ho|You'll wanna|Założenia|Παραδείγματα|Περιγραφή Σεναρίου|Σενάρια|Σενάριο|Υπόβαθρο|Кереш|Контекст|Концепт|Мисаллар|Мисоллар|Основа|Передумова|Позадина|Предистория|Предыстория|Приклади|Пример|Примери|Примеры|Рамка на сценарий|Скица|Структура сценарија|Структура сценария|Структура сценарію|Сценарий|Сценарий структураси|Сценарийның төзелеше|Сценарији|Сценарио|Сценарій|Тарих|Үрнәкләр|דוגמאות|רקע|תבנית תרחיש|תרחיש|الخلفية|الگوی سناریو|امثلة|پس منظر|زمینه|سناریو|سيناريو|سيناريو مخطط|مثالیں|منظر نامے کا خاکہ|منظرنامہ|نمونه ها|उदाहरण|परिदृश्य|परिदृश्य रूपरेखा|पृष्ठभूमि|ਉਦਾਹਰਨਾਂ|ਪਟਕਥਾ|ਪਟਕਥਾ ਢਾਂਚਾ|ਪਟਕਥਾ ਰੂਪ ਰੇਖਾ|ਪਿਛੋਕੜ|ఉదాహరణలు|కథనం|నేపథ్యం|సన్నివేశం|ಉದಾಹರಣೆಗಳು|ಕಥಾಸಾರಾಂಶ|ವಿವರಣೆ|ಹಿನ್ನೆಲೆ|โครงสร้างของเหตุการณ์|ชุดของตัวอย่าง|ชุดของเหตุการณ์|แนวคิด|สรุปเหตุการณ์|เหตุการณ์|배경|시나리오|시나리오 개요|예|サンプル|シナリオ|シナリオアウトライン|シナリオテンプレ|シナリオテンプレート|テンプレ|例|例子|剧本|剧本大纲|劇本|劇本大綱|场景|场景大纲|場景|場景大綱|背景):[^:\r\n]*/m,lookbehind:!0,inside:{important:{pattern:/(:)[^\r\n]*/,lookbehind:!0},keyword:/[^:\r\n]+:/}},"table-body":{pattern:RegExp("("+n+")(?:"+n+")+"),lookbehind:!0,inside:{outline:{pattern:/<[^>]+>/,alias:"variable"},td:{pattern:/\s*[^\s|][^|]*/,alias:"string"},punctuation:/\|/}},"table-head":{pattern:RegExp(n),inside:{th:{pattern:/\s*[^\s|][^|]*/,alias:"variable"},punctuation:/\|/}},atrule:{pattern:/(^[ \t]+)(?:'a|'ach|'ej|7|a|A také|A taktiež|A tiež|A zároveň|Aber|Ac|Adott|Akkor|Ak|Aleshores|Ale|Ali|Allora|Alors|Als|Ama|Amennyiben|Amikor|Ampak|an|AN|Ananging|And y'all|And|Angenommen|Anrhegedig a|An|Apabila|Atès|Atesa|Atunci|Avast!|Aye|A|awer|Bagi|Banjur|Bet|Biết|Blimey!|Buh|But at the end of the day I reckon|But y'all|But|BUT|Cal|Când|Cand|Cando|Ce|Cuando|Če|Ða ðe|Ða|Dadas|Dada|Dados|Dado|DaH ghu' bejlu'|dann|Dann|Dano|Dan|Dar|Dat fiind|Data|Date fiind|Date|Dati fiind|Dati|Daţi fiind|Dați fiind|DEN|Dato|De|Den youse gotta|Dengan|Diberi|Diyelim ki|Donada|Donat|Donitaĵo|Do|Dun|Duota|Ðurh|Eeldades|Ef|Eğer ki|Entao|Então|Entón|E|En|Entonces|Epi|És|Etant donnée|Etant donné|Et|Étant données|Étant donnée|Étant donné|Etant données|Etant donnés|Étant donnés|Fakat|Gangway!|Gdy|Gegeben seien|Gegeben sei|Gegeven|Gegewe|ghu' noblu'|Gitt|Given y'all|Given|Givet|Givun|Ha|Cho|I CAN HAZ|In|Ir|It's just unbelievable|I|Ja|Jeśli|Jeżeli|Kad|Kada|Kadar|Kai|Kaj|Když|Keď|Kemudian|Ketika|Khi|Kiedy|Ko|Kuid|Kui|Kun|Lan|latlh|Le sa a|Let go and haul|Le|Lè sa a|Lè|Logo|Lorsqu'<|Lorsque|mä|Maar|Mais|Mając|Ma|Majd|Maka|Manawa|Mas|Men|Menawa|Mutta|Nalika|Nalikaning|Nanging|Når|När|Nato|Nhưng|Niin|Njuk|O zaman|Och|Og|Oletetaan|Ond|Onda|Oraz|Pak|Pero|Però|Podano|Pokiaľ|Pokud|Potem|Potom|Privzeto|Pryd|Quan|Quand|Quando|qaSDI'|Så|Sed|Se|Siis|Sipoze ke|Sipoze Ke|Sipoze|Si|Şi|Și|Soit|Stel|Tada|Tad|Takrat|Tak|Tapi|Ter|Tetapi|Tha the|Tha|Then y'all|Then|Thì|Thurh|Toda|Too right|Un|Und|ugeholl|Và|vaj|Vendar|Ve|wann|Wanneer|WEN|Wenn|When y'all|When|Wtedy|Wun|Y'know|Yeah nah|Yna|Youse know like when|Youse know when youse got|Y|Za predpokladu|Za předpokladu|Zadan|Zadani|Zadano|Zadate|Zadato|Zakładając|Zaradi|Zatati|Þa þe|Þa|Þá|Þegar|Þurh|Αλλά|Δεδομένου|Και|Όταν|Τότε|А також|Агар|Але|Али|Аммо|А|Әгәр|Әйтик|Әмма|Бирок|Ва|Вә|Дадено|Дано|Допустим|Если|Задате|Задати|Задато|И|І|К тому же|Када|Кад|Когато|Когда|Коли|Ләкин|Лекин|Нәтиҗәдә|Нехай|Но|Онда|Припустимо, що|Припустимо|Пусть|Также|Та|Тогда|Тоді|То|Унда|Һәм|Якщо|אבל|אזי|אז|בהינתן|וגם|כאשר|آنگاه|اذاً|اگر|اما|اور|با فرض|بالفرض|بفرض|پھر|تب|ثم|جب|عندما|فرض کیا|لكن|لیکن|متى|هنگامی|و|अगर|और|कदा|किन्तु|चूंकि|जब|तथा|तदा|तब|परन्तु|पर|यदि|ਅਤੇ|ਜਦੋਂ|ਜਿਵੇਂ ਕਿ|ਜੇਕਰ|ਤਦ|ਪਰ|అప్పుడు|ఈ పరిస్థితిలో|కాని|చెప్పబడినది|మరియు|ಆದರೆ|ನಂತರ|ನೀಡಿದ|ಮತ್ತು|ಸ್ಥಿತಿಯನ್ನು|กำหนดให้|ดังนั้น|แต่|เมื่อ|และ|그러면<|그리고<|단<|만약<|만일<|먼저<|조건<|하지만<|かつ<|しかし<|ただし<|ならば<|もし<|並且<|但し<|但是<|假如<|假定<|假設<|假设<|前提<|同时<|同時<|并且<|当<|當<|而且<|那么<|那麼<)(?=[ \t])/m,lookbehind:!0},string:{pattern:/"(?:\\.|[^"\\\r\n])*"|'(?:\\.|[^'\\\r\n])*'/,inside:{outline:{pattern:/<[^>]+>/,alias:"variable"}}},outline:{pattern:/<[^>]+>/,alias:"variable"}}}(Prism); \ No newline at end of file diff --git a/atlas/static/prism/prism-line-numbers.min.css b/atlas/static/prism/prism-line-numbers.min.css deleted file mode 100644 index 8170f64..0000000 --- a/atlas/static/prism/prism-line-numbers.min.css +++ /dev/null @@ -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} \ No newline at end of file diff --git a/atlas/static/prism/prism-line-numbers.min.js b/atlas/static/prism/prism-line-numbers.min.js deleted file mode 100644 index 1f12d2d..0000000 --- a/atlas/static/prism/prism-line-numbers.min.js +++ /dev/null @@ -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);ts&&(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("");(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;rcode[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} \ No newline at end of file diff --git a/atlas/static/prism/prism.min.js b/atlas/static/prism/prism.min.js deleted file mode 100644 index 054ce1e..0000000 --- a/atlas/static/prism/prism.min.js +++ /dev/null @@ -1 +0,0 @@ -var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(l){var n=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,e={},j={manual:l.Prism&&l.Prism.manual,disableWorkerMessageHandler:l.Prism&&l.Prism.disableWorkerMessageHandler,util:{encode:function e(t){return t instanceof C?new C(t.type,e(t.content),t.alias):Array.isArray(t)?t.map(e):t.replace(/&/g,"&").replace(/=i.reach);y+=b.value.length,b=b.next){var v=b.value;if(n.length>t.length)return;if(!(v instanceof C)){var F,x=1;if(m){if(!(F=L(f,y,t,p))||F.index>=t.length)break;var k=F.index,w=F.index+F[0].length,A=y;for(A+=b.value.length;A<=k;)b=b.next,A+=b.value.length;if(A-=b.value.length,y=A,b.value instanceof C)continue;for(var P=b;P!==n.tail&&(Ai.reach&&(i.reach=v),b.prev),S=(S&&(_=z(n,_,S),y+=S.length),O(n,_,x),new C(o,d?j.tokenize($,d):$,h,$));b=z(n,_,S),E&&z(n,b,E),1i.reach&&(i.reach=$.reach))}}}}}(e,r,t,r.head,0),r),i=[],o=s.head.next;o!==s.tail;)i.push(o.value),o=o.next;return i},hooks:{all:{},add:function(e,t){var n=j.hooks.all;n[e]=n[e]||[],n[e].push(t)},run:function(e,t){var n=j.hooks.all[e];if(n&&n.length)for(var a,r=0;a=n[r++];)a(t)}},Token:C};function C(e,t,n,a){this.type=e,this.content=t,this.alias=n,this.length=0|(a||"").length}function L(e,t,n,a){e.lastIndex=t;t=e.exec(n);return t&&a&&t[1]&&(e=t[1].length,t.index+=e,t[0]=t[0].slice(e)),t}function u(){var e={value:null,prev:null,next:null},t={value:null,prev:e,next:null};e.next=t,this.head=e,this.tail=t,this.length=0}function z(e,t,n){var a=t.next,n={value:n,prev:t,next:a};return t.next=n,a.prev=n,e.length++,n}function O(e,t,n){for(var a=t.next,r=0;r"+s.content+""},!l.document)return l.addEventListener&&(j.disableWorkerMessageHandler||l.addEventListener("message",function(e){var e=JSON.parse(e.data),t=e.language,n=e.code,e=e.immediateClose;l.postMessage(j.highlight(n,j.languages[t],t)),e&&l.close()},!1)),j;var a,e=j.util.currentScript();function r(){j.manual||j.highlightAll()}return e&&(j.filename=e.src,e.hasAttribute("data-manual")&&(j.manual=!0)),j.manual||("loading"===(a=document.readyState)||"interactive"===a&&e&&e.defer?document.addEventListener("DOMContentLoaded",r):window.requestAnimationFrame?window.requestAnimationFrame(r):window.setTimeout(r,16)),j}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism),Prism.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.languages.markup.doctype.inside["internal-subset"].inside=Prism.languages.markup,Prism.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),Object.defineProperty(Prism.languages.markup.tag,"addInlined",{value:function(e,t){var n={},n=(n["language-"+t]={pattern:/(^$)/i,lookbehind:!0,inside:Prism.languages[t]},n.cdata=/^$/i,{"included-cdata":{pattern://i,inside:n}}),t=(n["language-"+t]={pattern:/[\s\S]+/,inside:Prism.languages[t]},{});t[e]={pattern:RegExp(/(<__[^>]*>)(?:))*\]\]>|(?!)/.source.replace(/__/g,function(){return e}),"i"),lookbehind:!0,greedy:!0,inside:n},Prism.languages.insertBefore("markup","cdata",t)}}),Object.defineProperty(Prism.languages.markup.tag,"addAttribute",{value:function(e,t){Prism.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[t,"language-"+t],inside:Prism.languages[t]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup,Prism.languages.xml=Prism.languages.extend("markup",{}),Prism.languages.ssml=Prism.languages.xml,Prism.languages.atom=Prism.languages.xml,Prism.languages.rss=Prism.languages.xml,function(e){var t=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/,t=(e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:RegExp("@[\\w-](?:"+/[^;{\s"']|\s+(?!\s)/.source+"|"+t.source+")*?"+/(?:;|(?=\s*\{))/.source),inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+t.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+t.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+t.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:t,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css,e.languages.markup);t&&(t.tag.addInlined("style","css"),t.tag.addAttribute("style","css"))}(Prism),Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp(/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)/.source+/\//.source+"(?:"+/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}/.source+"|"+/(?:\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.)*\])*\])*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}v[dgimyus]{0,7}/.source+")"+/(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/.source),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:Prism.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),Prism.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),Prism.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),Prism.languages.markup&&(Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),Prism.languages.js=Prism.languages.javascript,function(){var l,u,g,c,e;void 0!==Prism&&"undefined"!=typeof document&&(Element.prototype.matches||(Element.prototype.matches=Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector),l={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"},c="pre[data-src]:not(["+(u="data-src-status")+'="loaded"]):not(['+u+'="'+(g="loading")+'"])',Prism.hooks.add("before-highlightall",function(e){e.selector+=", "+c}),Prism.hooks.add("before-sanity-check",function(e){var r,t,n,a,s,i,o=e.element;o.matches(c)&&(e.code="",o.setAttribute(u,g),(r=o.appendChild(document.createElement("CODE"))).textContent="Loading…",t=o.getAttribute("data-src"),"none"===(e=e.language)&&(n=(/\.(\w+)$/.exec(t)||[,"none"])[1],e=l[n]||n),Prism.util.setLanguage(r,e),Prism.util.setLanguage(o,e),(n=Prism.plugins.autoloader)&&n.loadLanguages(e),n=t,a=function(e){o.setAttribute(u,"loaded");var t,n,a=function(e){var t,n;if(e=/^\s*(\d+)\s*(?:(,)\s*(?:(\d+)\s*)?)?$/.exec(e||""))return t=Number(e[1]),n=e[2],e=e[3],n?e?[t,Number(e)]:[t,void 0]:[t,t]}(o.getAttribute("data-range"));a&&(t=e.split(/\r\n?|\n/g),n=a[0],a=null==a[1]?t.length:a[1],n<0&&(n+=t.length),n=Math.max(0,Math.min(n-1,t.length)),a<0&&(a+=t.length),a=Math.max(0,Math.min(a,t.length)),e=t.slice(n,a).join("\n"),o.hasAttribute("data-start")||o.setAttribute("data-start",String(n+1))),r.textContent=e,Prism.highlightElement(r)},s=function(e){o.setAttribute(u,"failed"),r.textContent=e},(i=new XMLHttpRequest).open("GET",n,!0),i.onreadystatechange=function(){4==i.readyState&&(i.status<400&&i.responseText?a(i.responseText):400<=i.status?s("✖ Error "+i.status+" while fetching file: "+i.statusText):s("✖ Error: File does not exist or is empty"))},i.send(null))}),e=!(Prism.plugins.fileHighlight={highlight:function(e){for(var t,n=(e||document).querySelectorAll(c),a=0;t=n[a++];)Prism.highlightElement(t)}}),Prism.fileHighlight=function(){e||(console.warn("Prism.fileHighlight is deprecated. Use `Prism.plugins.fileHighlight.highlight` instead."),e=!0),Prism.plugins.fileHighlight.highlight.apply(this,arguments)})}(); \ No newline at end of file diff --git a/build.py b/build.py index f9bf37e..c7e8ce4 100644 --- a/build.py +++ b/build.py @@ -2,59 +2,54 @@ """ Soleprint Build Tool -Builds the soleprint instance using modelgen for model generation. - -All modes copy files (no symlinks) for Docker compatibility. -After editing source files, re-run `python build.py` to update gen/. +Generates soleprint instances from source + room config. Usage: - python build.py # Build gen/standalone/ - python build.py --cfg # Build gen// - python build.py --all # Build all (standalone + rooms) - python build.py --deploy --output /path/ # Build for production - python build.py --models # Only regenerate models + python build.py # Build gen/standalone/ + python build.py --cfg # Build gen// + python build.py --all # Build all rooms + python build.py --output /path/ # Build to custom path + python build.py --models # Only regenerate models -Examples: - # Build standalone - python build.py - cd gen/standalone && .venv/bin/python run.py +Generated structure for standalone rooms: + gen/standalone/ + artery/, atlas/, station/, main.py, ... - # With room config - python build.py --cfg myroom - cd gen/myroom && .venv/bin/python run.py - - # Build all targets - python build.py --all - - # Build for deployment - python build.py --deploy --output ../deploy/soleprint/ +Generated structure for managed rooms: + gen// + / # Copied repos + ctrl + link/ # Bridge code + soleprint/ # Soleprint instance """ import argparse +import json import logging -import os import shutil import subprocess import sys from pathlib import Path -# SPR root is where this script lives 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__) +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): - """Create directory if it doesn't exist.""" 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.""" if target.is_symlink(): target.unlink() @@ -66,40 +61,153 @@ def copy_path(source: Path, target: Path): if source.is_dir(): 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: shutil.copy2(source, target) - log.info(f" Copied: {target.name}") + if not quiet: + log.info(f" {target.name}") def count_files(path: Path) -> int: - """Count files in directory recursively.""" return sum(1 for _ in path.rglob("*") if _.is_file()) -def generate_models(output_dir: Path, cfg_name: str | None = None): - """Generate models using modelgen tool. +def merge_into(source: Path, target: Path): + """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 - cfg_name: Room config name (e.g., 'amar'), or None for standalone - """ - room = cfg_name or "standalone" + +def parse_gitignore(gitignore_path: Path) -> set[str]: + """Parse .gitignore and return set of patterns to ignore.""" + patterns = set() + 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}/") + + # Copy ctrl from cfg//managed/ctrl/ + room_cfg = SPR_ROOT / "cfg" / cfg_name + managed_ctrl = room_cfg / "managed" / "ctrl" + if managed_ctrl.exists(): + ctrl_dir = managed_dir / "ctrl" + ensure_dir(ctrl_dir) + for item in managed_ctrl.iterdir(): + 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" if not config_path.exists(): - log.warning(f"Config not found at {config_path}") + log.warning(f"Config not found: {config_path}") return False - # Soleprint-specific: models go in models/pydantic/__init__.py models_file = output_dir / "models" / "pydantic" / "__init__.py" models_file.parent.mkdir(parents=True, exist_ok=True) - # Run modelgen as subprocess cmd = [ sys.executable, "-m", - "station.tools.modelgen", + "soleprint.station.tools.modelgen", "from-config", "--config", str(config_path), @@ -113,309 +221,143 @@ def generate_models(output_dir: Path, cfg_name: str | None = None): return result.returncode == 0 -def copy_cfg(output_dir: Path, cfg_name: str | None): - """Copy 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" +def copy_cfg(output_dir: Path, room: str): + """Copy room configuration files to output directory.""" room_cfg = SPR_ROOT / "cfg" / room 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 - 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" ensure_dir(cfg_dir) - room_config = room_cfg / "config.json" - if room_config.exists(): - copy_path(room_config, cfg_dir / "config.json") + if (room_cfg / "config.json").exists(): + copy_path(room_cfg / "config.json", cfg_dir / "config.json") - # Copy data/ to output data/ - room_data = room_cfg / "data" - if room_data.exists(): - log.info(f" Copying {room} data files...") - copy_path(room_data, output_dir / "data") + # data/ -> data/ + if (room_cfg / "data").exists(): + copy_path(room_cfg / "data", output_dir / "data") - # Copy .env.example to output root - env_example = room_cfg / ".env.example" - if env_example.exists(): - copy_path(env_example, output_dir / ".env.example") + # .env.example + if (room_cfg / ".env.example").exists(): + copy_path(room_cfg / ".env.example", output_dir / ".env.example") - # Copy room-specific databrowse depot if exists - room_databrowse = room_cfg / "databrowse" / "depot" - 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-specific models 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_path(room_models, output_dir / "models" / room) - # Copy room-specific soleprint config (docker-compose.yml, etc) + # Room-specific soleprint config (docker-compose.yml, etc) room_soleprint = room_cfg / "soleprint" if room_soleprint.exists(): - log.info(f" Copying {room} soleprint config...") for item in room_soleprint.iterdir(): copy_path(item, output_dir / item.name) + # Merge room-specific system configs (artery, atlas, station) + for system in ["artery", "atlas", "station"]: + room_system = room_cfg / 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: - gen/standalone/ or gen// - ├── main.py - ├── run.py - ├── index.html - ├── requirements.txt - ├── Dockerfile - ├── dataloader/ - ├── artery/ - ├── atlas/ - ├── station/ - ├── data/ - ├── cfg/ - ├── .env.example # From cfg//.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) +def build_soleprint(output_dir: Path, room: str): + """Build soleprint folder with core + room config merged.""" + soleprint = SPR_ROOT / "soleprint" # Soleprint core files - 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") + log.info("Copying soleprint core...") + for name in [ + "main.py", + "run.py", + "index.html", + "generate.html", + "requirements.txt", + "Dockerfile", + ]: + if (soleprint / name).exists(): + copy_path(soleprint / name, output_dir / name) copy_path(soleprint / "dataloader", output_dir / "dataloader") - if (soleprint / "Dockerfile").exists(): - copy_path(soleprint / "Dockerfile", output_dir / "Dockerfile") # System directories - log.info("\nCopying systems...") + log.info("Copying systems...") for system in ["artery", "atlas", "station"]: - source = SPR_ROOT / system + source = soleprint / 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) + # Room config (includes merging room-specific artery/atlas/station) + copy_cfg(output_dir, room) - # Models (generated) - log.info("\nGenerating models...") - if not generate_models(output_dir, cfg_name): - log.warning("Model generation failed, you may need to run it manually") + # Generate models + log.info("Generating models...") + if not generate_models(output_dir, room): + 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}" - ) + +def build(output_dir: Path, cfg_name: str | None = None): + """Build complete room instance.""" + room = cfg_name or "standalone" + config = load_config(cfg_name) + managed = config.get("managed") + + log.info(f"\n=== Building {room} ===") + ensure_dir(output_dir) + + if managed: + # 3-folder structure: /, 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: - log.info(f"\nAfter editing source, rebuild with: python build.py") + # Standalone: everything in output_dir + build_soleprint(output_dir, room) + + log.info(f"\n✓ Built: {output_dir}") -def build_deploy(output_dir: Path, cfg_name: str | None = None): - """ - 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" - existing = SPR_ROOT / "gen" / room / "models" - if existing.exists(): - log.info(" Using existing models from gen/") - copy_path(existing, output_dir / "models") - - # Copy schema.json for reference - 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(): +def build_models_only(): """Only regenerate models.""" - log.info("\n=== Generating models only ===") - - output_dir = SPR_ROOT / "gen" - ensure_dir(output_dir) - - if generate_models(output_dir): - log.info("\n✓ Models generated!") + log.info("Generating models...") + if generate_models(SPR_ROOT / "gen"): + log.info("✓ Models generated") else: log.error("Model generation failed") sys.exit(1) def main(): - parser = argparse.ArgumentParser( - description="Soleprint Build Tool", - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=__doc__, - ) + parser = argparse.ArgumentParser(description="Soleprint Build Tool") - parser.add_argument( - "--output", - "-o", - type=Path, - default=None, - help="Output directory (default: gen/standalone/ or gen//)", - ) - 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", - ) + parser.add_argument("--output", "-o", type=Path, help="Output directory") + parser.add_argument("--cfg", "-c", type=str, help="Room config name") + parser.add_argument("--all", action="store_true", help="Build all rooms") + parser.add_argument("--models", action="store_true", help="Only regenerate models") args = parser.parse_args() if args.models: - build_models() - elif args.deploy: - if not args.output: - log.error("--deploy requires --output") - sys.exit(1) - build_deploy(args.output.resolve(), args.cfg) + build_models_only() elif args.all: - # Build standalone - build_dev(SPR_ROOT / "gen" / "standalone", None) - # Build all room configs - cfg_dir = SPR_ROOT / "cfg" - for room in cfg_dir.iterdir(): + build(SPR_ROOT / "gen" / "standalone", None) + for room in (SPR_ROOT / "cfg").iterdir(): 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: - # Determine output directory if args.output: output_dir = args.output.resolve() elif args.cfg: output_dir = SPR_ROOT / "gen" / args.cfg else: output_dir = SPR_ROOT / "gen" / "standalone" - build_dev(output_dir, args.cfg) + build(output_dir, args.cfg) if __name__ == "__main__": diff --git a/artery/shunts/amar/.env.example b/cfg/amar/artery/shunts/amar/.env.example similarity index 100% rename from artery/shunts/amar/.env.example rename to cfg/amar/artery/shunts/amar/.env.example diff --git a/artery/shunts/amar/README.md b/cfg/amar/artery/shunts/amar/README.md similarity index 100% rename from artery/shunts/amar/README.md rename to cfg/amar/artery/shunts/amar/README.md diff --git a/artery/shunts/amar/__init__.py b/cfg/amar/artery/shunts/amar/__init__.py similarity index 100% rename from artery/shunts/amar/__init__.py rename to cfg/amar/artery/shunts/amar/__init__.py diff --git a/artery/shunts/amar/api/__init__.py b/cfg/amar/artery/shunts/amar/api/__init__.py similarity index 100% rename from artery/shunts/amar/api/__init__.py rename to cfg/amar/artery/shunts/amar/api/__init__.py diff --git a/artery/shunts/amar/api/routes.py b/cfg/amar/artery/shunts/amar/api/routes.py similarity index 100% rename from artery/shunts/amar/api/routes.py rename to cfg/amar/artery/shunts/amar/api/routes.py diff --git a/artery/shunts/amar/core/__init__.py b/cfg/amar/artery/shunts/amar/core/__init__.py similarity index 100% rename from artery/shunts/amar/core/__init__.py rename to cfg/amar/artery/shunts/amar/core/__init__.py diff --git a/artery/shunts/amar/core/config.py b/cfg/amar/artery/shunts/amar/core/config.py similarity index 100% rename from artery/shunts/amar/core/config.py rename to cfg/amar/artery/shunts/amar/core/config.py diff --git a/artery/shunts/amar/main.py b/cfg/amar/artery/shunts/amar/main.py similarity index 100% rename from artery/shunts/amar/main.py rename to cfg/amar/artery/shunts/amar/main.py diff --git a/artery/shunts/amar/requirements.txt b/cfg/amar/artery/shunts/amar/requirements.txt similarity index 100% rename from artery/shunts/amar/requirements.txt rename to cfg/amar/artery/shunts/amar/requirements.txt diff --git a/artery/shunts/amar/run.py b/cfg/amar/artery/shunts/amar/run.py similarity index 100% rename from artery/shunts/amar/run.py rename to cfg/amar/artery/shunts/amar/run.py diff --git a/artery/shunts/amar/templates/index.html b/cfg/amar/artery/shunts/amar/templates/index.html similarity index 100% rename from artery/shunts/amar/templates/index.html rename to cfg/amar/artery/shunts/amar/templates/index.html diff --git a/atlas/book/arch-model/01-backend-architecture.dot b/cfg/amar/atlas/books/arch/01-backend-architecture.dot similarity index 100% rename from atlas/book/arch-model/01-backend-architecture.dot rename to cfg/amar/atlas/books/arch/01-backend-architecture.dot diff --git a/atlas/book/arch-model/01-backend-architecture.svg b/cfg/amar/atlas/books/arch/01-backend-architecture.svg similarity index 100% rename from atlas/book/arch-model/01-backend-architecture.svg rename to cfg/amar/atlas/books/arch/01-backend-architecture.svg diff --git a/atlas/book/arch-model/02-frontend-architecture.dot b/cfg/amar/atlas/books/arch/02-frontend-architecture.dot similarity index 100% rename from atlas/book/arch-model/02-frontend-architecture.dot rename to cfg/amar/atlas/books/arch/02-frontend-architecture.dot diff --git a/atlas/book/arch-model/02-frontend-architecture.svg b/cfg/amar/atlas/books/arch/02-frontend-architecture.svg similarity index 100% rename from atlas/book/arch-model/02-frontend-architecture.svg rename to cfg/amar/atlas/books/arch/02-frontend-architecture.svg diff --git a/atlas/book/arch-model/03-data-model.dot b/cfg/amar/atlas/books/arch/03-data-model.dot similarity index 100% rename from atlas/book/arch-model/03-data-model.dot rename to cfg/amar/atlas/books/arch/03-data-model.dot diff --git a/atlas/book/arch-model/03-data-model.svg b/cfg/amar/atlas/books/arch/03-data-model.svg similarity index 100% rename from atlas/book/arch-model/03-data-model.svg rename to cfg/amar/atlas/books/arch/03-data-model.svg diff --git a/atlas/book/arch-model/04-data-model-simple.dot b/cfg/amar/atlas/books/arch/04-data-model-simple.dot similarity index 100% rename from atlas/book/arch-model/04-data-model-simple.dot rename to cfg/amar/atlas/books/arch/04-data-model-simple.dot diff --git a/atlas/book/arch-model/04-data-model-simple.svg b/cfg/amar/atlas/books/arch/04-data-model-simple.svg similarity index 100% rename from atlas/book/arch-model/04-data-model-simple.svg rename to cfg/amar/atlas/books/arch/04-data-model-simple.svg diff --git a/atlas/book/arch-model/graph.html b/cfg/amar/atlas/books/arch/graph.html similarity index 100% rename from atlas/book/arch-model/graph.html rename to cfg/amar/atlas/books/arch/graph.html diff --git a/atlas/book/arch-model/index.html b/cfg/amar/atlas/books/arch/index.html similarity index 100% rename from atlas/book/arch-model/index.html rename to cfg/amar/atlas/books/arch/index.html diff --git a/atlas/book/arch-model/styles.css b/cfg/amar/atlas/books/arch/styles.css similarity index 100% rename from atlas/book/arch-model/styles.css rename to cfg/amar/atlas/books/arch/styles.css diff --git a/atlas/book/drive-index/index.html b/cfg/amar/atlas/books/drive-index/index.html similarity index 100% rename from atlas/book/drive-index/index.html rename to cfg/amar/atlas/books/drive-index/index.html diff --git a/atlas/book/drive-index/index.md b/cfg/amar/atlas/books/drive-index/index.md similarity index 100% rename from atlas/book/drive-index/index.md rename to cfg/amar/atlas/books/drive-index/index.md diff --git a/atlas/book/feature-form-samples/CLAUDE.md b/cfg/amar/atlas/books/feature-form-samples/CLAUDE.md similarity index 100% rename from atlas/book/feature-form-samples/CLAUDE.md rename to cfg/amar/atlas/books/feature-form-samples/CLAUDE.md diff --git a/atlas/book/feature-form-samples/detail.html b/cfg/amar/atlas/books/feature-form-samples/detail.html similarity index 100% rename from atlas/book/feature-form-samples/detail.html rename to cfg/amar/atlas/books/feature-form-samples/detail.html diff --git a/atlas/book/feature-form-samples/feature-form/.larder b/cfg/amar/atlas/books/feature-form-samples/feature-form/.larder similarity index 100% rename from atlas/book/feature-form-samples/feature-form/.larder rename to cfg/amar/atlas/books/feature-form-samples/feature-form/.larder diff --git a/atlas/book/feature-form-samples/feature-form/backoffice/01-gestion-solicitudes.md b/cfg/amar/atlas/books/feature-form-samples/feature-form/backoffice/01-gestion-solicitudes.md similarity index 100% rename from atlas/book/feature-form-samples/feature-form/backoffice/01-gestion-solicitudes.md rename to cfg/amar/atlas/books/feature-form-samples/feature-form/backoffice/01-gestion-solicitudes.md diff --git a/atlas/book/feature-form-samples/feature-form/backoffice/02-gestion-usuarios.md b/cfg/amar/atlas/books/feature-form-samples/feature-form/backoffice/02-gestion-usuarios.md similarity index 100% rename from atlas/book/feature-form-samples/feature-form/backoffice/02-gestion-usuarios.md rename to cfg/amar/atlas/books/feature-form-samples/feature-form/backoffice/02-gestion-usuarios.md diff --git a/atlas/book/feature-form-samples/feature-form/backoffice/03-gestion-servicios.md b/cfg/amar/atlas/books/feature-form-samples/feature-form/backoffice/03-gestion-servicios.md similarity index 100% rename from atlas/book/feature-form-samples/feature-form/backoffice/03-gestion-servicios.md rename to cfg/amar/atlas/books/feature-form-samples/feature-form/backoffice/03-gestion-servicios.md diff --git a/atlas/book/feature-form-samples/feature-form/backoffice/04-reembolsos.md b/cfg/amar/atlas/books/feature-form-samples/feature-form/backoffice/04-reembolsos.md similarity index 100% rename from atlas/book/feature-form-samples/feature-form/backoffice/04-reembolsos.md rename to cfg/amar/atlas/books/feature-form-samples/feature-form/backoffice/04-reembolsos.md diff --git a/atlas/book/feature-form-samples/feature-form/backoffice/05-reportes.md b/cfg/amar/atlas/books/feature-form-samples/feature-form/backoffice/05-reportes.md similarity index 100% rename from atlas/book/feature-form-samples/feature-form/backoffice/05-reportes.md rename to cfg/amar/atlas/books/feature-form-samples/feature-form/backoffice/05-reportes.md diff --git a/atlas/book/feature-form-samples/feature-form/pet-owner/01-registro.md b/cfg/amar/atlas/books/feature-form-samples/feature-form/pet-owner/01-registro.md similarity index 100% rename from atlas/book/feature-form-samples/feature-form/pet-owner/01-registro.md rename to cfg/amar/atlas/books/feature-form-samples/feature-form/pet-owner/01-registro.md diff --git a/atlas/book/feature-form-samples/feature-form/pet-owner/02-reservar-turno.md b/cfg/amar/atlas/books/feature-form-samples/feature-form/pet-owner/02-reservar-turno.md similarity index 100% rename from atlas/book/feature-form-samples/feature-form/pet-owner/02-reservar-turno.md rename to cfg/amar/atlas/books/feature-form-samples/feature-form/pet-owner/02-reservar-turno.md diff --git a/atlas/book/feature-form-samples/feature-form/pet-owner/03-gestion-mascotas.md b/cfg/amar/atlas/books/feature-form-samples/feature-form/pet-owner/03-gestion-mascotas.md similarity index 100% rename from atlas/book/feature-form-samples/feature-form/pet-owner/03-gestion-mascotas.md rename to cfg/amar/atlas/books/feature-form-samples/feature-form/pet-owner/03-gestion-mascotas.md diff --git a/atlas/book/feature-form-samples/feature-form/pet-owner/04-pago-turno.md b/cfg/amar/atlas/books/feature-form-samples/feature-form/pet-owner/04-pago-turno.md similarity index 100% rename from atlas/book/feature-form-samples/feature-form/pet-owner/04-pago-turno.md rename to cfg/amar/atlas/books/feature-form-samples/feature-form/pet-owner/04-pago-turno.md diff --git a/atlas/book/feature-form-samples/feature-form/pet-owner/05-historial-medico.md b/cfg/amar/atlas/books/feature-form-samples/feature-form/pet-owner/05-historial-medico.md similarity index 100% rename from atlas/book/feature-form-samples/feature-form/pet-owner/05-historial-medico.md rename to cfg/amar/atlas/books/feature-form-samples/feature-form/pet-owner/05-historial-medico.md diff --git a/atlas/book/feature-form-samples/feature-form/veterinarian/01-aceptar-solicitud.md b/cfg/amar/atlas/books/feature-form-samples/feature-form/veterinarian/01-aceptar-solicitud.md similarity index 100% rename from atlas/book/feature-form-samples/feature-form/veterinarian/01-aceptar-solicitud.md rename to cfg/amar/atlas/books/feature-form-samples/feature-form/veterinarian/01-aceptar-solicitud.md diff --git a/atlas/book/feature-form-samples/feature-form/veterinarian/02-gestion-agenda.md b/cfg/amar/atlas/books/feature-form-samples/feature-form/veterinarian/02-gestion-agenda.md similarity index 100% rename from atlas/book/feature-form-samples/feature-form/veterinarian/02-gestion-agenda.md rename to cfg/amar/atlas/books/feature-form-samples/feature-form/veterinarian/02-gestion-agenda.md diff --git a/atlas/book/feature-form-samples/feature-form/veterinarian/03-realizar-visita.md b/cfg/amar/atlas/books/feature-form-samples/feature-form/veterinarian/03-realizar-visita.md similarity index 100% rename from atlas/book/feature-form-samples/feature-form/veterinarian/03-realizar-visita.md rename to cfg/amar/atlas/books/feature-form-samples/feature-form/veterinarian/03-realizar-visita.md diff --git a/atlas/book/feature-form-samples/feature-form/veterinarian/04-zonas-cobertura.md b/cfg/amar/atlas/books/feature-form-samples/feature-form/veterinarian/04-zonas-cobertura.md similarity index 100% rename from atlas/book/feature-form-samples/feature-form/veterinarian/04-zonas-cobertura.md rename to cfg/amar/atlas/books/feature-form-samples/feature-form/veterinarian/04-zonas-cobertura.md diff --git a/atlas/book/feature-form-samples/feature-form/veterinarian/05-historial-pacientes.md b/cfg/amar/atlas/books/feature-form-samples/feature-form/veterinarian/05-historial-pacientes.md similarity index 100% rename from atlas/book/feature-form-samples/feature-form/veterinarian/05-historial-pacientes.md rename to cfg/amar/atlas/books/feature-form-samples/feature-form/veterinarian/05-historial-pacientes.md diff --git a/atlas/book/feature-form-samples/index.html b/cfg/amar/atlas/books/feature-form-samples/index.html similarity index 100% rename from atlas/book/feature-form-samples/index.html rename to cfg/amar/atlas/books/feature-form-samples/index.html diff --git a/atlas/book/feature-form-samples/template/feature-form.md b/cfg/amar/atlas/books/feature-form-samples/template/feature-form.md similarity index 100% rename from atlas/book/feature-form-samples/template/feature-form.md rename to cfg/amar/atlas/books/feature-form-samples/template/feature-form.md diff --git a/atlas/book/gherkin-samples/CLAUDE.md b/cfg/amar/atlas/books/gherkin-samples/CLAUDE.md similarity index 100% rename from atlas/book/gherkin-samples/CLAUDE.md rename to cfg/amar/atlas/books/gherkin-samples/CLAUDE.md diff --git a/atlas/book/gherkin-samples/detail.html b/cfg/amar/atlas/books/gherkin-samples/detail.html similarity index 100% rename from atlas/book/gherkin-samples/detail.html rename to cfg/amar/atlas/books/gherkin-samples/detail.html diff --git a/atlas/book/gherkin-samples/en/backoffice/01-gestion-solicitudes.feature b/cfg/amar/atlas/books/gherkin-samples/en/backoffice/01-gestion-solicitudes.feature similarity index 100% rename from atlas/book/gherkin-samples/en/backoffice/01-gestion-solicitudes.feature rename to cfg/amar/atlas/books/gherkin-samples/en/backoffice/01-gestion-solicitudes.feature diff --git a/atlas/book/gherkin-samples/en/backoffice/02-gestion-usuarios.feature b/cfg/amar/atlas/books/gherkin-samples/en/backoffice/02-gestion-usuarios.feature similarity index 100% rename from atlas/book/gherkin-samples/en/backoffice/02-gestion-usuarios.feature rename to cfg/amar/atlas/books/gherkin-samples/en/backoffice/02-gestion-usuarios.feature diff --git a/atlas/book/gherkin-samples/en/backoffice/03-gestion-servicios.feature b/cfg/amar/atlas/books/gherkin-samples/en/backoffice/03-gestion-servicios.feature similarity index 100% rename from atlas/book/gherkin-samples/en/backoffice/03-gestion-servicios.feature rename to cfg/amar/atlas/books/gherkin-samples/en/backoffice/03-gestion-servicios.feature diff --git a/atlas/book/gherkin-samples/en/backoffice/04-reembolsos.feature b/cfg/amar/atlas/books/gherkin-samples/en/backoffice/04-reembolsos.feature similarity index 100% rename from atlas/book/gherkin-samples/en/backoffice/04-reembolsos.feature rename to cfg/amar/atlas/books/gherkin-samples/en/backoffice/04-reembolsos.feature diff --git a/atlas/book/gherkin-samples/en/backoffice/05-reportes.feature b/cfg/amar/atlas/books/gherkin-samples/en/backoffice/05-reportes.feature similarity index 100% rename from atlas/book/gherkin-samples/en/backoffice/05-reportes.feature rename to cfg/amar/atlas/books/gherkin-samples/en/backoffice/05-reportes.feature diff --git a/atlas/book/gherkin-samples/en/pet-owner/01-registro.feature b/cfg/amar/atlas/books/gherkin-samples/en/pet-owner/01-registro.feature similarity index 100% rename from atlas/book/gherkin-samples/en/pet-owner/01-registro.feature rename to cfg/amar/atlas/books/gherkin-samples/en/pet-owner/01-registro.feature diff --git a/atlas/book/gherkin-samples/en/pet-owner/02-reservar-turno.feature b/cfg/amar/atlas/books/gherkin-samples/en/pet-owner/02-reservar-turno.feature similarity index 100% rename from atlas/book/gherkin-samples/en/pet-owner/02-reservar-turno.feature rename to cfg/amar/atlas/books/gherkin-samples/en/pet-owner/02-reservar-turno.feature diff --git a/atlas/book/gherkin-samples/en/pet-owner/03-gestion-mascotas.feature b/cfg/amar/atlas/books/gherkin-samples/en/pet-owner/03-gestion-mascotas.feature similarity index 100% rename from atlas/book/gherkin-samples/en/pet-owner/03-gestion-mascotas.feature rename to cfg/amar/atlas/books/gherkin-samples/en/pet-owner/03-gestion-mascotas.feature diff --git a/atlas/book/gherkin-samples/en/pet-owner/04-pago-turno.feature b/cfg/amar/atlas/books/gherkin-samples/en/pet-owner/04-pago-turno.feature similarity index 100% rename from atlas/book/gherkin-samples/en/pet-owner/04-pago-turno.feature rename to cfg/amar/atlas/books/gherkin-samples/en/pet-owner/04-pago-turno.feature diff --git a/atlas/book/gherkin-samples/en/pet-owner/05-historial-medico.feature b/cfg/amar/atlas/books/gherkin-samples/en/pet-owner/05-historial-medico.feature similarity index 100% rename from atlas/book/gherkin-samples/en/pet-owner/05-historial-medico.feature rename to cfg/amar/atlas/books/gherkin-samples/en/pet-owner/05-historial-medico.feature diff --git a/atlas/book/gherkin-samples/en/veterinarian/01-aceptar-solicitud.feature b/cfg/amar/atlas/books/gherkin-samples/en/veterinarian/01-aceptar-solicitud.feature similarity index 100% rename from atlas/book/gherkin-samples/en/veterinarian/01-aceptar-solicitud.feature rename to cfg/amar/atlas/books/gherkin-samples/en/veterinarian/01-aceptar-solicitud.feature diff --git a/atlas/book/gherkin-samples/en/veterinarian/02-gestion-agenda.feature b/cfg/amar/atlas/books/gherkin-samples/en/veterinarian/02-gestion-agenda.feature similarity index 100% rename from atlas/book/gherkin-samples/en/veterinarian/02-gestion-agenda.feature rename to cfg/amar/atlas/books/gherkin-samples/en/veterinarian/02-gestion-agenda.feature diff --git a/atlas/book/gherkin-samples/en/veterinarian/03-realizar-visita.feature b/cfg/amar/atlas/books/gherkin-samples/en/veterinarian/03-realizar-visita.feature similarity index 100% rename from atlas/book/gherkin-samples/en/veterinarian/03-realizar-visita.feature rename to cfg/amar/atlas/books/gherkin-samples/en/veterinarian/03-realizar-visita.feature diff --git a/atlas/book/gherkin-samples/en/veterinarian/04-zonas-cobertura.feature b/cfg/amar/atlas/books/gherkin-samples/en/veterinarian/04-zonas-cobertura.feature similarity index 100% rename from atlas/book/gherkin-samples/en/veterinarian/04-zonas-cobertura.feature rename to cfg/amar/atlas/books/gherkin-samples/en/veterinarian/04-zonas-cobertura.feature diff --git a/atlas/book/gherkin-samples/en/veterinarian/05-historial-pacientes.feature b/cfg/amar/atlas/books/gherkin-samples/en/veterinarian/05-historial-pacientes.feature similarity index 100% rename from atlas/book/gherkin-samples/en/veterinarian/05-historial-pacientes.feature rename to cfg/amar/atlas/books/gherkin-samples/en/veterinarian/05-historial-pacientes.feature diff --git a/atlas/book/gherkin-samples/es/backoffice/01-gestion-solicitudes.feature b/cfg/amar/atlas/books/gherkin-samples/es/backoffice/01-gestion-solicitudes.feature similarity index 100% rename from atlas/book/gherkin-samples/es/backoffice/01-gestion-solicitudes.feature rename to cfg/amar/atlas/books/gherkin-samples/es/backoffice/01-gestion-solicitudes.feature diff --git a/atlas/book/gherkin-samples/es/backoffice/02-gestion-usuarios.feature b/cfg/amar/atlas/books/gherkin-samples/es/backoffice/02-gestion-usuarios.feature similarity index 100% rename from atlas/book/gherkin-samples/es/backoffice/02-gestion-usuarios.feature rename to cfg/amar/atlas/books/gherkin-samples/es/backoffice/02-gestion-usuarios.feature diff --git a/atlas/book/gherkin-samples/es/backoffice/03-gestion-servicios.feature b/cfg/amar/atlas/books/gherkin-samples/es/backoffice/03-gestion-servicios.feature similarity index 100% rename from atlas/book/gherkin-samples/es/backoffice/03-gestion-servicios.feature rename to cfg/amar/atlas/books/gherkin-samples/es/backoffice/03-gestion-servicios.feature diff --git a/atlas/book/gherkin-samples/es/backoffice/04-reembolsos.feature b/cfg/amar/atlas/books/gherkin-samples/es/backoffice/04-reembolsos.feature similarity index 100% rename from atlas/book/gherkin-samples/es/backoffice/04-reembolsos.feature rename to cfg/amar/atlas/books/gherkin-samples/es/backoffice/04-reembolsos.feature diff --git a/atlas/book/gherkin-samples/es/backoffice/05-reportes.feature b/cfg/amar/atlas/books/gherkin-samples/es/backoffice/05-reportes.feature similarity index 100% rename from atlas/book/gherkin-samples/es/backoffice/05-reportes.feature rename to cfg/amar/atlas/books/gherkin-samples/es/backoffice/05-reportes.feature diff --git a/atlas/book/gherkin-samples/es/pet-owner/01-registro.feature b/cfg/amar/atlas/books/gherkin-samples/es/pet-owner/01-registro.feature similarity index 100% rename from atlas/book/gherkin-samples/es/pet-owner/01-registro.feature rename to cfg/amar/atlas/books/gherkin-samples/es/pet-owner/01-registro.feature diff --git a/atlas/book/gherkin-samples/es/pet-owner/02-reservar-turno.feature b/cfg/amar/atlas/books/gherkin-samples/es/pet-owner/02-reservar-turno.feature similarity index 100% rename from atlas/book/gherkin-samples/es/pet-owner/02-reservar-turno.feature rename to cfg/amar/atlas/books/gherkin-samples/es/pet-owner/02-reservar-turno.feature diff --git a/atlas/book/gherkin-samples/es/pet-owner/03-gestion-mascotas.feature b/cfg/amar/atlas/books/gherkin-samples/es/pet-owner/03-gestion-mascotas.feature similarity index 100% rename from atlas/book/gherkin-samples/es/pet-owner/03-gestion-mascotas.feature rename to cfg/amar/atlas/books/gherkin-samples/es/pet-owner/03-gestion-mascotas.feature diff --git a/atlas/book/gherkin-samples/es/pet-owner/04-pago-turno.feature b/cfg/amar/atlas/books/gherkin-samples/es/pet-owner/04-pago-turno.feature similarity index 100% rename from atlas/book/gherkin-samples/es/pet-owner/04-pago-turno.feature rename to cfg/amar/atlas/books/gherkin-samples/es/pet-owner/04-pago-turno.feature diff --git a/atlas/book/gherkin-samples/es/pet-owner/05-historial-medico.feature b/cfg/amar/atlas/books/gherkin-samples/es/pet-owner/05-historial-medico.feature similarity index 100% rename from atlas/book/gherkin-samples/es/pet-owner/05-historial-medico.feature rename to cfg/amar/atlas/books/gherkin-samples/es/pet-owner/05-historial-medico.feature diff --git a/atlas/book/gherkin-samples/es/veterinarian/01-aceptar-solicitud.feature b/cfg/amar/atlas/books/gherkin-samples/es/veterinarian/01-aceptar-solicitud.feature similarity index 100% rename from atlas/book/gherkin-samples/es/veterinarian/01-aceptar-solicitud.feature rename to cfg/amar/atlas/books/gherkin-samples/es/veterinarian/01-aceptar-solicitud.feature diff --git a/atlas/book/gherkin-samples/es/veterinarian/02-gestion-agenda.feature b/cfg/amar/atlas/books/gherkin-samples/es/veterinarian/02-gestion-agenda.feature similarity index 100% rename from atlas/book/gherkin-samples/es/veterinarian/02-gestion-agenda.feature rename to cfg/amar/atlas/books/gherkin-samples/es/veterinarian/02-gestion-agenda.feature diff --git a/atlas/book/gherkin-samples/es/veterinarian/03-realizar-visita.feature b/cfg/amar/atlas/books/gherkin-samples/es/veterinarian/03-realizar-visita.feature similarity index 100% rename from atlas/book/gherkin-samples/es/veterinarian/03-realizar-visita.feature rename to cfg/amar/atlas/books/gherkin-samples/es/veterinarian/03-realizar-visita.feature diff --git a/atlas/book/gherkin-samples/es/veterinarian/04-zonas-cobertura.feature b/cfg/amar/atlas/books/gherkin-samples/es/veterinarian/04-zonas-cobertura.feature similarity index 100% rename from atlas/book/gherkin-samples/es/veterinarian/04-zonas-cobertura.feature rename to cfg/amar/atlas/books/gherkin-samples/es/veterinarian/04-zonas-cobertura.feature diff --git a/atlas/book/gherkin-samples/es/veterinarian/05-historial-pacientes.feature b/cfg/amar/atlas/books/gherkin-samples/es/veterinarian/05-historial-pacientes.feature similarity index 100% rename from atlas/book/gherkin-samples/es/veterinarian/05-historial-pacientes.feature rename to cfg/amar/atlas/books/gherkin-samples/es/veterinarian/05-historial-pacientes.feature diff --git a/atlas/book/gherkin-samples/index.html b/cfg/amar/atlas/books/gherkin-samples/index.html similarity index 100% rename from atlas/book/gherkin-samples/index.html rename to cfg/amar/atlas/books/gherkin-samples/index.html diff --git a/cfg/amar/config.json b/cfg/amar/config.json index ec62d31..9af9c57 100644 --- a/cfg/amar/config.json +++ b/cfg/amar/config.json @@ -8,6 +8,13 @@ "icon": "👣", "hub_port": 12000 }, + "managed": { + "name": "amar", + "repos": { + "backend": "/home/mariano/wdir/ama/amar_django_back", + "frontend": "/home/mariano/wdir/ama/amar_frontend" + } + }, "systems": [ { "key": "data_flow", diff --git a/cfg/amar/Dockerfile.backend b/cfg/amar/managed/ctrl/Dockerfile.backend similarity index 100% rename from cfg/amar/Dockerfile.backend rename to cfg/amar/managed/ctrl/Dockerfile.backend diff --git a/cfg/amar/Dockerfile.frontend b/cfg/amar/managed/ctrl/Dockerfile.frontend similarity index 100% rename from cfg/amar/Dockerfile.frontend rename to cfg/amar/managed/ctrl/Dockerfile.frontend diff --git a/cfg/amar/docker-compose.yml b/cfg/amar/managed/ctrl/docker-compose.yml similarity index 100% rename from cfg/amar/docker-compose.yml rename to cfg/amar/managed/ctrl/docker-compose.yml diff --git a/cfg/amar/monitors/turnos/__init__.py b/cfg/amar/station/monitors/turnos/__init__.py similarity index 100% rename from cfg/amar/monitors/turnos/__init__.py rename to cfg/amar/station/monitors/turnos/__init__.py diff --git a/cfg/amar/monitors/turnos/index.html b/cfg/amar/station/monitors/turnos/index.html similarity index 100% rename from cfg/amar/monitors/turnos/index.html rename to cfg/amar/station/monitors/turnos/index.html diff --git a/cfg/amar/monitors/turnos/list.html b/cfg/amar/station/monitors/turnos/list.html similarity index 100% rename from cfg/amar/monitors/turnos/list.html rename to cfg/amar/station/monitors/turnos/list.html diff --git a/cfg/amar/monitors/turnos/main.py b/cfg/amar/station/monitors/turnos/main.py similarity index 100% rename from cfg/amar/monitors/turnos/main.py rename to cfg/amar/station/monitors/turnos/main.py diff --git a/cfg/amar/databrowse/depot/scenarios.json b/cfg/amar/station/tools/databrowse/depot/scenarios.json similarity index 100% rename from cfg/amar/databrowse/depot/scenarios.json rename to cfg/amar/station/tools/databrowse/depot/scenarios.json diff --git a/cfg/amar/databrowse/depot/schema.json b/cfg/amar/station/tools/databrowse/depot/schema.json similarity index 100% rename from cfg/amar/databrowse/depot/schema.json rename to cfg/amar/station/tools/databrowse/depot/schema.json diff --git a/cfg/amar/databrowse/depot/views.json b/cfg/amar/station/tools/databrowse/depot/views.json similarity index 100% rename from cfg/amar/databrowse/depot/views.json rename to cfg/amar/station/tools/databrowse/depot/views.json diff --git a/station/tools/datagen/amar.py b/cfg/amar/station/tools/datagen/amar.py similarity index 100% rename from station/tools/datagen/amar.py rename to cfg/amar/station/tools/datagen/amar.py diff --git a/cfg/amar/tester/tests/README.md b/cfg/amar/station/tools/tester/tests/README.md similarity index 100% rename from cfg/amar/tester/tests/README.md rename to cfg/amar/station/tools/tester/tests/README.md diff --git a/cfg/amar/tester/tests/__init__.py b/cfg/amar/station/tools/tester/tests/__init__.py similarity index 100% rename from cfg/amar/tester/tests/__init__.py rename to cfg/amar/station/tools/tester/tests/__init__.py diff --git a/cfg/amar/tester/tests/base.py b/cfg/amar/station/tools/tester/tests/base.py similarity index 100% rename from cfg/amar/tester/tests/base.py rename to cfg/amar/station/tools/tester/tests/base.py diff --git a/cfg/amar/tester/tests/conftest.py b/cfg/amar/station/tools/tester/tests/conftest.py similarity index 100% rename from cfg/amar/tester/tests/conftest.py rename to cfg/amar/station/tools/tester/tests/conftest.py diff --git a/cfg/amar/tester/tests/endpoints.py b/cfg/amar/station/tools/tester/tests/endpoints.py similarity index 100% rename from cfg/amar/tester/tests/endpoints.py rename to cfg/amar/station/tools/tester/tests/endpoints.py diff --git a/cfg/amar/tester/tests/helpers.py b/cfg/amar/station/tools/tester/tests/helpers.py similarity index 100% rename from cfg/amar/tester/tests/helpers.py rename to cfg/amar/station/tools/tester/tests/helpers.py diff --git a/cfg/amar/tester/tests/mascotas/__init__.py b/cfg/amar/station/tools/tester/tests/mascotas/__init__.py similarity index 100% rename from cfg/amar/tester/tests/mascotas/__init__.py rename to cfg/amar/station/tools/tester/tests/mascotas/__init__.py diff --git a/cfg/amar/tester/tests/mascotas/test_coverage.py b/cfg/amar/station/tools/tester/tests/mascotas/test_coverage.py similarity index 100% rename from cfg/amar/tester/tests/mascotas/test_coverage.py rename to cfg/amar/station/tools/tester/tests/mascotas/test_coverage.py diff --git a/cfg/amar/tester/tests/mascotas/test_pet_owners.py b/cfg/amar/station/tools/tester/tests/mascotas/test_pet_owners.py similarity index 100% rename from cfg/amar/tester/tests/mascotas/test_pet_owners.py rename to cfg/amar/station/tools/tester/tests/mascotas/test_pet_owners.py diff --git a/cfg/amar/tester/tests/mascotas/test_pets.py b/cfg/amar/station/tools/tester/tests/mascotas/test_pets.py similarity index 100% rename from cfg/amar/tester/tests/mascotas/test_pets.py rename to cfg/amar/station/tools/tester/tests/mascotas/test_pets.py diff --git a/cfg/amar/tester/tests/productos/__init__.py b/cfg/amar/station/tools/tester/tests/productos/__init__.py similarity index 100% rename from cfg/amar/tester/tests/productos/__init__.py rename to cfg/amar/station/tools/tester/tests/productos/__init__.py diff --git a/cfg/amar/tester/tests/productos/test_cart.py b/cfg/amar/station/tools/tester/tests/productos/test_cart.py similarity index 100% rename from cfg/amar/tester/tests/productos/test_cart.py rename to cfg/amar/station/tools/tester/tests/productos/test_cart.py diff --git a/cfg/amar/tester/tests/productos/test_categories.py b/cfg/amar/station/tools/tester/tests/productos/test_categories.py similarity index 100% rename from cfg/amar/tester/tests/productos/test_categories.py rename to cfg/amar/station/tools/tester/tests/productos/test_categories.py diff --git a/cfg/amar/tester/tests/productos/test_services.py b/cfg/amar/station/tools/tester/tests/productos/test_services.py similarity index 100% rename from cfg/amar/tester/tests/productos/test_services.py rename to cfg/amar/station/tools/tester/tests/productos/test_services.py diff --git a/cfg/amar/tester/tests/solicitudes/__init__.py b/cfg/amar/station/tools/tester/tests/solicitudes/__init__.py similarity index 100% rename from cfg/amar/tester/tests/solicitudes/__init__.py rename to cfg/amar/station/tools/tester/tests/solicitudes/__init__.py diff --git a/cfg/amar/tester/tests/solicitudes/test_service_requests.py b/cfg/amar/station/tools/tester/tests/solicitudes/test_service_requests.py similarity index 100% rename from cfg/amar/tester/tests/solicitudes/test_service_requests.py rename to cfg/amar/station/tools/tester/tests/solicitudes/test_service_requests.py diff --git a/cfg/amar/tester/tests/workflows/__init__.py b/cfg/amar/station/tools/tester/tests/workflows/__init__.py similarity index 100% rename from cfg/amar/tester/tests/workflows/__init__.py rename to cfg/amar/station/tools/tester/tests/workflows/__init__.py diff --git a/cfg/amar/tester/tests/workflows/test_turnero_general.py b/cfg/amar/station/tools/tester/tests/workflows/test_turnero_general.py similarity index 100% rename from cfg/amar/tester/tests/workflows/test_turnero_general.py rename to cfg/amar/station/tools/tester/tests/workflows/test_turnero_general.py diff --git a/artery/__init__.py b/soleprint/artery/__init__.py similarity index 100% rename from artery/__init__.py rename to soleprint/artery/__init__.py diff --git a/artery/index.html b/soleprint/artery/index.html similarity index 100% rename from artery/index.html rename to soleprint/artery/index.html diff --git a/artery/room/README.md b/soleprint/artery/room/README.md similarity index 100% rename from artery/room/README.md rename to soleprint/artery/room/README.md diff --git a/artery/room/__init__.py b/soleprint/artery/room/__init__.py similarity index 100% rename from artery/room/__init__.py rename to soleprint/artery/room/__init__.py diff --git a/artery/room/ctrl/build.sh b/soleprint/artery/room/ctrl/build.sh similarity index 100% rename from artery/room/ctrl/build.sh rename to soleprint/artery/room/ctrl/build.sh diff --git a/artery/room/ctrl/logs.sh b/soleprint/artery/room/ctrl/logs.sh similarity index 100% rename from artery/room/ctrl/logs.sh rename to soleprint/artery/room/ctrl/logs.sh diff --git a/artery/room/ctrl/start.sh b/soleprint/artery/room/ctrl/start.sh similarity index 100% rename from artery/room/ctrl/start.sh rename to soleprint/artery/room/ctrl/start.sh diff --git a/artery/room/ctrl/status.sh b/soleprint/artery/room/ctrl/status.sh similarity index 100% rename from artery/room/ctrl/status.sh rename to soleprint/artery/room/ctrl/status.sh diff --git a/artery/room/ctrl/stop.sh b/soleprint/artery/room/ctrl/stop.sh similarity index 100% rename from artery/room/ctrl/stop.sh rename to soleprint/artery/room/ctrl/stop.sh diff --git a/artery/shunts/__init__.py b/soleprint/artery/shunts/__init__.py similarity index 100% rename from artery/shunts/__init__.py rename to soleprint/artery/shunts/__init__.py diff --git a/artery/shunts/example/README.md b/soleprint/artery/shunts/example/README.md similarity index 100% rename from artery/shunts/example/README.md rename to soleprint/artery/shunts/example/README.md diff --git a/artery/shunts/example/depot/responses.json b/soleprint/artery/shunts/example/depot/responses.json similarity index 100% rename from artery/shunts/example/depot/responses.json rename to soleprint/artery/shunts/example/depot/responses.json diff --git a/artery/shunts/example/main.py b/soleprint/artery/shunts/example/main.py similarity index 100% rename from artery/shunts/example/main.py rename to soleprint/artery/shunts/example/main.py diff --git a/artery/shunts/mercadopago/.env.example b/soleprint/artery/shunts/mercadopago/.env.example similarity index 100% rename from artery/shunts/mercadopago/.env.example rename to soleprint/artery/shunts/mercadopago/.env.example diff --git a/artery/shunts/mercadopago/README.md b/soleprint/artery/shunts/mercadopago/README.md similarity index 100% rename from artery/shunts/mercadopago/README.md rename to soleprint/artery/shunts/mercadopago/README.md diff --git a/artery/shunts/mercadopago/__init__.py b/soleprint/artery/shunts/mercadopago/__init__.py similarity index 100% rename from artery/shunts/mercadopago/__init__.py rename to soleprint/artery/shunts/mercadopago/__init__.py diff --git a/artery/shunts/mercadopago/api/__init__.py b/soleprint/artery/shunts/mercadopago/api/__init__.py similarity index 100% rename from artery/shunts/mercadopago/api/__init__.py rename to soleprint/artery/shunts/mercadopago/api/__init__.py diff --git a/artery/shunts/mercadopago/api/routes.py b/soleprint/artery/shunts/mercadopago/api/routes.py similarity index 100% rename from artery/shunts/mercadopago/api/routes.py rename to soleprint/artery/shunts/mercadopago/api/routes.py diff --git a/artery/shunts/mercadopago/core/__init__.py b/soleprint/artery/shunts/mercadopago/core/__init__.py similarity index 100% rename from artery/shunts/mercadopago/core/__init__.py rename to soleprint/artery/shunts/mercadopago/core/__init__.py diff --git a/artery/shunts/mercadopago/core/config.py b/soleprint/artery/shunts/mercadopago/core/config.py similarity index 100% rename from artery/shunts/mercadopago/core/config.py rename to soleprint/artery/shunts/mercadopago/core/config.py diff --git a/artery/shunts/mercadopago/main.py b/soleprint/artery/shunts/mercadopago/main.py similarity index 100% rename from artery/shunts/mercadopago/main.py rename to soleprint/artery/shunts/mercadopago/main.py diff --git a/artery/shunts/mercadopago/requirements.txt b/soleprint/artery/shunts/mercadopago/requirements.txt similarity index 100% rename from artery/shunts/mercadopago/requirements.txt rename to soleprint/artery/shunts/mercadopago/requirements.txt diff --git a/artery/shunts/mercadopago/run.py b/soleprint/artery/shunts/mercadopago/run.py similarity index 100% rename from artery/shunts/mercadopago/run.py rename to soleprint/artery/shunts/mercadopago/run.py diff --git a/artery/shunts/mercadopago/templates/index.html b/soleprint/artery/shunts/mercadopago/templates/index.html similarity index 100% rename from artery/shunts/mercadopago/templates/index.html rename to soleprint/artery/shunts/mercadopago/templates/index.html diff --git a/artery/veins/PATTERNS.md b/soleprint/artery/veins/PATTERNS.md similarity index 100% rename from artery/veins/PATTERNS.md rename to soleprint/artery/veins/PATTERNS.md diff --git a/artery/veins/__init__.py b/soleprint/artery/veins/__init__.py similarity index 100% rename from artery/veins/__init__.py rename to soleprint/artery/veins/__init__.py diff --git a/artery/veins/base.py b/soleprint/artery/veins/base.py similarity index 100% rename from artery/veins/base.py rename to soleprint/artery/veins/base.py diff --git a/artery/veins/google/.env.example b/soleprint/artery/veins/google/.env.example similarity index 100% rename from artery/veins/google/.env.example rename to soleprint/artery/veins/google/.env.example diff --git a/artery/veins/google/README.md b/soleprint/artery/veins/google/README.md similarity index 100% rename from artery/veins/google/README.md rename to soleprint/artery/veins/google/README.md diff --git a/artery/veins/google/__init__.py b/soleprint/artery/veins/google/__init__.py similarity index 100% rename from artery/veins/google/__init__.py rename to soleprint/artery/veins/google/__init__.py diff --git a/artery/veins/google/api/__init__.py b/soleprint/artery/veins/google/api/__init__.py similarity index 100% rename from artery/veins/google/api/__init__.py rename to soleprint/artery/veins/google/api/__init__.py diff --git a/artery/veins/google/api/routes.py b/soleprint/artery/veins/google/api/routes.py similarity index 100% rename from artery/veins/google/api/routes.py rename to soleprint/artery/veins/google/api/routes.py diff --git a/artery/veins/google/core/__init__.py b/soleprint/artery/veins/google/core/__init__.py similarity index 100% rename from artery/veins/google/core/__init__.py rename to soleprint/artery/veins/google/core/__init__.py diff --git a/artery/veins/google/core/config.py b/soleprint/artery/veins/google/core/config.py similarity index 100% rename from artery/veins/google/core/config.py rename to soleprint/artery/veins/google/core/config.py diff --git a/artery/veins/google/core/oauth.py b/soleprint/artery/veins/google/core/oauth.py similarity index 100% rename from artery/veins/google/core/oauth.py rename to soleprint/artery/veins/google/core/oauth.py diff --git a/artery/veins/google/core/sheets.py b/soleprint/artery/veins/google/core/sheets.py similarity index 100% rename from artery/veins/google/core/sheets.py rename to soleprint/artery/veins/google/core/sheets.py diff --git a/artery/veins/google/main.py b/soleprint/artery/veins/google/main.py similarity index 100% rename from artery/veins/google/main.py rename to soleprint/artery/veins/google/main.py diff --git a/artery/veins/google/models/__init__.py b/soleprint/artery/veins/google/models/__init__.py similarity index 100% rename from artery/veins/google/models/__init__.py rename to soleprint/artery/veins/google/models/__init__.py diff --git a/artery/veins/google/models/formatter.py b/soleprint/artery/veins/google/models/formatter.py similarity index 100% rename from artery/veins/google/models/formatter.py rename to soleprint/artery/veins/google/models/formatter.py diff --git a/artery/veins/google/models/spreadsheet.py b/soleprint/artery/veins/google/models/spreadsheet.py similarity index 100% rename from artery/veins/google/models/spreadsheet.py rename to soleprint/artery/veins/google/models/spreadsheet.py diff --git a/artery/veins/google/requirements.txt b/soleprint/artery/veins/google/requirements.txt similarity index 100% rename from artery/veins/google/requirements.txt rename to soleprint/artery/veins/google/requirements.txt diff --git a/artery/veins/google/run.py b/soleprint/artery/veins/google/run.py similarity index 100% rename from artery/veins/google/run.py rename to soleprint/artery/veins/google/run.py diff --git a/artery/veins/google/storage/.gitignore b/soleprint/artery/veins/google/storage/.gitignore similarity index 100% rename from artery/veins/google/storage/.gitignore rename to soleprint/artery/veins/google/storage/.gitignore diff --git a/artery/veins/jira/.env.example b/soleprint/artery/veins/jira/.env.example similarity index 100% rename from artery/veins/jira/.env.example rename to soleprint/artery/veins/jira/.env.example diff --git a/artery/veins/jira/README.md b/soleprint/artery/veins/jira/README.md similarity index 100% rename from artery/veins/jira/README.md rename to soleprint/artery/veins/jira/README.md diff --git a/artery/veins/jira/__init__.py b/soleprint/artery/veins/jira/__init__.py similarity index 100% rename from artery/veins/jira/__init__.py rename to soleprint/artery/veins/jira/__init__.py diff --git a/artery/veins/jira/api/__init__.py b/soleprint/artery/veins/jira/api/__init__.py similarity index 100% rename from artery/veins/jira/api/__init__.py rename to soleprint/artery/veins/jira/api/__init__.py diff --git a/artery/veins/jira/api/routes.py b/soleprint/artery/veins/jira/api/routes.py similarity index 100% rename from artery/veins/jira/api/routes.py rename to soleprint/artery/veins/jira/api/routes.py diff --git a/artery/veins/jira/core/__init__.py b/soleprint/artery/veins/jira/core/__init__.py similarity index 100% rename from artery/veins/jira/core/__init__.py rename to soleprint/artery/veins/jira/core/__init__.py diff --git a/artery/veins/jira/core/auth.py b/soleprint/artery/veins/jira/core/auth.py similarity index 100% rename from artery/veins/jira/core/auth.py rename to soleprint/artery/veins/jira/core/auth.py diff --git a/artery/veins/jira/core/client.py b/soleprint/artery/veins/jira/core/client.py similarity index 100% rename from artery/veins/jira/core/client.py rename to soleprint/artery/veins/jira/core/client.py diff --git a/artery/veins/jira/core/config.py b/soleprint/artery/veins/jira/core/config.py similarity index 100% rename from artery/veins/jira/core/config.py rename to soleprint/artery/veins/jira/core/config.py diff --git a/artery/veins/jira/core/query.py b/soleprint/artery/veins/jira/core/query.py similarity index 100% rename from artery/veins/jira/core/query.py rename to soleprint/artery/veins/jira/core/query.py diff --git a/artery/veins/jira/main.py b/soleprint/artery/veins/jira/main.py similarity index 100% rename from artery/veins/jira/main.py rename to soleprint/artery/veins/jira/main.py diff --git a/artery/veins/jira/models/__init__.py b/soleprint/artery/veins/jira/models/__init__.py similarity index 100% rename from artery/veins/jira/models/__init__.py rename to soleprint/artery/veins/jira/models/__init__.py diff --git a/artery/veins/jira/models/formatter.py b/soleprint/artery/veins/jira/models/formatter.py similarity index 100% rename from artery/veins/jira/models/formatter.py rename to soleprint/artery/veins/jira/models/formatter.py diff --git a/artery/veins/jira/models/ticket.py b/soleprint/artery/veins/jira/models/ticket.py similarity index 100% rename from artery/veins/jira/models/ticket.py rename to soleprint/artery/veins/jira/models/ticket.py diff --git a/artery/veins/jira/requirements.txt b/soleprint/artery/veins/jira/requirements.txt similarity index 100% rename from artery/veins/jira/requirements.txt rename to soleprint/artery/veins/jira/requirements.txt diff --git a/artery/veins/jira/run.py b/soleprint/artery/veins/jira/run.py similarity index 100% rename from artery/veins/jira/run.py rename to soleprint/artery/veins/jira/run.py diff --git a/artery/veins/oauth.py b/soleprint/artery/veins/oauth.py similarity index 100% rename from artery/veins/oauth.py rename to soleprint/artery/veins/oauth.py diff --git a/artery/veins/slack/__init__.py b/soleprint/artery/veins/slack/__init__.py similarity index 100% rename from artery/veins/slack/__init__.py rename to soleprint/artery/veins/slack/__init__.py diff --git a/artery/veins/slack/api/__init__.py b/soleprint/artery/veins/slack/api/__init__.py similarity index 100% rename from artery/veins/slack/api/__init__.py rename to soleprint/artery/veins/slack/api/__init__.py diff --git a/artery/veins/slack/api/routes.py b/soleprint/artery/veins/slack/api/routes.py similarity index 100% rename from artery/veins/slack/api/routes.py rename to soleprint/artery/veins/slack/api/routes.py diff --git a/artery/veins/slack/core/__init__.py b/soleprint/artery/veins/slack/core/__init__.py similarity index 100% rename from artery/veins/slack/core/__init__.py rename to soleprint/artery/veins/slack/core/__init__.py diff --git a/artery/veins/slack/core/auth.py b/soleprint/artery/veins/slack/core/auth.py similarity index 100% rename from artery/veins/slack/core/auth.py rename to soleprint/artery/veins/slack/core/auth.py diff --git a/artery/veins/slack/core/client.py b/soleprint/artery/veins/slack/core/client.py similarity index 100% rename from artery/veins/slack/core/client.py rename to soleprint/artery/veins/slack/core/client.py diff --git a/artery/veins/slack/core/config.py b/soleprint/artery/veins/slack/core/config.py similarity index 100% rename from artery/veins/slack/core/config.py rename to soleprint/artery/veins/slack/core/config.py diff --git a/artery/veins/slack/main.py b/soleprint/artery/veins/slack/main.py similarity index 100% rename from artery/veins/slack/main.py rename to soleprint/artery/veins/slack/main.py diff --git a/artery/veins/slack/models/__init__.py b/soleprint/artery/veins/slack/models/__init__.py similarity index 100% rename from artery/veins/slack/models/__init__.py rename to soleprint/artery/veins/slack/models/__init__.py diff --git a/artery/veins/slack/models/formatter.py b/soleprint/artery/veins/slack/models/formatter.py similarity index 100% rename from artery/veins/slack/models/formatter.py rename to soleprint/artery/veins/slack/models/formatter.py diff --git a/artery/veins/slack/models/message.py b/soleprint/artery/veins/slack/models/message.py similarity index 100% rename from artery/veins/slack/models/message.py rename to soleprint/artery/veins/slack/models/message.py diff --git a/artery/veins/slack/requirements.txt b/soleprint/artery/veins/slack/requirements.txt similarity index 100% rename from artery/veins/slack/requirements.txt rename to soleprint/artery/veins/slack/requirements.txt diff --git a/artery/veins/slack/run.py b/soleprint/artery/veins/slack/run.py similarity index 100% rename from artery/veins/slack/run.py rename to soleprint/artery/veins/slack/run.py diff --git a/atlas/.dockerignore b/soleprint/atlas/.dockerignore similarity index 100% rename from atlas/.dockerignore rename to soleprint/atlas/.dockerignore diff --git a/atlas/.gitignore b/soleprint/atlas/.gitignore similarity index 100% rename from atlas/.gitignore rename to soleprint/atlas/.gitignore diff --git a/atlas/CLAUDE.md b/soleprint/atlas/CLAUDE.md similarity index 100% rename from atlas/CLAUDE.md rename to soleprint/atlas/CLAUDE.md diff --git a/atlas/__init__.py b/soleprint/atlas/__init__.py similarity index 100% rename from atlas/__init__.py rename to soleprint/atlas/__init__.py diff --git a/atlas/book-template.html b/soleprint/atlas/book-template.html similarity index 100% rename from atlas/book-template.html rename to soleprint/atlas/book-template.html diff --git a/atlas/book/feature-flow/CLAUDE.md b/soleprint/atlas/books/feature-flow/CLAUDE.md similarity index 100% rename from atlas/book/feature-flow/CLAUDE.md rename to soleprint/atlas/books/feature-flow/CLAUDE.md diff --git a/atlas/book/feature-flow/index-en.html b/soleprint/atlas/books/feature-flow/index-en.html similarity index 100% rename from atlas/book/feature-flow/index-en.html rename to soleprint/atlas/books/feature-flow/index-en.html diff --git a/atlas/book/feature-flow/index-es.html b/soleprint/atlas/books/feature-flow/index-es.html similarity index 100% rename from atlas/book/feature-flow/index-es.html rename to soleprint/atlas/books/feature-flow/index-es.html diff --git a/atlas/index.html b/soleprint/atlas/index.html similarity index 100% rename from atlas/index.html rename to soleprint/atlas/index.html diff --git a/atlas/main.py b/soleprint/atlas/main.py similarity index 100% rename from atlas/main.py rename to soleprint/atlas/main.py diff --git a/atlas/requirements.txt b/soleprint/atlas/requirements.txt similarity index 100% rename from atlas/requirements.txt rename to soleprint/atlas/requirements.txt diff --git a/atlas/books/sysmonstm/static/prism/prism-gherkin.min.js b/soleprint/atlas/static/prism/prism-gherkin.min.js similarity index 100% rename from atlas/books/sysmonstm/static/prism/prism-gherkin.min.js rename to soleprint/atlas/static/prism/prism-gherkin.min.js diff --git a/atlas/books/sysmonstm/static/prism/prism-line-numbers.min.css b/soleprint/atlas/static/prism/prism-line-numbers.min.css similarity index 100% rename from atlas/books/sysmonstm/static/prism/prism-line-numbers.min.css rename to soleprint/atlas/static/prism/prism-line-numbers.min.css diff --git a/atlas/books/sysmonstm/static/prism/prism-line-numbers.min.js b/soleprint/atlas/static/prism/prism-line-numbers.min.js similarity index 100% rename from atlas/books/sysmonstm/static/prism/prism-line-numbers.min.js rename to soleprint/atlas/static/prism/prism-line-numbers.min.js diff --git a/atlas/books/sysmonstm/static/prism/prism-tomorrow.min.css b/soleprint/atlas/static/prism/prism-tomorrow.min.css similarity index 100% rename from atlas/books/sysmonstm/static/prism/prism-tomorrow.min.css rename to soleprint/atlas/static/prism/prism-tomorrow.min.css diff --git a/atlas/books/sysmonstm/static/prism/prism.min.js b/soleprint/atlas/static/prism/prism.min.js similarity index 100% rename from atlas/books/sysmonstm/static/prism/prism.min.js rename to soleprint/atlas/static/prism/prism.min.js diff --git a/soleprint/generate.html b/soleprint/generate.html new file mode 100644 index 0000000..f4d7d0f --- /dev/null +++ b/soleprint/generate.html @@ -0,0 +1,512 @@ + + + + + + Soleprint - Generate Configuration + + + +
+
+

Soleprint Configuration Generator

+

+ Generate a new room configuration for wrapping a managed + application +

+
+ +
+
+

Configuration

+
+
Room Settings
+
+ + +
+ Unique identifier for this configuration +
+
+ +
Framework Branding
+
+
+ + +
+
+ + +
+
+ +
System Names
+
+
+ + +
+
+ + +
+
+
+ + +
+ +
+ Managed Application (Optional) +
+
+ + +
+ + +
+ + +
+
+
+
+ +
+

Preview

+
+ # Enter configuration details and click "Preview + Structure" + + gen/<room>/ + <managed>/ + # if managed app configured + link/ + <soleprint>/ +
+
+
+
+ + + + diff --git a/soleprint/main.py b/soleprint/main.py index f8b0245..d73de5f 100644 --- a/soleprint/main.py +++ b/soleprint/main.py @@ -20,23 +20,65 @@ Routes: /station/* → proxy to station service """ +import json import os from pathlib import Path +from typing import Optional -# Import data functions from dataloader import get_artery_data, get_atlas_data, get_station_data from fastapi import FastAPI, Request -from fastapi.responses import RedirectResponse +from fastapi.responses import FileResponse, RedirectResponse from fastapi.templating import Jinja2Templates +from pydantic import BaseModel app = FastAPI(title="Soleprint", version="0.1.0") templates = Jinja2Templates(directory=Path(__file__).parent) + +# === Generation Models === + + +class FrameworkConfig(BaseModel): + name: str = "soleprint" + icon: Optional[str] = None + + +class SystemsConfig(BaseModel): + artery: str = "artery" + atlas: str = "atlas" + station: str = "station" + + +class ManagedConfig(BaseModel): + name: str + repos: dict[str, str] + + +class GenerationRequest(BaseModel): + room_name: str + framework: FrameworkConfig = FrameworkConfig() + systems: SystemsConfig = SystemsConfig() + managed: Optional[ManagedConfig] = None + + +# Load config if available +CONFIG_PATH = Path(__file__).parent / "cfg" / "config.json" +CONFIG = {} +if CONFIG_PATH.exists(): + CONFIG = json.loads(CONFIG_PATH.read_text()) + +# Get ports from config or use defaults +HUB_PORT = CONFIG.get("framework", {}).get("hub_port", 12000) +SYSTEM_PORTS = {s["key"]: s["port"] for s in CONFIG.get("systems", [])} +ARTERY_PORT = SYSTEM_PORTS.get("data_flow", 12001) +ATLAS_PORT = SYSTEM_PORTS.get("documentation", 12002) +STATION_PORT = SYSTEM_PORTS.get("execution", 12003) + # Service URLs (internal for API calls) -ARTERY_URL = os.getenv("ARTERY_URL", "http://localhost:12001") -ATLAS_URL = os.getenv("ATLAS_URL", "http://localhost:12002") -STATION_URL = os.getenv("STATION_URL", "http://localhost:12003") +ARTERY_URL = os.getenv("ARTERY_URL", f"http://localhost:{ARTERY_PORT}") +ATLAS_URL = os.getenv("ATLAS_URL", f"http://localhost:{ATLAS_PORT}") +STATION_URL = os.getenv("STATION_URL", f"http://localhost:{STATION_PORT}") # External URLs (for frontend links, falls back to internal) ARTERY_EXTERNAL_URL = os.getenv("ARTERY_EXTERNAL_URL", ARTERY_URL) @@ -78,6 +120,137 @@ def api_station_data(): return get_station_data() +# === Generation API === + + +@app.get("/generate") +def generation_ui(): + """Serve the generation UI.""" + return FileResponse(Path(__file__).parent / "generate.html") + + +@app.post("/api/generate") +def generate_config(req: GenerationRequest): + """Generate a config.json for a new room.""" + config = { + "framework": { + "name": req.framework.name, + "slug": req.framework.name.lower().replace(" ", "-"), + "version": "0.1.0", + "description": "Development workflow and documentation system", + "tagline": "Mapping development footprints", + "icon": req.framework.icon or "", + "hub_port": HUB_PORT, + }, + "systems": [ + { + "key": "data_flow", + "name": req.systems.artery, + "slug": req.systems.artery.lower(), + "title": req.systems.artery.title(), + "tagline": "Todo lo vital", + "port": ARTERY_PORT, + "icon": "", + }, + { + "key": "documentation", + "name": req.systems.atlas, + "slug": req.systems.atlas.lower(), + "title": req.systems.atlas.title(), + "tagline": "Documentacion accionable", + "port": ATLAS_PORT, + "icon": "", + }, + { + "key": "execution", + "name": req.systems.station, + "slug": req.systems.station.lower(), + "title": req.systems.station.title(), + "tagline": "Monitores, Entornos y Herramientas", + "port": STATION_PORT, + "icon": "", + }, + ], + "components": { + "shared": { + "config": { + "name": "room", + "title": "Room", + "description": "Runtime environment configuration", + "plural": "rooms", + }, + "data": { + "name": "depot", + "title": "Depot", + "description": "Data storage / provisions", + "plural": "depots", + }, + }, + "data_flow": { + "connector": { + "name": "vein", + "title": "Vein", + "description": "Stateless API connector", + "plural": "veins", + }, + "mock": { + "name": "shunt", + "title": "Shunt", + "description": "Fake connector for testing", + "plural": "shunts", + }, + }, + "documentation": { + "library": { + "name": "book", + "title": "Book", + "description": "Documentation library", + }, + }, + "execution": { + "utility": { + "name": "tool", + "title": "Tool", + "description": "Execution utility", + "plural": "tools", + }, + "watcher": { + "name": "monitor", + "title": "Monitor", + "description": "Service monitor", + "plural": "monitors", + }, + }, + }, + } + + if req.managed: + config["managed"] = { + "name": req.managed.name, + "repos": req.managed.repos, + } + + return {"config": config, "room_name": req.room_name} + + +@app.post("/api/generate/preview") +def generate_preview(req: GenerationRequest): + """Preview the generated folder structure.""" + room = req.room_name or "room" + fw = req.framework.name or "soleprint" + sys = req.systems + + lines = [f'gen/{room}/'] + + if req.managed and req.managed.name: + lines.append(f' {req.managed.name}/') + lines.append(' link/') + + lines.append(f' {fw}/') + + return {"tree": "\n".join(lines)} + + @app.get("/") def index(request: Request): return templates.TemplateResponse( diff --git a/station/__init__.py b/soleprint/station/__init__.py similarity index 100% rename from station/__init__.py rename to soleprint/station/__init__.py diff --git a/station/index.html b/soleprint/station/index.html similarity index 100% rename from station/index.html rename to soleprint/station/index.html diff --git a/station/monitors/__init__.py b/soleprint/station/monitors/__init__.py similarity index 100% rename from station/monitors/__init__.py rename to soleprint/station/monitors/__init__.py diff --git a/station/monitors/databrowse/README.md b/soleprint/station/monitors/databrowse/README.md similarity index 100% rename from station/monitors/databrowse/README.md rename to soleprint/station/monitors/databrowse/README.md diff --git a/station/monitors/databrowse/depot/.depot b/soleprint/station/monitors/databrowse/depot/.depot similarity index 100% rename from station/monitors/databrowse/depot/.depot rename to soleprint/station/monitors/databrowse/depot/.depot diff --git a/station/monitors/databrowse/depot/scenarios.json b/soleprint/station/monitors/databrowse/depot/scenarios.json similarity index 100% rename from station/monitors/databrowse/depot/scenarios.json rename to soleprint/station/monitors/databrowse/depot/scenarios.json diff --git a/station/monitors/databrowse/depot/schema.json b/soleprint/station/monitors/databrowse/depot/schema.json similarity index 100% rename from station/monitors/databrowse/depot/schema.json rename to soleprint/station/monitors/databrowse/depot/schema.json diff --git a/station/monitors/databrowse/depot/views.json b/soleprint/station/monitors/databrowse/depot/views.json similarity index 100% rename from station/monitors/databrowse/depot/views.json rename to soleprint/station/monitors/databrowse/depot/views.json diff --git a/station/monitors/databrowse/index.html b/soleprint/station/monitors/databrowse/index.html similarity index 100% rename from station/monitors/databrowse/index.html rename to soleprint/station/monitors/databrowse/index.html diff --git a/station/monitors/databrowse/main.py b/soleprint/station/monitors/databrowse/main.py similarity index 100% rename from station/monitors/databrowse/main.py rename to soleprint/station/monitors/databrowse/main.py diff --git a/station/monitors/databrowse/view.html b/soleprint/station/monitors/databrowse/view.html similarity index 100% rename from station/monitors/databrowse/view.html rename to soleprint/station/monitors/databrowse/view.html diff --git a/station/tools/__init__.py b/soleprint/station/tools/__init__.py similarity index 100% rename from station/tools/__init__.py rename to soleprint/station/tools/__init__.py diff --git a/station/tools/datagen/README.md b/soleprint/station/tools/datagen/README.md similarity index 100% rename from station/tools/datagen/README.md rename to soleprint/station/tools/datagen/README.md diff --git a/station/tools/datagen/__init__.py b/soleprint/station/tools/datagen/__init__.py similarity index 100% rename from station/tools/datagen/__init__.py rename to soleprint/station/tools/datagen/__init__.py diff --git a/station/tools/hub/README.md b/soleprint/station/tools/hub/README.md similarity index 100% rename from station/tools/hub/README.md rename to soleprint/station/tools/hub/README.md diff --git a/station/tools/hub/firewalld.sh b/soleprint/station/tools/hub/firewalld.sh similarity index 100% rename from station/tools/hub/firewalld.sh rename to soleprint/station/tools/hub/firewalld.sh diff --git a/station/tools/hub/iptables.sh b/soleprint/station/tools/hub/iptables.sh similarity index 100% rename from station/tools/hub/iptables.sh rename to soleprint/station/tools/hub/iptables.sh diff --git a/station/tools/hub/ports b/soleprint/station/tools/hub/ports similarity index 100% rename from station/tools/hub/ports rename to soleprint/station/tools/hub/ports diff --git a/station/tools/hub/ufw.sh b/soleprint/station/tools/hub/ufw.sh similarity index 100% rename from station/tools/hub/ufw.sh rename to soleprint/station/tools/hub/ufw.sh diff --git a/station/tools/hub/update-ports.sh b/soleprint/station/tools/hub/update-ports.sh similarity index 100% rename from station/tools/hub/update-ports.sh rename to soleprint/station/tools/hub/update-ports.sh diff --git a/station/tools/infra/README.md b/soleprint/station/tools/infra/README.md similarity index 100% rename from station/tools/infra/README.md rename to soleprint/station/tools/infra/README.md diff --git a/station/tools/infra/aws/Pulumi.yaml b/soleprint/station/tools/infra/aws/Pulumi.yaml similarity index 100% rename from station/tools/infra/aws/Pulumi.yaml rename to soleprint/station/tools/infra/aws/Pulumi.yaml diff --git a/station/tools/infra/aws/__main__.py b/soleprint/station/tools/infra/aws/__main__.py similarity index 100% rename from station/tools/infra/aws/__main__.py rename to soleprint/station/tools/infra/aws/__main__.py diff --git a/station/tools/infra/aws/requirements.txt b/soleprint/station/tools/infra/aws/requirements.txt similarity index 100% rename from station/tools/infra/aws/requirements.txt rename to soleprint/station/tools/infra/aws/requirements.txt diff --git a/station/tools/infra/digitalocean/Pulumi.yaml b/soleprint/station/tools/infra/digitalocean/Pulumi.yaml similarity index 100% rename from station/tools/infra/digitalocean/Pulumi.yaml rename to soleprint/station/tools/infra/digitalocean/Pulumi.yaml diff --git a/station/tools/infra/digitalocean/__main__.py b/soleprint/station/tools/infra/digitalocean/__main__.py similarity index 100% rename from station/tools/infra/digitalocean/__main__.py rename to soleprint/station/tools/infra/digitalocean/__main__.py diff --git a/station/tools/infra/digitalocean/requirements.txt b/soleprint/station/tools/infra/digitalocean/requirements.txt similarity index 100% rename from station/tools/infra/digitalocean/requirements.txt rename to soleprint/station/tools/infra/digitalocean/requirements.txt diff --git a/station/tools/infra/gcp/Pulumi.yaml b/soleprint/station/tools/infra/gcp/Pulumi.yaml similarity index 100% rename from station/tools/infra/gcp/Pulumi.yaml rename to soleprint/station/tools/infra/gcp/Pulumi.yaml diff --git a/station/tools/infra/gcp/__main__.py b/soleprint/station/tools/infra/gcp/__main__.py similarity index 100% rename from station/tools/infra/gcp/__main__.py rename to soleprint/station/tools/infra/gcp/__main__.py diff --git a/station/tools/infra/gcp/requirements.txt b/soleprint/station/tools/infra/gcp/requirements.txt similarity index 100% rename from station/tools/infra/gcp/requirements.txt rename to soleprint/station/tools/infra/gcp/requirements.txt diff --git a/station/tools/infra/shared/__init__.py b/soleprint/station/tools/infra/shared/__init__.py similarity index 100% rename from station/tools/infra/shared/__init__.py rename to soleprint/station/tools/infra/shared/__init__.py diff --git a/station/tools/infra/shared/config.py b/soleprint/station/tools/infra/shared/config.py similarity index 100% rename from station/tools/infra/shared/config.py rename to soleprint/station/tools/infra/shared/config.py diff --git a/station/tools/modelgen/__init__.py b/soleprint/station/tools/modelgen/__init__.py similarity index 100% rename from station/tools/modelgen/__init__.py rename to soleprint/station/tools/modelgen/__init__.py diff --git a/station/tools/modelgen/__main__.py b/soleprint/station/tools/modelgen/__main__.py similarity index 100% rename from station/tools/modelgen/__main__.py rename to soleprint/station/tools/modelgen/__main__.py diff --git a/station/tools/modelgen/config_loader.py b/soleprint/station/tools/modelgen/config_loader.py similarity index 100% rename from station/tools/modelgen/config_loader.py rename to soleprint/station/tools/modelgen/config_loader.py diff --git a/station/tools/modelgen/model_generator.py b/soleprint/station/tools/modelgen/model_generator.py similarity index 100% rename from station/tools/modelgen/model_generator.py rename to soleprint/station/tools/modelgen/model_generator.py diff --git a/station/tools/sbwrapper/README.md b/soleprint/station/tools/sbwrapper/README.md similarity index 100% rename from station/tools/sbwrapper/README.md rename to soleprint/station/tools/sbwrapper/README.md diff --git a/station/tools/sbwrapper/config.json b/soleprint/station/tools/sbwrapper/config.json similarity index 100% rename from station/tools/sbwrapper/config.json rename to soleprint/station/tools/sbwrapper/config.json diff --git a/station/tools/sbwrapper/index.html b/soleprint/station/tools/sbwrapper/index.html similarity index 100% rename from station/tools/sbwrapper/index.html rename to soleprint/station/tools/sbwrapper/index.html diff --git a/station/tools/sbwrapper/sidebar.css b/soleprint/station/tools/sbwrapper/sidebar.css similarity index 100% rename from station/tools/sbwrapper/sidebar.css rename to soleprint/station/tools/sbwrapper/sidebar.css diff --git a/station/tools/sbwrapper/sidebar.js b/soleprint/station/tools/sbwrapper/sidebar.js similarity index 100% rename from station/tools/sbwrapper/sidebar.js rename to soleprint/station/tools/sbwrapper/sidebar.js diff --git a/station/tools/tester/.env b/soleprint/station/tools/tester/.env similarity index 100% rename from station/tools/tester/.env rename to soleprint/station/tools/tester/.env diff --git a/station/tools/tester/ENHANCEMENT_DESIGN.md b/soleprint/station/tools/tester/ENHANCEMENT_DESIGN.md similarity index 100% rename from station/tools/tester/ENHANCEMENT_DESIGN.md rename to soleprint/station/tools/tester/ENHANCEMENT_DESIGN.md diff --git a/station/tools/tester/README.md b/soleprint/station/tools/tester/README.md similarity index 100% rename from station/tools/tester/README.md rename to soleprint/station/tools/tester/README.md diff --git a/station/tools/tester/SESSION_6_IMPLEMENTATION.md b/soleprint/station/tools/tester/SESSION_6_IMPLEMENTATION.md similarity index 100% rename from station/tools/tester/SESSION_6_IMPLEMENTATION.md rename to soleprint/station/tools/tester/SESSION_6_IMPLEMENTATION.md diff --git a/station/tools/tester/__init__.py b/soleprint/station/tools/tester/__init__.py similarity index 100% rename from station/tools/tester/__init__.py rename to soleprint/station/tools/tester/__init__.py diff --git a/station/tools/tester/__main__.py b/soleprint/station/tools/tester/__main__.py similarity index 100% rename from station/tools/tester/__main__.py rename to soleprint/station/tools/tester/__main__.py diff --git a/station/tools/tester/api.py b/soleprint/station/tools/tester/api.py similarity index 100% rename from station/tools/tester/api.py rename to soleprint/station/tools/tester/api.py diff --git a/station/tools/tester/artifacts/.gitignore b/soleprint/station/tools/tester/artifacts/.gitignore similarity index 100% rename from station/tools/tester/artifacts/.gitignore rename to soleprint/station/tools/tester/artifacts/.gitignore diff --git a/station/tools/tester/base.py b/soleprint/station/tools/tester/base.py similarity index 100% rename from station/tools/tester/base.py rename to soleprint/station/tools/tester/base.py diff --git a/station/tools/tester/cli.py b/soleprint/station/tools/tester/cli.py similarity index 100% rename from station/tools/tester/cli.py rename to soleprint/station/tools/tester/cli.py diff --git a/station/tools/tester/config.py b/soleprint/station/tools/tester/config.py similarity index 100% rename from station/tools/tester/config.py rename to soleprint/station/tools/tester/config.py diff --git a/station/tools/tester/core.py b/soleprint/station/tools/tester/core.py similarity index 100% rename from station/tools/tester/core.py rename to soleprint/station/tools/tester/core.py diff --git a/station/tools/tester/endpoints.py b/soleprint/station/tools/tester/endpoints.py similarity index 100% rename from station/tools/tester/endpoints.py rename to soleprint/station/tools/tester/endpoints.py diff --git a/station/tools/tester/environments.json b/soleprint/station/tools/tester/environments.json similarity index 100% rename from station/tools/tester/environments.json rename to soleprint/station/tools/tester/environments.json diff --git a/station/tools/tester/features/.gitignore b/soleprint/station/tools/tester/features/.gitignore similarity index 100% rename from station/tools/tester/features/.gitignore rename to soleprint/station/tools/tester/features/.gitignore diff --git a/station/tools/tester/get-api-key.sh b/soleprint/station/tools/tester/get-api-key.sh similarity index 100% rename from station/tools/tester/get-api-key.sh rename to soleprint/station/tools/tester/get-api-key.sh diff --git a/station/tools/tester/gherkin/__init__.py b/soleprint/station/tools/tester/gherkin/__init__.py similarity index 100% rename from station/tools/tester/gherkin/__init__.py rename to soleprint/station/tools/tester/gherkin/__init__.py diff --git a/station/tools/tester/gherkin/mapper.py b/soleprint/station/tools/tester/gherkin/mapper.py similarity index 100% rename from station/tools/tester/gherkin/mapper.py rename to soleprint/station/tools/tester/gherkin/mapper.py diff --git a/station/tools/tester/gherkin/parser.py b/soleprint/station/tools/tester/gherkin/parser.py similarity index 100% rename from station/tools/tester/gherkin/parser.py rename to soleprint/station/tools/tester/gherkin/parser.py diff --git a/station/tools/tester/gherkin/sync.py b/soleprint/station/tools/tester/gherkin/sync.py similarity index 100% rename from station/tools/tester/gherkin/sync.py rename to soleprint/station/tools/tester/gherkin/sync.py diff --git a/station/tools/tester/helpers.py b/soleprint/station/tools/tester/helpers.py similarity index 100% rename from station/tools/tester/helpers.py rename to soleprint/station/tools/tester/helpers.py diff --git a/station/tools/tester/index.py b/soleprint/station/tools/tester/index.py similarity index 100% rename from station/tools/tester/index.py rename to soleprint/station/tools/tester/index.py diff --git a/station/tools/tester/playwright/README.md b/soleprint/station/tools/tester/playwright/README.md similarity index 100% rename from station/tools/tester/playwright/README.md rename to soleprint/station/tools/tester/playwright/README.md diff --git a/station/tools/tester/playwright/__init__.py b/soleprint/station/tools/tester/playwright/__init__.py similarity index 100% rename from station/tools/tester/playwright/__init__.py rename to soleprint/station/tools/tester/playwright/__init__.py diff --git a/station/tools/tester/playwright/artifacts.py b/soleprint/station/tools/tester/playwright/artifacts.py similarity index 100% rename from station/tools/tester/playwright/artifacts.py rename to soleprint/station/tools/tester/playwright/artifacts.py diff --git a/station/tools/tester/playwright/discovery.py b/soleprint/station/tools/tester/playwright/discovery.py similarity index 100% rename from station/tools/tester/playwright/discovery.py rename to soleprint/station/tools/tester/playwright/discovery.py diff --git a/station/tools/tester/playwright/runner.py b/soleprint/station/tools/tester/playwright/runner.py similarity index 100% rename from station/tools/tester/playwright/runner.py rename to soleprint/station/tools/tester/playwright/runner.py diff --git a/station/tools/tester/templates/filters.html b/soleprint/station/tools/tester/templates/filters.html similarity index 100% rename from station/tools/tester/templates/filters.html rename to soleprint/station/tools/tester/templates/filters.html diff --git a/station/tools/tester/templates/filters_v2.html b/soleprint/station/tools/tester/templates/filters_v2.html similarity index 100% rename from station/tools/tester/templates/filters_v2.html rename to soleprint/station/tools/tester/templates/filters_v2.html diff --git a/station/tools/tester/templates/index.html b/soleprint/station/tools/tester/templates/index.html similarity index 100% rename from station/tools/tester/templates/index.html rename to soleprint/station/tools/tester/templates/index.html diff --git a/station/tools/tester/tests/README.md b/soleprint/station/tools/tester/tests/README.md similarity index 100% rename from station/tools/tester/tests/README.md rename to soleprint/station/tools/tester/tests/README.md diff --git a/station/tools/tester/tests/__init__.py b/soleprint/station/tools/tester/tests/__init__.py similarity index 100% rename from station/tools/tester/tests/__init__.py rename to soleprint/station/tools/tester/tests/__init__.py diff --git a/station/tools/tester/tests/_dev/__init__.py b/soleprint/station/tools/tester/tests/_dev/__init__.py similarity index 100% rename from station/tools/tester/tests/_dev/__init__.py rename to soleprint/station/tools/tester/tests/_dev/__init__.py diff --git a/station/tools/tester/tests/_dev/test_health.py b/soleprint/station/tools/tester/tests/_dev/test_health.py similarity index 100% rename from station/tools/tester/tests/_dev/test_health.py rename to soleprint/station/tools/tester/tests/_dev/test_health.py diff --git a/station/tools/tester/tests/base.py b/soleprint/station/tools/tester/tests/base.py similarity index 100% rename from station/tools/tester/tests/base.py rename to soleprint/station/tools/tester/tests/base.py diff --git a/station/tools/tester/tests/conftest.py b/soleprint/station/tools/tester/tests/conftest.py similarity index 100% rename from station/tools/tester/tests/conftest.py rename to soleprint/station/tools/tester/tests/conftest.py diff --git a/station/tools/tester/tests/endpoints.py b/soleprint/station/tools/tester/tests/endpoints.py similarity index 100% rename from station/tools/tester/tests/endpoints.py rename to soleprint/station/tools/tester/tests/endpoints.py diff --git a/station/tools/tester/tests/example/__init__.py b/soleprint/station/tools/tester/tests/example/__init__.py similarity index 100% rename from station/tools/tester/tests/example/__init__.py rename to soleprint/station/tools/tester/tests/example/__init__.py diff --git a/station/tools/tester/tests/example/test_health.py b/soleprint/station/tools/tester/tests/example/test_health.py similarity index 100% rename from station/tools/tester/tests/example/test_health.py rename to soleprint/station/tools/tester/tests/example/test_health.py diff --git a/station/tools/tester/tests/helpers.py b/soleprint/station/tools/tester/tests/helpers.py similarity index 100% rename from station/tools/tester/tests/helpers.py rename to soleprint/station/tools/tester/tests/helpers.py