MasterOfPuppetsDev
  • Home
  • Services
  • How I Work
  • The Workshop
  • Contacts
  • Sign in
  • Register

TRS Control — A Compliance Web App Built for My Father

Summary

TRS Control is a full-stack web application I built for my father, an environmental officer at ARPA Lombardia (the Regional Agency for Environmental Protection of Lombardia, Italy). His job requires tracking the movement of excavated earth and rock ("Terre e Rocce da Scavo") under Italian environmental law — a process that was fully manual: paper forms, spreadsheets, and a lot of room for error. TRS Control digitizes and automates the entire workflow, from photographing a transport document to generating a compliance-ready PDF report.

The project posed two non-trivial challenges beyond the technical ones: his work machine was heavily locked down by the agency's IT policy, making standard software installation impossible; and the final product needed to be so simple to start that a non-developer could run it without any friction. Both constraints shaped every architectural and UX decision in the project.


The Problem Domain

Under Italian environmental regulation, every load of excavated material — soil, rock, gravel — must be documented with a transport form (DDT) and tracked from the excavation site to its destination. The destination site has rules attached to it: some sites only accept specific waste codes (CER codes), others have a hard capacity limit in kilograms, and others restrict the number of deliveries per authorization request.

ARPA Lombardia officers oversee compliance across multiple operators, sites, and excavations. The existing workflow was entirely paper-based: operators would fax or email scanned transport forms, and the office would manually enter data into spreadsheets. This was slow, error-prone, and produced no audit trail.

TRS Control replaces this with a structured digital pipeline.


The Real-World Constraints

A Locked-Down Machine

My father's work computer runs a hardened Windows configuration managed by the agency's IT department. Standard installation methods — running an installer with admin rights, installing Python from python.org, using pip globally — were all blocked. This ruled out the obvious approach.

The solution was Scoop, a command-line package manager for Windows that installs entirely within the user's home directory (%USERPROFILE%\scoop) without requiring elevated privileges. I wrote a master PowerShell setup script (setup_dev.ps1) that bootstraps the entire environment from scratch:

  1. Installs Scoop (no admin needed — it's a user-level install)
  2. Uses Scoop to install Git, Python, Tesseract OCR, and PostgreSQL
  3. Creates an isolated Python virtual environment (venv)
  4. Installs all Python dependencies from requirements.txt into the venv
  5. Initializes and seeds the PostgreSQL database
  6. Generates the mcp_config.json for the AI development tooling

The whole process is a single command:

.\setup_dev.ps1

When it finishes, the terminal prints SETUP COMPLETATO in green. That's the only feedback the user needs.

Zero-Friction Daily Use

A developer would think nothing of running three terminal commands to start a local server. A non-technical user would find that sequence fragile and anxiety-inducing. I addressed this at two levels:

Consolidated scripts. Repetitive operations are wrapped in short PowerShell scripts in the comandi/ folder. Starting the database is .\comandi\start_db.ps1, stopping it is .\comandi\stop_db.ps1. The whole startup sequence is documented as a five-row checklist in the README.

Guided AI workflows. The project integrates with Antigravity, an AI-capable IDE. A set of structured slash-command workflows lives in .agent/workflows/. The most important is /presentazione-interattiva: it starts the server, seeds demo data if the database is empty, navigates the browser to the login page, and guides the user through a full interactive tour of the application — no prior knowledge required.


Architecture

TRS Control is a server-rendered web application built on FastAPI with a PostgreSQL database and Jinja2 HTML templates. The stack was chosen for clarity over novelty: it maps closely to concepts a non-expert reader (like my father) can follow, and it avoids the complexity of a separate frontend framework.

Browser
  │
  ▼
FastAPI (app/main.py)
  ├── Middleware: CSRF protection, rate limiting
  ├── Routers: auth, documents, movements, sites, excavations, admin, reports
  ├── Services: OCR/AI, geocoding, business logic, report generation
  └── Models (SQLAlchemy ORM)
        │
        ▼
PostgreSQL database

Data Model

The five core tables reflect the real-world entities in the compliance workflow:

  • Users — three roles: Admin, Producer (excavation operators), Receiver (site managers)
  • Excavations — the source project from which material is extracted
  • Sites — the destination. Each site has a type (A, B, or C) that determines which business rules apply
  • Documents — uploaded transport forms (images, PDFs, Word docs, RTF files) with extracted and verified data stored as JSON
  • Movements — the core record linking an excavation to a site, tracking weight, CER waste code, transporter, license plate, and lifecycle status

Every movement has a lifecycle: DECLARED → IN_TRANSIT → CONFIRMED (or DISPUTED if there is a weight discrepancy at arrival).

Business Rules

The most domain-specific part of the system is the movement validator (app/services/logic_service.py), which enforces three different rule sets depending on site type:

  • Type A (Industrial Cycle): No capacity limit. Only CER codes listed in the site's authorized codes are accepted. Any other code is blocked outright.
  • Type B (Environmental Restoration): Hard capacity ceiling. The validator computes remaining = capacity_max - current_load and rejects any movement that would exceed it, with an error message that tells the operator exactly how many kilograms are still available.
  • Type C (General Use): No weight cap, but the number of loads per authorization request is capped at four.

These rules are enforced at the service layer, not at the database layer alone, so the error messages are human-readable and context-aware.


AI-Powered Document Processing

The feature that saves the most time is the document upload pipeline. An operator photographs a paper transport form with their phone and uploads it. The system extracts the relevant fields automatically and presents them for human verification.

The Extraction Pipeline

The service (app/services/ocr_service.py) follows a graceful degradation chain:

  1. Native text extraction (for PDFs, Word, RTF files): the file's embedded text is parsed directly with pdfplumber, python-docx, or striprtf. If this yields at least 75% of the expected fields, AI is not called at all.
  2. AI Vision (for images, or incomplete text documents): the file is base64-encoded and sent to OpenAI GPT-4o or Anthropic Claude (whichever API key is configured). The model is prompted to return a structured JSON object with fields for date, weight, CER code, license plate, and transporter name.
  3. Tesseract OCR fallback: if no API key is configured, Tesseract extracts the raw text and a set of regex patterns finds the fields. Because Tesseract frequently confuses visually similar characters (0/O, 1/I, 5/S), a normalization pass runs before the regex, converting OCR-typical substitutions back to digits.

The Verification UI

AI output is never committed directly to the database. After extraction, the user sees a split-screen view: the original document image on the left, a pre-filled form on the right. They can correct any field before clicking "Verify and Sign." This human-in-the-loop step is not just good practice — it is legally required, because the operator who signs is personally responsible for the data.


Address Verification

One operational problem in this domain is fake or mistyped addresses. An operator might submit a destination of "Via Roma 1, Milano" that does not correspond to any real authorized facility. The system addresses this with a geocoding pipeline in app/services/geocoding_service.py.

When a new site or excavation is created, the address is automatically validated:

  1. Google Maps Platform (primary): confirms the address exists, returns GPS coordinates, and flags whether a business is registered at that location.
  2. OpenStreetMap Nominatim (fallback): used if Google is not configured or rate-limits the request.

Every user-created site or excavation starts in PENDING verification status. Admins have a dedicated dashboard (/admin/verify) to review pending locations on a map. On desktop it is a classic data table; on mobile it is a swipe-card interface — swipe right to approve, swipe left to reject — inspired by the interaction model of Tinder. Pending sites can still be used, but are flagged visually until an admin confirms them.


Security

Security received deliberate attention given that the application handles regulated environmental data:

  • Password hashing: Argon2 (via argon2-cffi), the winner of the Password Hashing Competition, is used instead of bcrypt. It is significantly more resistant to GPU-based attacks.
  • Recovery codes: at registration, a cryptographically random recovery code (secrets.token_urlsafe) is generated, shown exactly once, and stored only as a hash. If the user loses their password, they present this code to reset it. After use, a new code is immediately generated. Admins can also reset codes on behalf of users.
  • CSRF protection: all state-changing form submissions require a token validated server-side by custom middleware.
  • Rate limiting: a sliding-window rate limiter blocks brute-force login attempts.
  • RBAC: route decorators enforce role checks. A Producer cannot access site management; a Receiver cannot create movements; neither can touch the admin panel.
  • Self-protection: the admin user management UI blocks an admin from deactivating or deleting their own account, preventing accidental lockout.

Report Generation

Operators and administrators can export data at any time from /reports. The report service (app/services/report_service.py) generates:

  • PDF reports using ReportLab, formatted for regulatory submission
  • Excel spreadsheets via openpyxl, with per-sheet breakdowns by site, excavation, or date range
  • CSV exports for integration with other tools

Testing

The project has a two-layer test suite.

Python unit and integration tests (pytest) cover authentication flows, document processing, business rule enforcement, CSRF and rate-limit middleware, multi-user scenarios, and report generation. The test database uses SQLite in-memory mode so no external service is needed to run the suite. Notable test files include test_multiuser_e2e.py (full workflow with a producer, a transporter, and a receiver acting in sequence) and test_auth_flows.py (registration, recovery code issuance, password reset, and admin user management).

Cypress end-to-end tests cover the browser-facing flows: login, navigation, document upload, movement creation, site management, report download, and the admin import workflow. The test suite includes a demo_tour.cy.js that follows the same path as the interactive presentation workflow, making it double as a regression guard for the onboarding experience.

A known Heisenbug in the rate-limiter tests — where time.time() mocking interacts unpredictably with pytest's overhead — is documented in BUG_NOTI.md and marked xfail. The planned fix is to inject a Clock dependency into the rate limiter rather than patching time.time globally.


AI-Assisted Development

This project was built using Antigravity, an AI-capable IDE, with a custom MCP (Model Context Protocol) server living at .agent/trs_mcp_server/. The server exposes tools the AI agent can call directly during development:

  • trs_check — runs pytest --collect-only to verify syntax and imports without executing tests
  • trs_tests — runs the full test suite and returns the output
  • trs_start_server / trs_stop_server — starts and stops the Uvicorn dev server in the background
  • trs_logs — reads the latest lines from logs/server.log

This setup means the agent can verify its own changes end-to-end without leaving the chat: write a function, start the server, run the tests, read the logs, fix the errors, repeat. The coding rules in .agent/rules/coding-trs.md enforce conventions across all agent sessions: always comment code thoroughly, always update documentation after significant changes, always run the full test suite before signing off.


Key Technical Decisions

FastAPI over Django or Flask. FastAPI's async-native design and automatic OpenAPI documentation (/docs) made it easier to reason about I/O-bound operations (AI API calls, geocoding requests). The auto-generated docs also serve as a secondary tool for my father to understand what the system is doing.

Server-rendered HTML over a SPA. A React or Vue frontend would have added build tooling, a separate dev server, and a JSON API contract to maintain. Jinja2 templates keep the deployment a single process with no frontend build step — critical for a locked-down machine with no Node.js installed by default.

Scoop as the package manager. The decision to use Scoop was the single most important technical call in the project. It unlocked everything else: Python, Tesseract, and PostgreSQL all install into the user profile with no admin dialog, no UAC prompt, and no IT ticket.

Alembic for migrations. Database schema changes are tracked as versioned migration scripts. This means updates to the production database (my father's machine) are applied with a single command rather than manual SQL.


Takeaways

Building TRS Control taught me that the hardest problems in real-world software are often not algorithmic. The technical architecture — FastAPI, PostgreSQL, SQLAlchemy, AI APIs — came together relatively smoothly. The hard parts were:

  • Figuring out how to install a Python environment on a machine where I had no admin rights
  • Designing a startup sequence so simple that someone with no programming background could follow it reliably, every morning, under time pressure
  • Balancing automation (AI extraction) with legal accountability (mandatory human verification)

The project is not currently in active use: it was built as a working prototype for my father, who trialed it locally on his work machine to log and report on excavated-material movements across his area of jurisdiction. It remains a complete, end-to-end demonstration of the workflow.

© 2026 MasterOfPuppetsDev · Privacy Policy

Cookie Policy

We use cookies to improve your experience on our site. Technical cookies are necessary for the site to function, while others help us improve our services. Read the full privacy policy