featuremodel

The featuremodel feature owns the domain models that tie a dataset to its UVL files: FeatureModel, its metadata (FMMetaData) and its metrics (FMMetrics). It has almost no UI of its own — a single mostly-empty page at /featuremodel — because feature models are rendered where they matter, on the dataset detail page. What it contributes at runtime is the schema in the middle of the datasetfeaturemodelhubfile chain and the counters the public landing page shows. It lives in app/features/featuremodel/.

Table of contents

  1. What it does
  2. Routes
  3. Models
  4. Services and repositories
  5. Dependencies
  6. Templates and assets
  7. Tests
  8. Configuration

What it does

Every UVL file uploaded to uvlhub becomes one FeatureModel row inside a dataset, pointing at its FMMetaData (filename, title, description, publication type, tags, UVL version, authors) and at its stored files in hubfile. The rows are created by DataSetService.create_from_form during a dataset upload — this feature defines the tables and the services around them; the writing is driven from dataset.

FeatureModelService also feeds the statistics on the landing page: the number of feature models (counted in SQL with COUNT), and total feature model views and downloads, which it defines as the hubfile view and download totals and therefore delegates to HubfileService.

Routes

Real output of rosemary route:list for this blueprint:

Endpoint              Methods   Route
featuremodel.assets   GET       /featuremodel/<subfolder>/<filename>
featuremodel.index    GET       /featuremodel

/featuremodel is public, accepts only GET and renders featuremodel/index.html, a template whose content block is empty — the page exists as scaffolding, not as a destination. The real UI for feature models is the dataset detail page at /doi/<doi>/, which lists each model with its view, download and flamapy conversion actions.

Models

app/features/featuremodel/models.py defines:

Model Table Purpose
FeatureModel feature_model One UVL model. FK data_set_id into dataset’s data_set (not nullable), FK fm_meta_data_id. files relationship to hubfile’s Hubfile.
FMMetaData fm_meta_data uvl_filename, title, description, publication_type, DOI, tags, uvl_version, FK to FMMetrics.
FMMetrics fm_metrics solver / not_solver text columns.

The module imports Author and PublicationType from dataset.models at module level: FMMetaData.authors is a relationship over dataset’s author table (via its fm_meta_data_id column), and the publication type column reuses dataset’s enum. The schema tie is therefore bidirectional — feature_model has a foreign key into dataset’s data_set, and dataset’s author table has a foreign key into fm_meta_data. Neither feature’s tables can exist without the other.

Services and repositories

app/features/featuremodel/services.py:

Service Purpose
FeatureModelService count_feature_models (SQL COUNT), total_feature_model_views and total_feature_model_downloads, both delegated to HubfileService.
FMMetaDataService Thin CRUD wrapper over FMMetaDataRepository.

app/features/featuremodel/repositories.py holds FeatureModelRepository (whose count_feature_models uses func.count so the database does the counting) and FMMetaDataRepository, both extending BaseRepository.

FeaturemodelSeeder in seeders.py is an empty scaffold; the sample feature models are seeded by dataset’s DataSetSeeder.

Dependencies

Measured over production code only (tests excluded), featuremodel imports two other features:

Imports Where Why
dataset.models models.py, module level Author and PublicationType. This is a schema-level tie: featuremodel’s metadata is built out of dataset’s tables and enums, so the coupling exists before any request is served.
hubfile.services services.py Feature model view/download totals are defined as hubfile view/download totals.

Fan-in — five features import featuremodel:

Imported by For
dataset DataSetService uses FeatureModelRepository/FMMetaDataRepository directly (skipping this feature’s service); the seeder creates FeatureModel and FMMetaData rows.
explore Search joins over FeatureModel and FMMetaData.
hubfile HubfileRepository joins through FeatureModel to find a file’s dataset and owner.
public The landing page reads the counters from FeatureModelService.
zenodo upload_file takes a FeatureModel to know which UVL file to push.

Taken together: dataset, featuremodel and hubfile form a module-level dependency triangle — each of the three imports the other two. Together with auth they are the product’s core, and they are only meaningfully deployable as a unit.

Templates and assets

app/features/featuremodel/
├── templates/featuremodel/index.html   # empty content block
└── assets/js/scripts.js                # placeholder console.log

init_feature registers scripts.js with the framework asset registry, but the script is a one-line placeholder. The templates that actually display feature models belong to dataset (view_dataset.html).

Tests

One file per layer, marker set at module level:

app/features/featuremodel/tests/
├── test_unit.py          # pytest.mark.unit
├── test_repository.py    # pytest.mark.repository
├── test_service.py       # pytest.mark.service
├── test_integration.py   # pytest.mark.integration
└── test_selenium.py      # pytest.mark.e2e

The unit and repository layers cover the models and the SQL counting; the service layer checks that view and download totals really aggregate hubfile records; integration checks /featuremodel is public, renders its own template and rejects POST. Because the feature’s real UI lives on the dataset page, the e2e file drives /doi/<doi>/ and asserts that every feature model of the dataset is listed, scoped correctly, and offers its UVL and converted exports. Run one layer at a time:

rosemary test featuremodel --unit
rosemary test featuremodel --integration
rosemary test featuremodel --e2e

Configuration

grep os.getenv over the feature’s production code finds nothing: featuremodel reads no environment variables and ships no .env.example. Everything it needs at runtime comes through the database and the features around it.