Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Update parameter.add_data() functionality #112

Draft
wants to merge 34 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
5499b87
Make creation information a mixin
glatterf42 Apr 16, 2024
a2a63ff
Make name column a mixin
glatterf42 Apr 18, 2024
90b7105
Make optimization columns mixins
glatterf42 Apr 18, 2024
7b8e389
Inherit mixin requirements directly
glatterf42 Apr 18, 2024
4374241
Include optimization parameter basis (#79)
glatterf42 Jun 28, 2024
02e3f43
Fix references to DB filters in docs
glatterf42 Jul 3, 2024
33d186e
Streamline naming in tests
glatterf42 Jul 3, 2024
6910d9e
Fix and test parameter list and tabulate for specific runs
glatterf42 Aug 5, 2024
247b06c
Include Run-side of relationship
glatterf42 Aug 5, 2024
2d02e4f
Make indexset-creation a test utility
glatterf42 Aug 8, 2024
87dd645
Make new tests more efficient
glatterf42 Aug 9, 2024
94b337e
Incorporate changes from #110
glatterf42 Aug 22, 2024
8e737c6
Use pandas for updated add_data behaviour
glatterf42 Aug 27, 2024
52a07ed
Remove superfluous session.add() for parameter
glatterf42 Aug 29, 2024
5b488ab
Use core layer in core test
glatterf42 Aug 30, 2024
ce5ff14
Raise minimum pandas version to enable add_data upsert
glatterf42 Aug 30, 2024
b41eb28
Generalize UsageError for more optimization items
glatterf42 Sep 30, 2024
f160e9c
Use generalized UsageError for Table
glatterf42 Sep 30, 2024
a9e5ac8
Use own errors for Parameter
glatterf42 Sep 30, 2024
445cc16
Make optimization columns mixins
glatterf42 Apr 18, 2024
85c0bc2
Inherit mixin requirements directly
glatterf42 Apr 18, 2024
0b4896c
Include optimization parameter basis (#79)
glatterf42 Jun 28, 2024
a057ffe
Fix typo in docs
glatterf42 Aug 26, 2024
b44e56c
TEMPORARY Add test-parameter.data creation helper script
glatterf42 Aug 26, 2024
50a303d
Add test-parameter.data as csvs
glatterf42 Aug 26, 2024
0250108
Add test-parameter.data as fixture
glatterf42 Aug 26, 2024
4dd8103
Handle SQL events for temp tables
glatterf42 Aug 26, 2024
0f3a6b1
Add option to filter columns by optimization_item.id
glatterf42 Aug 26, 2024
4ac0650
Add equation__id and variable__id to column filtering
glatterf42 Aug 26, 2024
c303393
Add parameter.data validation for in-DB use
glatterf42 Aug 26, 2024
1e1fe65
Introduce new add_data()
glatterf42 Aug 26, 2024
bced419
Add benchmark test for parameter.add_data comparison
glatterf42 Aug 26, 2024
4fb9741
Add duplicate test for in-DB parameter.add_data
glatterf42 Aug 26, 2024
c6b29b9
Add 1e6 value benchmark
glatterf42 Oct 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/pytest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ jobs:
postgres-version: "16"
backend: "sqlite,rest-sqlite"
pandas-version: "2.1.3"
# pandas 2.0.0
# pandas 2.1.0
- python-version: "3.10"
with-pyarrow: true
postgres-version: "16"
backend: "sqlite,rest-sqlite"
pandas-version: "2.0.0"
pandas-version: "2.1.0"

name: py${{ matrix.python-version }} | backend=${{ matrix.backend }} | with-pyarrow=${{ matrix.with-pyarrow }} | pgsql=${{ matrix.postgres-version }} | pandas=${{ matrix.pandas-version }}
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion DEVELOPING.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ py.test
### Running tests with PostgreSQL

In order to run the local tests with PostgreSQL you'll need to have a local instance
of this database running. The easiest way to do this using a docker container.
of this database running. The easiest way to do this is using a docker container.

Using the official [`postgres`](https://hub.docker.com/_/postgres) image is recommended.
Get the latest version on you local machine using (having docker installed):
Expand Down
8 changes: 4 additions & 4 deletions ixmp4/__init__.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# flake8: noqa
import importlib.metadata

from ixmp4.core import IndexSet as IndexSet
from ixmp4.core import Model as Model
from ixmp4.core import Parameter as Parameter
from ixmp4.core import Platform as Platform
from ixmp4.core import Region as Region
from ixmp4.core import Run as Run
from ixmp4.core import Scalar as Scalar
from ixmp4.core import Scenario as Scenario
from ixmp4.core import Table as Table
from ixmp4.core import Unit as Unit
from ixmp4.core import Variable as Variable
from ixmp4.core import IndexSet as IndexSet
from ixmp4.core import Scalar as Scalar
from ixmp4.core import Table as Table
from ixmp4.core.exceptions import InconsistentIamcType as InconsistentIamcType
from ixmp4.core.exceptions import IxmpError as IxmpError
from ixmp4.core.exceptions import NotFound as NotFound
Expand Down
1 change: 1 addition & 0 deletions ixmp4/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from .optimization.indexset import IndexSet as IndexSet
from .optimization.scalar import Scalar as Scalar
from .optimization.table import Table as Table
from .optimization.parameter import Parameter as Parameter
from .platform import Platform as Platform
from .region import Region as Region
from .run import Run as Run
Expand Down
7 changes: 2 additions & 5 deletions ixmp4/core/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,6 @@ class OptimizationDataValidationError(IxmpError):
http_error_name = "optimization_data_validation_error"


# == Optimization.Table ==


class OptimizationTableUsageError(IxmpError):
class OptimizationItemUsageError(IxmpError):
http_status_code = 422
http_error_name = "optimization_table_usage_error"
http_error_name = "optimization_item_usage_error"
3 changes: 3 additions & 0 deletions ixmp4/core/optimization/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from ..base import BaseFacade
from .indexset import IndexSetRepository
from .parameter import ParameterRepository
from .scalar import ScalarRepository
from .table import TableRepository

Expand All @@ -11,11 +12,13 @@ class OptimizationData(BaseFacade):
IndexSet, Table, Variable, etc."""

indexsets: IndexSetRepository
parameters: ParameterRepository
scalars: ScalarRepository
tables: TableRepository

def __init__(self, *args, run: Run, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.indexsets = IndexSetRepository(_backend=self.backend, _run=run)
self.parameters = ParameterRepository(_backend=self.backend, _run=run)
self.scalars = ScalarRepository(_backend=self.backend, _run=run)
self.tables = TableRepository(_backend=self.backend, _run=run)
133 changes: 133 additions & 0 deletions ixmp4/core/optimization/parameter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
from datetime import datetime
from typing import Any, ClassVar, Iterable

import pandas as pd

from ixmp4.core.base import BaseFacade, BaseModelFacade
from ixmp4.data.abstract import Docs as DocsModel
from ixmp4.data.abstract import Parameter as ParameterModel
from ixmp4.data.abstract import Run
from ixmp4.data.abstract.optimization import Column


class Parameter(BaseModelFacade):
_model: ParameterModel
NotFound: ClassVar = ParameterModel.NotFound
NotUnique: ClassVar = ParameterModel.NotUnique

@property
def id(self) -> int:
return self._model.id

@property
def name(self) -> str:
return self._model.name

@property
def run_id(self) -> int:
return self._model.run__id

@property
def data(self) -> dict[str, Any]:
return self._model.data

def add(self, data: dict[str, Any] | pd.DataFrame) -> None:
"""Adds data to an existing Parameter."""
self.backend.optimization.parameters.add_data(
parameter_id=self._model.id, data=data
)
self._model.data = self.backend.optimization.parameters.get(
run_id=self._model.run__id, name=self._model.name
).data

@property
def values(self) -> list:
return self._model.data.get("values", [])

@property
def units(self) -> list:
return self._model.data.get("units", [])

@property
def constrained_to_indexsets(self) -> list[str]:
return [column.indexset.name for column in self._model.columns]

@property
def columns(self) -> list[Column]:
return self._model.columns

@property
def created_at(self) -> datetime | None:
return self._model.created_at

@property
def created_by(self) -> str | None:
return self._model.created_by

@property
def docs(self):
try:
return self.backend.optimization.parameters.docs.get(self.id).description
except DocsModel.NotFound:
return None

@docs.setter
def docs(self, description):
if description is None:
self.backend.optimization.parameters.docs.delete(self.id)
else:
self.backend.optimization.parameters.docs.set(self.id, description)

@docs.deleter
def docs(self):
try:
self.backend.optimization.parameters.docs.delete(self.id)
# TODO: silently failing
except DocsModel.NotFound:
return None

def __str__(self) -> str:
return f"<Parameter {self.id} name={self.name}>"


class ParameterRepository(BaseFacade):
_run: Run

def __init__(self, _run: Run, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self._run = _run

def create(
self,
name: str,
constrained_to_indexsets: list[str],
column_names: list[str] | None = None,
) -> Parameter:
model = self.backend.optimization.parameters.create(
name=name,
run_id=self._run.id,
constrained_to_indexsets=constrained_to_indexsets,
column_names=column_names,
)
return Parameter(_backend=self.backend, _model=model)

def get(self, name: str) -> Parameter:
model = self.backend.optimization.parameters.get(run_id=self._run.id, name=name)
return Parameter(_backend=self.backend, _model=model)

def list(self, name: str | None = None) -> Iterable[Parameter]:
parameters = self.backend.optimization.parameters.list(
run_id=self._run.id, name=name
)
return [
Parameter(
_backend=self.backend,
_model=i,
)
for i in parameters
]

def tabulate(self, name: str | None = None) -> pd.DataFrame:
return self.backend.optimization.parameters.tabulate(
run_id=self._run.id, name=name
)
3 changes: 2 additions & 1 deletion ixmp4/data/abstract/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
This module holds a shared datastructure and interface for normalization
between the database and api data models and repositories.
"""
# flake8: noqa

from .base import (
BaseMeta,
Expand Down Expand Up @@ -32,6 +31,8 @@
from .optimization import (
IndexSet,
IndexSetRepository,
Parameter,
ParameterRepository,
Scalar,
ScalarRepository,
Table,
Expand Down
1 change: 1 addition & 0 deletions ixmp4/data/abstract/optimization/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .column import Column
from .indexset import IndexSet, IndexSetRepository
from .parameter import Parameter, ParameterRepository
from .scalar import Scalar, ScalarRepository
from .table import Table, TableRepository
4 changes: 3 additions & 1 deletion ixmp4/data/abstract/optimization/column.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ class Column(base.BaseModel, Protocol):
"""Unique name of the Column."""
dtype: types.String
"""Type of the Column's data."""
table__id: types.Integer
table__id: types.Mapped[int | None]
"""Foreign unique integer id of a Table."""
parameter__id: types.Mapped[int | None]
"""Foreign unique integer id of a Parameter."""
indexset: types.Mapped[IndexSet]
"""Associated IndexSet."""
constrained_to_indexset: types.Integer
Expand Down
4 changes: 2 additions & 2 deletions ixmp4/data/abstract/optimization/indexset.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def list(self, *, name: str | None = None, **kwargs) -> list[IndexSet]:
# TODO: Update kwargs
\*\*kwargs: any
More filter parameters as specified in
`ixmp4.data.db.iamc.variable.filters.VariableFilter`.
`ixmp4.data.db.optimization.indexset.filter.OptimizationIndexSetFilter`.

Returns
-------
Expand All @@ -112,7 +112,7 @@ def tabulate(self, *, name: str | None = None, **kwargs) -> pd.DataFrame:
# TODO: Update kwargs
\*\*kwargs: any
More filter parameters as specified in
`ixmp4.data.db.iamc.variable.filters.VariableFilter`.
`ixmp4.data.db.optimization.indexset.filter.OptimizationIndexSetFilter`.

Returns
-------
Expand Down
Loading
Loading