Change log

Migrating from v0.5 or earlier? See the migration guide for before/after snippets covering every breaking change in v0.6–v0.7.

v0.8.2

Released on 05-06-2026

Added

  • Database-backed ScenarioResult persistenceSqlScenarioRepository now writes results to the database via the same dual-path strategy as DatabaseDataManager.

    Details

    Results whose subclass implements the new algomancy_scenario.persistence.SqlResultLayout protocol are written as real, externally queryable SQL tables (one per sub-table, named result__<session>__<scenario_id>__<sub>); everything else falls back to a JSON blob in algomancy_scenario_runs.result_blob via the new abstract to_json / from_json contract on BaseScenarioResult. Typed rehydration on restart uses the algorithm’s result_class attribute (defaults to the bundled ScenarioResult). SqlScenarioRepository.delete cleans up any per-scenario result tables alongside the run/KPI rows.

Changed

  • Actualized the quickstart documentation page.

  • StatefulDataManager marked deprecated; DatabaseDataManager should be used for persistent deployments; StatelessDataManager is the default for in-memory use.

  • BaseScenarioResult now requires to_json / from_json — abstract on the base, concrete on the bundled ScenarioResult. Breaking for custom subclasses.

    Details

    Add to_json(self) -> str and a from_json(cls, json_string) -> BaseScenarioResult classmethod to existing BaseScenarioResult subclasses, or implement algomancy_scenario.persistence.SqlResultLayout to land DataFrames in real SQL tables instead of a JSON blob. The contract mirrors BaseDataSource.to_json / from_json exactly.

  • BaseAlgorithm.result_class — new class attribute (defaults to ScenarioResult) used by the repository to rehydrate persisted results into their original type; override on algorithms whose run() returns a custom subclass.

Fixed

  • Fixed a bug where playwright tests would fail to initialize due to an erroneous version lookup.

  • Added documentation for DatabaseDataManager.

v0.8.1

Released on 05-06-2026

Added

  • Data Parameters — per-scenario knobs declared by BaseDataSource subclasses.

    Details

    A concrete BaseDataSource subclass can override the new initialize_data_parameters() method to return a typed BaseParameterSet describing data-side knobs (date range, region filter, category whitelist, …). The framework collects user-supplied values per scenario, persists them alongside the algorithm parameters, and pushes them onto the algorithm via BaseAlgorithm.set_data_params before run(). Algorithms read self.data_params and decide whether and how to act on them — nothing is applied automatically.

    Surfaces:

    • BaseDataSource.initialize_data_parameters() — default returns EmptyParameters(), so existing subclasses keep working unchanged.

    • BaseAlgorithm.data_params property + set_data_params(...) method.

    • Scenario accepts a data_params kwarg and pushes it onto the algorithm before each run().

    • ScenarioFactory.get_associated_parameters(algo_name, dataset_key) returns the (algo_params, data_params) tuple, resolving the data half via the selected data source.

    • ScenarioManager.create_scenario(..., data_params=...) and ScenarioManager.get_data_parameters(dataset_key).

    • GUI: a second parameter card renders next to the algorithm card in the scenario-creation modal, populated as soon as the user picks a dataset.

    • API: GET /api/v1/sessions/{sid}/data/{dataset_key}/parameters returns the descriptor; POST /scenarios accepts a new data_params field.

    • Persistence: new nullable data_parameter_values TEXT column on algomancy_scenarios, added idempotently via ALTER TABLE on startup so existing databases keep loading.

    See Data Parameters for the data-source declaration pattern and Algorithms and Parameters for the algorithm-side read pattern.

  • SqlTableLayout protocol for database persistence for custom data sources.

    Details

    DatabaseDataManager now works for any BaseDataSource subclass, not just the bundled DataSource. Two persistence paths are dispatched at write time:

    • Subclasses that implement the new algomancy_data.database.SqlTableLayout protocol (to_sql_tables() -> dict[str, DataFrame] and from_sql_tables(tables)) get per-sub-table SQL storage — DataFrames land in real ds__{session}__{dataset}__{sub} tables and stay externally queryable.

    • Subclasses that don’t implement it fall back to JSON-blob persistence via the abstract to_json/from_json API already required by BaseDataSource. The catalogue grows a nullable payload column to hold the blob.

    The bundled DataSource satisfies SqlTableLayout trivially via its tables dict, so existing deployments see no behavioural change. Pre-existing algomancy_datasets tables that are missing the payload column now raise a clear error on startup directing the user at the manual-rebuild path. See Database persistence of custom data sources for the protocol and a worked example.

  • Added infrastructure to delete sessions.

    New SessionManager.delete_session(id) is used by a GUI interface element and the DELETE /api/v1/sessions/{session_id} API endpoint.

Breaking

  • Session identity is now a UUID; new property display_name is the mutable label.

    Details

    Previously a session’s identifier was its user-facing string ("main", "alice_experiment"), which made renaming impossible and made URLs leak human-readable names. The new set-up features:

    • Each session has a stable UUID id and a separate mutable display_name.

    • API routes use UUIDs: /api/v1/sessions/{session_uuid}/.... The list endpoint returns {sessions: [{id, display_name}, ...], default: id}.

    • New PATCH /api/v1/sessions/{id} renames a session (display_name only).

    • CreateSessionRequest.name is now display_name; CopySessionRequest.new_name is now new_display_name.

    • Filesystem backend writes a small meta.json into each session directory carrying {id, display_name}. Directories without meta.json are migrated transparently on first scan.

    • DB backend changes algomancy_sessions from (name PK) to (id PK, display_name).

    • SessionManager API renames: sessions_namessession_ids, start_session_namestart_session_id, new list_sessions(), get_display_name(), rename_session(), resolve_id_by_display_name().

    • Migration: run python scripts/migrate_m14_session_ids.py [--data-path DIR] [--database-url URL] once per deployment. The filesystem migration is also performed automatically on first scan, so upgrading without running the script will still work — the script just makes the rewrite eager and visible. For API clients, the resolver still accepts the old display_name in URLs (e.g. /sessions/main/...) as a soft alias, so existing integrations keep working while you migrate them to UUIDs.

  • Sessions are now always used

    Details

    The CoreConfig.use_sessions attribute was removed (and the same flag on ApiConfiguration). SessionManager is now always constructed; single-tenant deployments simply get a single auto-created "main" session. The /health endpoint no longer reports use_sessions.

    • Migration: delete use_sessions=... from your CoreConfig / ApiConfiguration / AppConfig arguments. If you were using use_sessions=False to hide the session picker from the GUI admin page, set FeatureConfig(show_session_picker=False) instead — the runtime keeps a session under the hood either way.

    • The GUI server attribute app.server.use_sessions is gone. Code that branched on it should use app.server.session_manager unconditionally; code that gated picker visibility should use app.server.show_session_picker.

v0.7.0

Added

  • algomancy-api package — new FastAPI HTTP service exposing the full ScenarioManager / SessionManager surface over HTTP, so remote frontends can drive an Algomancy backend over the network.

    Details

    The HTTP layer is deliberately thin — every route maps to a single manager method and responses reuse the existing to_dict() payloads. There is no parallel domain model; clients work with the same Scenario, DataSource, and KPI concepts the GUI does. Launch with algomancy-api --config-callback myapp:make_config (default port 8051) or algomancy-api --example for the bundled wiring. ApiLauncher.build(cfg) returns a standard FastAPI instance for use behind a production uvicorn/gunicorn process manager.

    • Sessions routerGET/POST /sessions, POST /sessions/{sid}/copy with session-name validation and conflict handling.

    • Algorithm + KPI discovery routerGET /sessions/{sid}/algorithms, GET /sessions/{sid}/algorithms/{name}/parameters (parameter descriptors derived from BaseParameterSet), and GET /sessions/{sid}/kpis.

    • Scenarios router — full CRUD plus POST /sessions/{sid}/scenarios/{id}/run, GET /sessions/{sid}/scenarios/{id}/status for polling, and GET /sessions/{sid}/processing for the currently running scenario.

    • Data router — list, fetch, delete, derive, POST /sessions/{sid}/data/from-json to add a dataset from a DataSource.to_json() payload, and POST /sessions/{sid}/etl to run ETL over a multipart upload.

    • MetaGET /health liveness probe; OpenAPI schema at /openapi.json and Swagger UI at /docs.

  • Error mapping — single exception → HTTP-status map (ValueError → 400, ParameterError → 400, AssertionError → 409, anything else → 500); response shape is always {"detail": "<message>"}.

  • End-to-end smoke test (tests/test_smoke_live.py) drives the full session → scenario → run → poll → fetch flow against a live server.

  • algomancy-scenario surfaceKPIFactory, ScenarioFactory, and ScenarioManager now expose algorithm/KPI template names for API discovery.

  • Documentation — new “Frontends” fundamentals page and HTTP API reference (docs/source/reference/api.md); refreshed algomancy-api README with quick-start and endpoint inventory.

Changed

  • SessionManager was lifted out of algomancy-gui into algomancy-scenario so it can be shared by all three frontends without pulling in Dash.

Fixed

  • Bugfix to BaseKPI surfaced while wiring up the algorithms router.

0.6.0

Released on 2026-05-29

The 0.6.0 release is the first instalment of the algomancy-data ETL/schema overhaul (milestones M1–M8). The chain of changes is large but additive where possible; consult the migration guide for the breaking-change summary.

Added

  • Schema API modernization (M1) — new Column dataclass gives schemas a declarative field model; Schema gains columns(), required_columns(), optional_columns(), and primary_key() classmethods.

    Details

    Column fields: name, dtype, optional, primary_key, default, nullable, unique, description. Column is exported from the public algomancy_data API. Example schemas migrated to the Column style; legacy _DATATYPES dicts auto-convert with a DeprecationWarning.

  • Structured validation framework (M2)ValidationMessage gains location fields; run_validation returns a rich ValidationResult dataclass; new built-in validators added.

    Details
    • ValidationMessage gains optional table, column, row, and code fields. __str__ surfaces the location inline; __eq__ / to_dict() added for inspection and serialisation.

    • ValidationSequence.run_validation now returns a ValidationResult dataclass (is_valid, messages, halt_on, counts_by_severity, plus as_dataframe(), messages_by_severity(), messages_at_least(), and __bool__ / __iter__ / __len__ helpers).

    • ValidationSequence.halt_on (default CRITICAL) lets projects promote ERROR or WARNING to a pipeline-halting severity without subclassing.

    • New built-in validators: RequiredColumnsValidator, OptionalColumnGuard (validator/transformer hybrid that injects missing optional columns with their declared default and dtype), PrimaryKeyValidator (joint-key uniqueness + non-null), UniqueValueValidator, and MissingValueValidator.

  • Predictable ETL termination (M3)ETLPipeline.run() returns an ETLResult dataclass; data-quality failures surface cleanly rather than as exceptions.

    Details

    ETLResult carries status, datasource, validation_result, raised, plus is_success / is_failure / messages helpers. Data-quality failures arrive as ETLResult(status='failed'); programmer errors (KeyError/AttributeError/TypeError) still propagate so real defects aren’t masked. DataManager.etl_data / ScenarioManager.etl_data return the result; auto-create only fires on success. DataTypeConverter records ConversionIssue objects instead of silently producing NaNs; these surface as CONVERSION_FAILED messages. StatefulDataManager.startup() records per-item failures in self.startup_errors and rolls back failing keys.

  • Reduce ETL boilerplate (M4) — extractor registry and SimpleETLFactory let users build a full pipeline without subclassing ETLFactory.

    Details

    New extractor registry (register_extractor, get_extractor_class, registered_keys) keyed on (FileExtension, SchemaType). The four built-in extractors (CSV single, JSON single, XLSX single, XLSX multi) populate it at import time. ETLFactory now ships with default implementations of create_extraction_sequence, create_validation_sequence, create_transformation_sequence, and create_loader — no longer @abstractmethod. New SimpleETLFactory(schemas, transformers=None, loader=None, logger=None) is a concrete factory requiring no subclassing. DataManager.prepare_files dispatches by the schema’s declared _EXTENSION, falling back to the path suffix.

  • Flexibility & extension points (M5)register_extractor is now public; new ForeignKeyValidator and DataFrameExtractor; docs on extending the registry.

    Details

    Schema.extension() accepts any StrEnum-derived value or plain string, so user-defined FileExtension subclasses route through the registry without changes. ForeignKeyValidator checks every (non-null) value in left_table[left_col] exists in right_table[right_col]; supports composite keys; emits structured messages with FK_VIOLATION, TABLE_NOT_FOUND, and COLUMN_NOT_FOUND codes. DataFrameExtractor wraps a pre-built pandas DataFrame via the same Extractor contract. New docs page fundamentals/extending.md covers subclassing FileExtension, registering an extractor, and extending DataTypeConverter.

  • Docs, tests, migration (M6)algomancy-data coverage at 87%; docs/source/fundamentals/ETL.md rewritten end-to-end; single migration guide added.

    Details

    pytest-cov added; algomancy-data coverage reports 87%. docs/source/fundamentals/ETL.md opens with a SimpleETLFactory quickstart, then covers Column-based schemas, the extractor registry, new validators, structured validation messages, ETLResult-based termination, and a full worked example. New docs/source/migration.md covers every breaking change in v0.6/0.7/0.8 with before/after snippets. Bundled example refreshed: ExampleETLFactory inherits from SimpleETLFactory, demonstrates OptionalColumnGuard and ForeignKeyValidator.

  • Relational cascade cleanup (M7) — declarative cascade-drop mechanism via CascadeDropTransformer and CascadeSnapshot; drops surface as structured ValidationMessages.

    Details

    Declare foreign keys via Column(foreign_key=("parent_table", "parent_col")), opt parents into cascade with parent_requires_child=True, and optionally track_partial_loss=True. CascadeDropTransformer drops orphans and parents-with-missing-children to a fixpoint; CascadeSnapshot enables partial-loss detection. Drop messages carry codes CASCADE_ORPHAN_DROP, CASCADE_REQUIRED_CHILD_DROP, and CASCADE_PARTIAL_LOSS_DROP at Severity.ERROR. ForeignKeyValidator.from_schemas([...]) auto-derives validators from the same declarations. Fully opt-in: pipelines without the transformer are unaffected.

  • Quickstart modernization (M8)algomancy-quickstart templates updated to emit Column-based schemas, SimpleETLFactory-derived ETL factories, and new validation defaults out of the box.

  • Quickstart Module (algomancy-quickstart) — interactive setup wizard that guides new users through creating a full Algomancy application in five steps.

    Details

    The QuickstartWizard covers: (1) creating folder structure and a basic main.py; (2) generating implementation templates for schemas, algorithms, KPIs, ETL factories, and custom pages; (3) scanning data files and generating ETL pipelines with schema inference; (4) installing default assets (CSS, images) from GitHub or a bundled fallback; and (5) configuring custom styling with colors and themes. Features interactive prompts, intelligent file detection (CSV, XLSX, JSON), automatic datatype inference with column mapping, and Jinja2-based code generation.

  • Added character-safe and existing scenario name checks (via InputChecker), resulting in a disabled create button when invalid.

  • Added list_tags function to the scenario registry, accessible via ScenarioManager.

  • BaseAlgorithm now has access to the application’s central logger through self._logger.

  • Added pages section to the tutorial.

Changed

  • Cleanup of dependencies between algomancy-content and algomancy-gui.

    Details
    • [Breaking] BasePage and subclasses moved to algomancy_gui; update imports accordingly.

    • LibraryManager moved to algomancy-gui from algomancy-content.

    • algomancy-gui internals reorganized.

  • Refinement of Schema class.

    Details
    • [Breaking] _defined_datatypes moved to _DATATYPES as a class attribute.

    • Schemas no longer need to be instantiated before being passed to the configuration — passing types works; passing instances still works.

    • [Breaking] Schema classproperties (datatypes, datatype_groups, sub_names, file_name, extension) are now explicit @classmethods — call sites must use (). The classproperty shim was removed; get_subschema() now returns a dynamic class rather than an instance, eliminating subschema instance state.

  • Made dataset_name_invalid generic (name_invalid) for both datasets and scenarios.

  • Moved name_invalid callback to inputchecker.py to avoid duplicate code.

  • Euro (€) is now the default quantity for money.

  • Split AppConfiguration into separate configuration containers for better organization. (backwards compatible)

  • Implemented the Singleton pattern on Logger for global access.

  • BASE_DATA_BOUND renamed to BASEDATASOURCE for legibility.

  • fill_empty() in transformer.py: replaced deprecated fillna(method=...) with DataFrame.ffill(axis=1).

  • DataManagementImportModal now reads ETLResult and shows validation message counts on failure (back-compat catch on ValidationError preserved).

Fixed

0.4.4

Released on 23-2-2026

Added

  • Documentation updates: reference is now complete with some todos

0.4.3

Released on 18-2-2026

Added

  • Added BaseDataSource._post_derive() method as a hook to allow subclasses to perform post-derivation processing.

  • Added _post_derive() PyTest to pre-merge testing protocol.

0.4.2

Released on 17-2-2026

Added

  • Added test_datasource.py to the PyTest pre-merge protocol.

Fixed

  • Fixed bug where BaseDataSource.derive() would not update ds_type and id appropriately.

  • Fixed bug where DataSource.from_json() would not preserve datatypes.

0.4.0

Released on 12-02-2026

Changed

  • [Breaking] Removed InputFileConfigs, moved responsibility to Schema class

  • Updated documentation

  • Updated tutorial code (data, root files, assets, data handling, algorithms, KPIs)

Migration from InputFileConfiguration to Schema-only pattern

Prior to this release, file configurations were specified separately using SingleInputFileConfiguration and MultiInputFileConfiguration. These have been removed, and the Schema class now contains all necessary file metadata.

Migration example

For single-file schemas (CSV, JSON, XLSX with one sheet):

Old version

from algomancy_data import Schema, FileExtension, DataType, SingleInputFileConfiguration

class WarehouseLayoutSchema(Schema):
    ID = "slotid"
    X = "x"
    Y = "y"
    ZONE = "zone"

    @property
    def datatypes(self) -> Dict[str, DataType]:
        return {
            WarehouseLayoutSchema.ID: DataType.STRING,
            WarehouseLayoutSchema.ZONE: DataType.STRING,
            WarehouseLayoutSchema.X: DataType.FLOAT,
            WarehouseLayoutSchema.Y: DataType.FLOAT,
        }

# File configuration was separate
warehouse_config = SingleInputFileConfiguration(
    extension=FileExtension.CSV,
    file_name="warehouse_layout",
    file_schema=WarehouseLayoutSchema(),
)

New version

from algomancy_data import Schema, FileExtension, DataType
from algomancy_data.schema import SchemaType

class WarehouseLayoutSchema(Schema):
    # File metadata now lives in the Schema class
    _FILENAME = "warehouse_layout"
    _EXTENSION = FileExtension.CSV
    _SCHEMA_TYPE = SchemaType.SINGLE

    ID = "slotid"
    X = "x"
    Y = "y"
    ZONE = "zone"

    def _defined_datatypes(self) -> Dict[str, DataType]:
        return {
            WarehouseLayoutSchema.ID: DataType.STRING,
            WarehouseLayoutSchema.ZONE: DataType.STRING,
            WarehouseLayoutSchema.X: DataType.FLOAT,
            WarehouseLayoutSchema.Y: DataType.FLOAT,
        }

# No separate configuration needed - just instantiate the schema
warehouse_schema = WarehouseLayoutSchema()

Key changes for single-file schemas:

  • Add _FILENAME, _EXTENSION, and _SCHEMA_TYPE = SchemaType.SINGLE as class attributes

  • Rename datatypes property to _defined_datatypes() method

  • Remove the separate SingleInputFileConfiguration wrapper

For multi-sheet XLSX files:

Old version

from algomancy_data import Schema, FileExtension, DataType, MultiInputFileConfiguration

class StedenSchema(Schema):
    COUNTRY = "Country"
    CITY = "City"

    @property
    def datatypes(self) -> Dict[str, DataType]:
        return {
            StedenSchema.COUNTRY: DataType.STRING,
            StedenSchema.CITY: DataType.STRING,
        }

class KlantenSchema(Schema):
    ID = "ID"
    Name = "Naam"

    @property
    def datatypes(self) -> Dict[str, DataType]:
        return {
            KlantenSchema.ID: DataType.INTEGER,
            KlantenSchema.Name: DataType.STRING,
        }

# Separate configuration with multiple schemas
multisheet_config = MultiInputFileConfiguration(
    extension=FileExtension.XLSX,
    file_name="multisheet",
    file_schemas={
        "Steden": StedenSchema(),
        "Klanten": KlantenSchema(),
    },
)

New version

from algomancy_data import Schema, FileExtension, DataType
from algomancy_data.schema import SchemaType

class LocationSchema(Schema):
    # File metadata in the schema
    _FILENAME = "multisheet"
    _EXTENSION = FileExtension.XLSX
    _SCHEMA_TYPE = SchemaType.MULTI

    # All column names from all sheets in one schema
    # steden sheet
    COUNTRY = "Country"
    CITY = "City"

    # klanten sheet
    ID = "ID"
    Name = "Naam"

    def _defined_datatypes(self) -> Dict[str, Dict[str, DataType]]:
        # Return a dict of sheet names to column datatypes
        return {
            "Steden": {
                LocationSchema.COUNTRY: DataType.STRING,
                LocationSchema.CITY: DataType.STRING,
            },
            "Klanten": {
                LocationSchema.ID: DataType.INTEGER,
                LocationSchema.Name: DataType.STRING,
            },
        }

# No separate configuration needed
location_schema = LocationSchema()

Key changes for multi-sheet schemas:

  • Combine all sheet schemas into a single Schema class

  • Add _FILENAME, _EXTENSION, and _SCHEMA_TYPE = SchemaType.MULTI as class attributes

  • Rename datatypes property to _defined_datatypes() method

  • Return Dict[str, Dict[str, DataType]] (sheet name → column datatypes) instead of Dict[str, DataType]

  • Remove the separate MultiInputFileConfiguration wrapper and individual sheet schemas

0.3.21

12-02-2026

Added

  • Added charactersafe & existing dataset name checks (in InputChecker Class), resulting in a disabled import button

Changed

  • Custom pages should now subclass the appropriate base classes (moved from Protocol to AbstractBaseClass). Functional implementation should remain unchanged.

Fixed

  • Fixed a bug where the overview page failed to use the OverviewPage content from the registry appropriately.

  • Fixed risk of App breaking down when user tries to import/derive a new dataset with weird names (e.g. with .) or an already existing dataset name.

0.3.20

Fixed

  • StandardDataPage now work again when sessions are enabled

0.3.17

Fixed

  • StandardHomePage and StandardDataPage now work again when sessions are disabled

0.3.16

Changed

  • GuiLauncher was moved to algomancy-gui

  • Updated numpy dependency to 2.4.1 due to yanked version 2.4.0

0.3.13

Added

  • Added use_sessions atribute to AppConfiguration to allow disabling sessions.

0.3.5 - 0.3.12

Revised distribution model; the package is now hosted at pypi.org/project/algomancy and installable with the terminal command:

uv add algomancy

0.3.4

Released at 07-01-2026

Added

  • Added copy() to BaseParameterSet and corresponding tests.

  • Introduced locking to BaseParameterSet to prevent concurrent mutation.

  • Added get_parameters responsibility across factories/facades and session manager.

  • Work-in-progress Command Line Interface (CLI).

Changed

  • [Breaking] Renamed BaseAlgorithmParameters to BaseParameterSet and moved to algomancy-utils in preparation for data parameters

  • Migration to UV workspaces for the monorepo structure.

  • Units/measurements refactor in algomancy-utils: moved formatting to BaseMeasurement; removed Unit.name.

  • Various import path updates across packages after workspace migration.

  • [Breaking] algomancy-data (ETL): Unified ETL pipeline concepts.

    • Introduced ExtractionSequence and TransformationSequence as the orchestration primitives for extract and transform steps.

    • ETLPipeline now accepts extraction_sequence and transformation_sequence instead of a dict of extractors and a list of transformers.

    • ETLFactory abstract methods have been renamed:

      • create_extractors(...)create_extraction_sequence(...)

      • create_transformers()create_transformation_sequence()

    • Validation continues to use ValidationSequence unchanged.

    • See updated examples in example/data_handling/factories.py and implementation in:

      • packages/algomancy-data/src/algomancy_data/etl.py

      • packages/algomancy-data/src/algomancy_data/extractor.py

      • packages/algomancy-data/src/algomancy_data/transformer.py

Migration notes (ETL)

  • Replace factory method implementations and usages:

    • Implement create_extraction_sequence(files) to build and return an ExtractionSequence (use sequence.add_extractor(...)).

    • Implement create_transformation_sequence() to build and return a TransformationSequence (use sequence.add_transformer(...)).

  • When constructing ETLPipeline, pass the sequences instead of individual collections.

Fixed

  • GUI: fixed slider issue on showcase page; ensured overview loads immediately; reduced layout snapping on data page; hidden dummy card; hid initial loader flash via CSS; cleaned up legacy callbacks; verified callback field necessity using get_parameters.

  • Tests: updated and fixed several tests (including utils unit tests; temporary intentionally failing test removed/fixed).

CI/CD

  • Multiple pipeline updates for CI and CD, including result publication tweaks and cache adjustments.

Misc

  • Minor documentation updates and code cleanups.

0.3.3

Released at 12-12-2025

Summary

  • Breaking: Content pages moved to a protocol/class-based system. Instead of passing content functions, the frontend now receives Page classes. This aligns page composition with the new BaseAlgorithm/BaseKPI patterns.

  • Parameters

    • Added Multi-select parameter support via MultiEnumParameter.

    • Added time-related parameters TimeParameter and IntervalParameter with frontend input components.

  • Added a configuration option that shows/hides the Upload tab for parameter selection. Default is hidden.

  • Added a _ standard_ data page that uses the tables attribute of the data container to render simple DashTables.

Migration to Page classes (breaking)

Prior to this release, pages were typically supplied to the app as plain content functions. As of v0.3.2, pages are defined as classes that satisfy the page protocol and are passed to the frontend by type (class) rather than by function reference.

The content classes need to follow the appropriate Protocols. That is, implement the required fuctions with signature. These protocols are enforced by validation of the AppConfiguration. An outline of the expected functions is included below.

  • HomePage:

    • create_content() -> html.Div

    • register_callbacks() -> None

  • DataPage:

    • create_content(data: BASEDATASOURCE) -> html.Div

    • register_callbacks() -> None

  • ScenarioPage:

    • create_content(scenario: Scenario) -> html.Div

    • register_callbacks() -> None

  • ComparePage:

    • create_side_by_side_content(scenario: Scenario, side: str) -> html.Div

    • create_compare_section(left: Scenario, right: Scenario) -> html.Div:

    • create_details_section(left: Scenario, right: Scenario) -> html.Div

    • register_callbacks() -> None

  • OverviewPage:

    • create_content(List[Scenario]) -> html.Div

    • register_callbacks() -> None

Below is a conceptual before/after to illustrate the change.

Old version (functions passed to the frontend)

# example (conceptual)
from algomancy_gui.configuration.appconfig import AppConfig


class ExampleDataPage:
  def create_content(self, data) -> html.Div:
    ...

  def register_callbacks(self):
    ...


config = AppConfig(
  home_page_content='standard',
  data_page_content=ExampleDataPage.create_content,  # Callable[..., Div] or str
  data_page_callbacks=ExampleDataPage.register_callbacks  # Callable[..., None] or str
)

New version (Page classes passed to the frontend)

# example (conceptual)
from algomancy_gui.configuration.appconfig import AppConfig


class ExampleDataPage:
  def create_content(self, data) -> html.Div:
    ...

  def register_callbacks(self):
    ...


config = AppConfig(
  home_page_content='standard'  # Protocol[HomePage] or str
data_page = ExampleDataPage,  # Protocol[DataPage] or str
)

If you maintain custom pages, convert them into classes that implement the expected page protocol (constructor + render/handlers). For a minimal reference implementation, see algomancy_content.pages.placeholderscenariopage.PlaceholderScenarioPage.

0.3.2

Released at 16-12-2025

Summary

  • Added Sessions to the App

    • Sessions can be managed in the Admin page

    • An empty session can be created by clicking the “New Session” button in the Admin page

    • An copy of a session can be created by clicking the “Copy Session” button in the Compare page

      • This copies only the datasets and not the scenarios

  • Breaking: When retrieving the scenario manager get_app().server.scenario_manager no longer works

    • Scenario manager is now available through the session manager and the active session

0.3.1

Released at 03-12-2025

Summary

  • Breaking: Revised AlgorithmTemplate pattern to BaseAlgorithm workflow, analog to BaseDataSource

  • Breaking: Also revised KPITemplate pattern to BaseKPI workflow

  • Scenario engine

    • Export and typing cleanups in scenario to support BaseAlgorithm/BaseKPI patterns.

    • Measurement/unit utilities updated in scenario.unit.

    • Added threshold KPIs; ImprovementDirection.AT_LEAST, .AT_MOST, and argument threshold

  • Components and pages

    • Updated Compare and Scenario page callbacks and KPI Card to align with the new KPI base class and thresholds.

    • Minor adjustments to Standard Home and Overview pages.

  • Examples

    • Replaced example KPI template with a BaseKPI implementation (DelayKPI).

Migration to BaseAlgorithm

To align the development patterns, the template pattern has been substituted for a basemodel pattern, similarly to the creation of custom DataSources. Below is an example of the before and after.

Old version

def batching_algorithm(
    data: DataSource,
    parameters: BatchingAlgorithmParameters,
    set_progress: Callable[[float], None],
) -> ScenarioResult:
    sleep(parameters.batch_size)
    set_progress(100)
    return ScenarioResult(data_id=data.id) 


batching_algorithm_template = AlgorithmTemplate(
    name="Batching",
    param_type=BatchingAlgorithmParameters,   # Parameter type used to be passed 
    main_method_template=batching_algorithm,  # as well as the main method handle. 
)

New version

class BatchingAlgorithm(BaseAlgorithm):
    """ From v0.3.1, create your own algorithm by deriving BaseAlgorithm """
    def __init__(self, params: BatchingAlgorithmParameters):
        super().__init__("Batching", params)

    @staticmethod
    def initialize_parameters() -> BatchingAlgorithmParameters:
        """ Minimal bit of boilerplate, necessary for internal handling """
        return BatchingAlgorithmParameters()
    
    def run(self, data: DataSource) -> ScenarioResult:
        """ Derived Algorithms now have to implement their own run() method """
        sleep(self.params.batch_size)
        self.set_progress(100)
        return ScenarioResult(data_id=data.id)

Migration to BaseKPI

Similarly, the KPI creation has also been moved to the basemodel pattern.

Old version

default = QUANTITIES["default"]
default_unit = BaseMeasurement(default["unit"], min_digits=1, max_digits=3, decimals=1)

def create_error_template():
    def error_rate_calculation(result: ScenarioResult) -> float:
        return 0.1 * (1 + 0.5 * random.random())  # placeholder
    
    return KpiTemplate(
        name="Error Rate",
        better_when=ImprovementDirection.LOWER,
        callback=error_rate_calculation,
        measurement_base=default_unit,
    )

New version

default = QUANTITIES["default"]
default_unit = BaseMeasurement(default["unit"], min_digits=1, max_digits=3, decimals=1)

class ErrorKPI(BaseKPI):
    def __init__(self):
        """ Basic configurations are now made by passing them to the base object """
        super().__init__(
            name             = "Error Rate",
            better_when      = ImprovementDirection.LOWER,
            base_measurement = default_unit,
        )

    def compute(self, result: ScenarioResult) -> float:
        """ The user defines the compute function, as before"""
        return 0.1 * (1 + 0.5 * random.random())  # placeholder

Threshold KPIs

A threshold KPI is initialized with a threshold value and appropriate ImprovementDirection, in addition to the usual arguments. It is considered to be a ‘success’ if the value of the kpi exceeds (or does not exceed, in the case of AT_MOST) the threshold value. The .pretty() function will format a threshold as either a checkmark or a cross, depending on the value relative to the threshold.

An example is included below

# noinspection PyUnresolvedReferences
class DelayKPI(BaseKPI):
    def __init__(self):
        super().__init__(
            name="Average Delay",
            better_when=ImprovementDirection.AT_MOST,
            base_measurement=BaseMeasurement(QUANTITIES["time"]["s"], min_digits=1, max_digits=3, decimals=1),
            threshold=1200,
        )

0.2.15

Released at 28-11-2025

Summary

  • DataSource and ScenarioResult are now derived from the abstract BaseDataSource and BaseScenarioResult, which are used for internal typing and should be extended for custom containers rather than DataSource and ScenarioResult. This change is backwards-compatible, and should not break existing projects

0.2.14

Released at 21-11-2025

Summary

  • Styling and UX improvements across default components and pages.

  • Restructured CSS and refined component defaults; added/updated animated spinners and modal behavior.

  • Data engine: improvements to extractors.

  • Logging and reliability fixes.

Details

  • UI/UX

    • Restyled default components and performed multiple styling updates.

    • Restructured CSS and applied small CSS tweaks to polish visuals.

    • Disabled page background when modals are open for better focus.

    • Wrapped Data page in a spinner and added further updates to animated spinners with better customizability.

  • Data Engine

    • Multiple extractor-related improvements and refactors.

  • Examples

    • Updated tests and example implementation for alignment with UI and engine changes.

Bug fixes

  • Fixed a scale assertion bug in the measurement formatting logic.

  • Replaced erroneous log_exception usage with the correct log_traceback in logging.

Multi extractor update

This is a breaking change

InputFileConfiguration is now an abstract class; its uses should be replaced by SingelInputFileConfiguration, which is a drop-in replacement. Its counterpart, the MultiInputFileConfiguration is used by the MultiExtractor. Its use should be clear from the example below.

class StedenSchema(Schema):
    COUNTRY = "Country"
    CITY = "City"

    @property
    def datatypes(self) -> Dict[str, DataType]:
        return {
            StedenSchema.COUNTRY: DataType.STRING,
            StedenSchema.CITY: DataType.STRING,
        }


class KlantenSchema(Schema):
    ID = 'ID'
    Name = "Naam"

    @property
    def datatypes(self) -> Dict[str, DataType]:
        return {
            KlantenSchema.ID: DataType.INTEGER,
            KlantenSchema.Name: DataType.STRING,
        }


multisheet_config = MultiInputFileConfiguration(
    extension=FileExtension.XLSX,
    file_name="multisheet",
    file_schemas={
        "Steden": StedenSchema(),
        "Klanten": KlantenSchema(),
    }
)
class ExampleETLFactory(de.ETLFactory):
    def __init__(self, configs, logger=None):
        super().__init__(configs, logger)

    def create_extractors(
        self,
        files: Dict[str, F],  # name to path format
    ) -> Dict[str, de.Extractor]:
        ...
        multisheet_extractor = de.XLSXMultiExtractor(  # -- this is new
            file=cast(de.XLSXFile, files[multisheet]),
            schemas=self.get_schemas(multisheet),
            logger=self.logger,
        ),

Note: the resulting DataFrames show up with keys multisheet.Steden and multisheet.Klanten in the ETL internal dictionary.

0.2.13

Released at 06-11-2025

Summary

  • Autocreate now supports algorithms with parameters.

  • Delete button is now disabled while a scenario is queued or running.

  • CQM-loader is now a configurable option

0.2.12

Released at 06-11-2025

Summary

  • Bug fixes

Bug fixes

  • Fixed an issue where unit.py would cause crashes in python pre-3.14.

0.2.11

Released at 05-11-2025

Summary

  • Several minor bug fixes and documentation updates.

  • implemented __eq__ for DataSource, checking the internal uuid.

Bug fixes

  • Removed redundant message from internal logging

  • Scenario.cancel() and Scenario.refresh() now act properly when no logger is passed

  • Secret key is now set on BasicAuth

  • data path is no longer validated for configurations without persistent state.

0.2.10

Released at 05-11-2025

Summary

  • Introduced a unified KPI measurement framework with BaseMeasurement, replacing UOM. This allows for automatic unit conversion and consistent representation in the UI.

  • Renamed the remaining “performance” page references to “compare” across modules for consistency.

  • Improved logging: stack traces are now routed to the logger; startup log severity adjusted.

  • Documentation: README updates and minor cleanup.

  • Tests: Added pytest module tests/test_unit_measurement_examples.py covering Measurement examples from unit.py.

  • New feature: automatic creation of scenarios is now supported.

  • New feature: Added refresh functionality to the Scenario component.

  • [Breaking] New feature: AppConfiguration class now manages and validates the launch configuration

AppConfiguration

This is a breaking change.

Added AppConfiguration class to manage and validate the launch configuration. Conceptually, this class is a wrapper that provides a consistent interface for the configuration fields and their validation. This class is now used to manage the launch sequence. In particular, DashLauncher.build(…) now takes an AppConfiguration object as an argument, instead of a dictionary. Your main method must be migrated to use the new class. An example is shown below:

# main method: preferred version

from algomancy_gui.gui_launcher import GuiLauncher
from algomancy_gui.configuration.appconfig import AppConfig


def main():
  app_cfg = AppConfig(
    data_path="data",
    has_persistent_state=True,
    #       ...
  )
  app = GuiLauncher.build(app_cfg)
  GuiLauncher.run(app=app, host=app_cfg.host, port=app_cfg.port)

For migration, the AppConfiguration.from_dict(...) method can be used to create an AppConfiguration object from a dictionary. Note that this is not advised, as this will not allow for IDE support.

# main method: migration alternative

from algomancy_gui.gui_launcher import GuiLauncher
from algomancy_gui.configuration.appconfig import AppConfig


def main():
  configuration = {
    "data_path": "data",
    "has_persistent_state": True,
    #       ...   
  }
  app_cfg = AppConfig.from_dict(configuration)
  app = GuiLauncher.build(app_cfg)
  GuiLauncher.run(app=app, host=app_cfg.host, port=app_cfg.port)

Autocreate

Added automatic creation of scenarios. This will cause any creation of a DataSource (or derived) to spawn a Scenario with the same name (suffixed with [auto]). The algorithm template must be specified in the configuration dictionary. To configure, add the below to the configuration.

# framework configuration
app_cfg = AppConfiguration(
#    ...,
    autocreate= True,             # set to True for autocreate mode
    default_algo= "As is",        # select the name of an algorithm template to use for autocreation
#    ...,
)
  • Added refresh functionality to the Scenario component. This will cause the Scenario to reset its status and discard the ScenarioResult. To refresh a scenario, the Scenario.refresh() method is called from the Scenario management screen. The process scenario button is now context-aware. At a later time, this button will also support a cancel operation.

Interface changes

  • [Breaking] Replaced UOM with BaseMeasurement in KPI-related APIs and templates. Update custom KPI code to construct and return Measurement/BaseMeasurement instead of the old types.

  • [Breaking] Removed obsolete KpiType enum.

Measurement framework

  • New BaseUnit, Quantity, BaseMeasurement, and Measurement types in algomancy\\scenarioengine\\unit.py provide consistent formatting, auto-scaling, and unit chaining.

  • KPI templates should be migrated to BaseMeasurement/Measurement. See algomancy\\scenarioengine\\keyperformanceindicator.py for how KPIs surface measurements.

  • Extensive examples are available in algomancy\\scenarioengine\\unit.py\\example_usage().

  • KPI Template creation should now follow the following pattern:

import random

from src.algomancy import ImprovementDirection, KpiTemplate, ScenarioResult
from scenario import QUANTITIES, BaseMeasurement


def throughput_calculation(result: ScenarioResult) -> float:
    return 100 * (1 + 0.5 * random.random())  # placeholder


mass = QUANTITIES["mass"]
mass_kg = BaseMeasurement(
    mass["kg"],  # the default unit is kg; the associated quantity is mass
    min_digits=1,  # the minimum number of nonzero digits before the decimal point
    max_digits=3,  # the maximum number of nonzero digits before the decimal point
    decimals=2,  # the number of decimal places to display
    smallest_unit="g",  # the smallest unit to display - overrides min_digits
    largest_unit="ton",  # the largest unit to display - overrides max_digits
)

template = KpiTemplate(
    name="Throughput",
    # type=KpiType.NUMERIC,                     # KpiType has become redundant, formatting is now handled by Measurement
    better_when=ImprovementDirection.HIGHER,
    callback=throughput_calculation,
    measurement_base=mass_kg,  # Pass the measurement to use as a basis for the kpi value
)

Compare page naming cleanup

All remaining references to the performance page were renamed to compare for consistency (imports, component IDs, modules). If you import internal modules, update your imports accordingly.

Note: css classes are also affected, so you may need to update your style.css file.

Logging

  • Exceptions now include full stack traces in the central logger.

  • The startup message severity has been adjusted for better signal in production logs.

Docs

  • README refreshed to reflect the new measurement framework and naming.

0.2.9

Released at 29-10-2025

Summary

New features

  • Added internal ContentRegistry class, which now manages and distributes the content functions.

Bug fixes

  • Fixed issue where url callbacks would cause conflicts.

  • Fixed a bug where the url callbacks had multiple listeners, which sometimes caused synchronization issues.

ContentRegistry

The ContentRegistry class is now used to manage and distribute the content functions. These responsibilities were previously handled by the Launcher class, which has been refactored to only manage the launch sequence. This is a purely internal change, and should not affect the user.

0.2.8

Released at 29-10-2025

Summary

New features

  • Added a Waitress WSGI wrapper for production servers

Interface changes

  • [Breaking] Added additional CLI arguments threads and connections to the startup sequence

  • Simplified AlgorithmParameter syntax

Waitress WSGI wrapper

The Waitress WSGI wrapper is now used to run the application if debug is set to False. This should relieve issues experienced with the Flask development server, such as the lack of thread safety that could be observed when accessing the app from multiple sources simultaneously.

The wrapper is configured through the CLI arguments; in particular, threads and connections have been added. They control the number of threads and the maximum number of simultaneous connections, respectively. threads defaults to 8, and connections defaults to 100.

This is a breaking change. threads and connections are now required arguments of Launcher.run(...)

AlgorithmParameter syntax

The __getitem__ method of the AlgorithmParameters class has been implemented. Instead of accessing a parameter in key as algorithm_parameters._parameters[key].value, it can now be accessed as algorithm_parameters[key].

Note that this is optional, legacy syntax will still work.

0.2.7

Released at 27-10-2025

Summary

New features

  • Opened up compare page styling through style.css

  • The order of the main sections (side-by-side, compare, KPI cards, and details) are now configurable through the configuration dictionary

Interface changes

  • [Breaking] The side-by-side section of the compare page now passes "left" and "right" to the content function.

Bug fixes

  • MultiExtractor no longer uses the (previously renamed) extraction_message and extraction_success_message functions

Compare page configuration

The order of the main sections (side-by-side, compare, KPI cards, and details) are now configurable through the configuration dictionary. To configure, specify the list of component keys in the order you want them to appear in the compare page, and add it to the configuration dictionary with key performance_ordered_list_components. The expected keys are side-by-side, kpis, compare, and details. An example is shown below:

# framework configuration
configuration = {
    # ...
    "compare_ordered_list_components": [ 'side-by-side', 'kpis', 'compare', 'details']
    # ...
}

Side-by-side section

The side-by-side section of the compare page now passes "left" and "right" to the content function, which allows the scenario specific section to contain their own responsive elements, such as dropdown menus.

The content function signature has changed to include the side argument.

This is a breaking change. Content functions that expect only one argument will need to be updated.

Alternatively, **kwargs can be added to the function signature to allow for side to be passed and be robust for future expansion. See here for more details.

An example is shown below:

OLD

    @staticmethod
    def create_side_view(s: Scenario) -> html.Div:
    """ User defined function to create the side view of the compare page."""
        return html.Div(...)

NEW

    @staticmethod
    def create_side_view(s: Scenario, side: str) -> html.Div:
    """ User defined function to create the side view of the compare page."""
        return html.Div(...)

ALTERNATIVE

    @staticmethod
    def create_side_view(s: Scenario, **kwargs) -> html.Div:
    """ User defined function to create the side view of the compare page."""
        return html.Div(...)