This document orients AI/code agents and new contributors. It explains how the repository works, the main code style guidelines, the core logic and classes, how rendering happens in the web context, and the main technological choices.
A Django-based web UI for the digital service carbon footprint model e-footprint. It embeds:
- A Python/Django backend orchestrating sessions, model building, and HTTP views
- A Bootstrap/HTMX frontend for dynamic, partial-page updates and a lightweight SPA feel
- Templates and small JS utilities to render forms, charts, and explainable results
Upstream domain logic (carbon/energy modeling) is provided by the e-footprint Python package and is adapted/wrapped for the web here.
The codebase follows Clean Architecture principles with clear separation of concerns:
model_builder/
├── domain/ # Business logic, no Django dependencies
│ ├── entities/ # Web wrappers around efootprint objects
│ │ ├── web_core/ # Core entities (ModelWeb, ServerWeb, JobWeb, etc.)
│ │ ├── web_builders/ # Builder-specific entities (ExternalAPIWeb, etc.)
│ │ ├── web_abstract_modeling_classes/ # Base classes (ModelingObjectWeb)
│ │ └── efootprint_extensions/ # ExplainableObject extensions
│ ├── services/ # Domain services (EmissionsCalculationService, etc.)
│ ├── interfaces/ # Repository interfaces (ISystemRepository)
│ ├── object_factory.py # Object creation/editing logic
│ ├── all_efootprint_classes.py
│ └── efootprint_to_web_mapping.py
├── application/
│ └── use_cases/ # CreateObjectUseCase, EditObjectUseCase, DeleteObjectUseCase
├── adapters/
│ ├── views/ # HTTP views (thin adapters)
│ ├── presenters/ # HtmxPresenter (formats responses)
│ ├── repositories/ # SessionSystemRepository
│ ├── forms/ # Form parsing (form_data_parser.py) and generation (strategies)
│ └── ui_config/ # UI configuration providers
├── templates/ # Django templates
└── domain/reference_data/ # JSON configs (default data)
- User action → View (adapter)
- View maps request → Use Case input
- Use Case orchestrates domain logic
- Presenter formats output → HTTP response
- AI agents
- Favor making all edits on the same file at once rather than step-by-step changes.
- Always propose computationally efficient solutions, especially for data processing and numerical operations:
- Prefer vectorized NumPy operations over Python loops
- Use single-pass algorithms (e.g.,
np.bincount) over multiple iterations - When presenting solutions, include complexity analysis (e.g., O(n) vs O(n²))
- Present trade-offs between readability and performance when relevant
- Python
- Formatter/lint: follow
.pylintrc; prefer PEP 8; type hints where helpful - Keep functions small; raise explicit errors with actionable messages
- Keep number of lines low; prefer to write long lines while respecting the 120 characters limit
- Performance: For array/data operations, prioritize NumPy vectorization over list comprehensions or loops
- Prefer using double quotes
"for strings, over single quotes'
- Formatter/lint: follow
- Django
- Views: thin adapters that delegate to use cases and presenters
- Templates: small, composable partials (see
model_builder/templates/model_builder/...) - Keep session data authoritative for the current modeling "system"
- JavaScript
- Vanilla JS + small helpers only; keep logic minimal; prefer progressive enhancement
- Use HTMX attributes for partial updates over large custom JS
- Tests
- Python tests go under
tests/(and app-specific subfolders) - Cypress E2E tests under
cypress/
- Python tests go under
ModelWeb is the central web wrapper around the e-footprint domain model. It:
- Deserializes session
system_dataviaefootprint.api_utils.json_to_system - Wraps the resulting domain
Systeminto web objects (wrap_efootprint_object) - Exposes typed accessors:
servers,services,jobs,usage_patterns,usage_journeys, etc. - Serializes back to JSON with or without calculated attributes via
to_json() - Maintains a flat index of objects (
flat_efootprint_objs_dict) for quick lookups
CreateObjectUseCase- Object creation with hooks pattern (pre_create,post_create, etc.)EditObjectUseCase- Object editing withpre_edithookDeleteObjectUseCase- Object deletion withcan_delete,pre_deletehooks
ObjectLinkingService- Parent-child linking logicEditService- Handles object editing with cascade cleanupSystemValidationService- Validates system completenessEmissionsCalculationService- Calculates daily emissions timeseries
all_efootprint_classes.py-MODELING_OBJECT_CLASSES_DICTmerges e-footprint classesefootprint_to_web_mapping.py- Maps e-footprint class names to web wrappers
Package of web wrappers around e-footprint domain classes. Each web wrapper provides template-friendly properties and form generation logic
adapters/views/- HTTP views (thin adapters calling use cases)adapters/presenters/htmx_presenter.py- Formats use case outputs as HTMX responsesadapters/repositories/session_system_repository.py- Loads/saves system from Django sessionadapters/forms/form_data_parser.py- Parses HTTP form data before passing to use casesadapters/forms/form_field_generator.py- Form field generation utilitiesadapters/ui_config/- Provides UI configuration (class labels, field metadata)
create_efootprint_obj_from_parsed_data()- Creates efootprint objects from pre-parsed form dataedit_object_from_parsed_data()- Handles object updates viaModelingUpdate
Specialized ExplainableObject subclasses that enhance e-footprint types:
ExplainableStartDate- Date serialization for formsExplainableHourlyQuantitiesFromFormInputs- Timeseries from growth rate inputsExplainableRecurrentQuantitiesFromConstant- Recurrent timeseries from constant values
Current approach: Timeseries generation is handled by ExplainableObject subclasses registered to the base timeseries types:
- Purpose: Generate hourly timeseries from simple form inputs (start date, duration, initial volume, growth rate)
- Registration: Matches JSON with
"form_inputs"key containing"type": "growth_based" - Storage: Stores both form inputs (for editing) AND computed compressed timeseries
- Lazy evaluation: Computes timeseries only when
.valueaccessed; caches result
- Purpose: Generate 168-element recurrent array from single constant value
- Registration: Matches JSON with
"constant_value"and"constant_unit"keys - Storage: Stores constant value (for editing) AND computed recurring_values array
- Lazy evaluation: Generates 168-element array when
.valueaccessed; caches result
- Session-driven state: The current system model is stored in Django session as
system_data(JSON). Each request reconstructs the domain system viaModelWeb(repository). - HTMX partials: Most UI actions trigger small HTTP requests that replace DOM snippets using templates under
model_builder/templates/model_builder/. - Templates
- Base layouts in
theme/templates/(e.g.,base.html,navbar.html) with Bootstrap styling - Feature templates and partials under
model_builder/templates/model_builder/
- Base layouts in
- Frontend assets
theme/static/scripts/includes small utilities (loading bars, charts, leader lines)- CSS/SCSS in
theme/static/scssand compiled CSS intheme/static/css
- User action triggers GET/POST (often via HTMX) to a view in
adapters/views/ - View creates use case input from request
- Use case executes business logic via
ModelWeband domain services - Presenter formats output as HTTP response with HTMX triggers
- View returns response (full page or partial for DOM swap)
python manage.py runserver(seeINSTALL.mdfor full setup)
- Python:
poetry run pytestorpython -m pytest - Cypress:
npx cypress openornpx cypress run
You should NOT need to create extension classes - use base efootprint classes directly.
If you need to add a base efootprint class to the web interface:
- Ensure it's in
efootprint.all_classes_in_order.ALL_EFOOTPRINT_CLASSES - Add entry to
model_builder/reference_data/form_type_object.json - Optionally create a web wrapper in
domain/entities/web_core/and add toEFOOTPRINT_CLASS_STR_TO_WEB_CLASS_MAPPING - Run
tests/tests_structure.pyas a script to auto-generate form field entries
To add a new way to generate timeseries (e.g., from uploaded CSV):
- Create new
ExplainableObjectsubclass indomain/entities/efootprint_extensions/ - Register with
@ExplainableHourlyQuantities.register_subclass(lambda d: ...)using unique JSON key - Implement
from_json_dict(),to_json(),__init__(), and lazy@property value - Create form template in
side_panels/dynamic_form_fields/ - Update
generate_dynamic_form()to detect your new type
- Session-only by default; no PII expected. Do not log sensitive info.
- Validate inputs in views; trust computations to the domain engine but validate shape/types at boundaries.
Don’t run and debug tests proactively, unless explicitly asked. After creating a new test or making significant, already tested, refactoring, pause and let me run the tests.