Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
95 commits
Select commit Hold shift + click to select a range
c5bbc0e
move compatibility scripts to their own folder
May 21, 2025
f114d29
add parameter_value reencoding
May 21, 2025
304b257
add alembic migration script
May 21, 2025
3ab46be
spinedb_api/compat/: remove unused methods
suvayu May 21, 2025
0872ab0
spinedb_api/compat/: fix type hints
suvayu May 21, 2025
8bda858
spinedb_api/compat/: remove PEP 723 metadata
suvayu May 21, 2025
06db77a
spinedb_api/compat/: fix input type
May 23, 2025
0c7dd8e
add developer's documentation for data transition
May 23, 2025
a7f2140
alembic: blacken reencode_parameter_values.py
suvayu May 26, 2025
463767d
alembic: partly handle type column in reencode_parameter_values.py
suvayu May 26, 2025
3533c2c
models.py: remove bytes, since pa.UnionArray is now supported
suvayu May 26, 2025
7baf441
models.py: remove last remaining bits for bytes support
suvayu May 29, 2025
c5a41a2
models.py: fix TimePattern
suvayu May 29, 2025
acff451
models.py: add 'any_array' to support nullable mixed types
suvayu May 29, 2025
4841a6f
data_transition: fix missing import TimePattern
suvayu May 29, 2025
287851d
data_transition: alternate implementation
suvayu May 29, 2025
8ce8086
alembic: improve data migration script
Jun 2, 2025
7c2e269
models.py: remove last remaining bits for bytes support
suvayu May 29, 2025
1c6001d
models.py: fix 'any_array'
suvayu May 29, 2025
47d79a5
models.py: cleanup type aliases
suvayu May 30, 2025
fa25066
models.py: use pydantic dataclasses
suvayu May 30, 2025
811e263
compat: factor out some array creation to encode.py (WIP)
suvayu Jun 5, 2025
8d480ac
compat: make warning in make_columns more prominent
suvayu Jun 5, 2025
80749bd
data-transition: use relativedelta instead of pandas.DateOffset
suvayu Jun 5, 2025
57b78c4
models.py: model duration with relativedelta from dateutil
suvayu Jun 5, 2025
4b8ea09
compat/encode.py: change sentinel implementation
suvayu Jun 5, 2025
ae1f30f
compat/encode.py: fix column to array conversion for all types
suvayu Jun 5, 2025
4c8a990
compat/encode.py: drop all conversion from dataframe
suvayu Jun 5, 2025
4b11c43
pyproject.toml: upgrade pyarrow version, explicitly require pydantic
suvayu Jun 5, 2025
102bde2
compat: rename module for clarity
suvayu Jun 5, 2025
b375ce8
README_dev.md: update renamed module
suvayu Jun 5, 2025
0ac0896
compat: move duration parsing to own module for reusability
suvayu Jun 6, 2025
7d6053e
models.py: minor fix to import
suvayu Jun 7, 2025
4e767cc
models.py: cleaner implementation for time-pattern
suvayu Jun 7, 2025
5af2dd2
data_transition: fix old -> new format conversions
suvayu Jun 7, 2025
e4ffc8a
data_transition: fix module import formatting
suvayu Jun 7, 2025
51a33da
compat/converters.py: converters for duration types
suvayu Jun 8, 2025
02131e5
models.py: function to convert dict to array dataclasses
suvayu Jun 12, 2025
bfa64ea
arrow_value.py: function to convert array dataclasses to pyarrow
suvayu Jun 12, 2025
4f95a91
{arrow_values,models}.py: convert any array to union array
suvayu Jun 14, 2025
21122be
Merge branch 'master' into WIP-data-transition
soininen Jun 27, 2025
145faf1
compat/encode.py: bugfix, variables weren't named correctly
suvayu Jul 9, 2025
9115bc4
compat: fix formatter doc string, and minor formatting fix
suvayu Jul 11, 2025
ee03a13
{models,compat/encode}.py: fix conversion from pandas.Timestamp
suvayu Jul 11, 2025
759b563
to_database() for new array JSON
soininen Jun 30, 2025
6936c61
Add durations as acceptable values for value column
soininen Jul 24, 2025
5d95640
Make durations and datetimes work in run end encoded and dictionary a…
soininen Jul 24, 2025
d45575e
Fix using pd.TimeStamps or datetimes in IndexArray and the like
soininen Jul 25, 2025
c31bbdf
Apply new value JSON migration to all relevant tables
soininen Jul 25, 2025
e56e44f
Fix Alembic migration
soininen Jul 25, 2025
b70569d
Fix Alembic migration
soininen Jul 25, 2025
b153481
Merge branch 'master' into WIP-data-transition
soininen Aug 21, 2025
3a9d259
Add to_arrow() method to ParameterValue and its subclasses
soininen Aug 21, 2025
136f762
Always store new tabular JSON in database
soininen Aug 29, 2025
3fab999
Implement migration to tabular JSON while keeping values backwards co…
soininen Aug 29, 2025
2de12c8
Fix Alembic migration
soininen Aug 29, 2025
e9f8c99
Fix arrow_value.from_database()
soininen Aug 29, 2025
8c1603d
data_transition.py: remove experimentation converter
suvayu Aug 29, 2025
90c2d29
models.py: remove PEP 723 script, now depends on spinedb_api
suvayu Aug 29, 2025
54338d1
models.py: one import per line
suvayu Aug 30, 2025
aeedea7
converters.py: refactor relativedelta <-> JSON duration conversion
suvayu Aug 30, 2025
765a729
models.py: fix type annotations
suvayu Aug 30, 2025
d3ef8f6
models.py: remove custom validation & conversion
suvayu Aug 30, 2025
1172e89
models.py: change to schema generation using TypeAdapter (recommended)
suvayu Aug 30, 2025
023130d
models.py: refactor (de)serialisation
suvayu Aug 30, 2025
cfc61c6
data_transition.py: use models.TimePeriod to wrap time-patterns
suvayu Aug 30, 2025
d2dc941
data_transition.py: don't name date-time/duration as "value"
suvayu Aug 30, 2025
d946df8
models.py: fix metadata handling after rebase
suvayu Aug 30, 2025
2c696f2
models.py: allow time_period in any array for consistency
suvayu Aug 31, 2025
81de0e4
models.py: add missing mode in to_json
suvayu Aug 31, 2025
4ce80aa
Merge branch 'master' into WIP-data-transition
soininen Sep 1, 2025
ebf335a
models.py: fix typeddict, any array does not have value_type
suvayu Sep 1, 2025
97d93da
models.py: remove time_period from any array
suvayu Sep 1, 2025
6f81265
models.py: fix json serialization call
suvayu Sep 1, 2025
143f367
compat/converters.py: add pa.MonthDayNano -> duration/relativedelta
suvayu Sep 1, 2025
01fbd13
compat/converters.py: bug fix pa.MonthDayNano -> intermediate dict
suvayu Sep 1, 2025
6e4a3e9
Allow null values in index arrays, fix unit tests
soininen Sep 1, 2025
89f3808
Remove unused test_data_transition.py
soininen Sep 2, 2025
78002e6
Merge branch 'master' into WIP-data-transition
soininen Sep 3, 2025
1ad08e5
value_support.py: backwards compat fix load_db_value signature
suvayu Sep 8, 2025
117edde
Improve parameter value compatibility & refactoring
soininen Sep 5, 2025
deaa13a
Merge branch 'master' into WIP-data-transition
soininen Sep 11, 2025
482c322
Bump DB server version to 9
soininen Sep 11, 2025
67537c2
Merge branch 'master' into WIP-data-transition
soininen Sep 14, 2025
31588a0
Fix GAMS version check when current work directory is read-only
soininen Sep 16, 2025
a6085da
Revert "Fix GAMS version check when current work directory is read-only"
soininen Sep 16, 2025
d3cb47b
Fix Alembic migration
soininen Sep 17, 2025
2bb0da9
Merge branch 'master' into WIP-data-transition
soininen Sep 17, 2025
fbeda3e
Merge branch 'master' into WIP-data-transition
soininen Sep 25, 2025
24eb6d0
Fix unit tests for Alembic migrations
soininen Sep 25, 2025
faa8a51
Merge branch 'master' into WIP-data-transition
soininen Oct 10, 2025
cfeac47
Store leaf TimeSeries indices to last column when expanding Maps.
soininen Sep 17, 2025
4159c5d
Rework Map.from_arrow() to handle more corner-cases
soininen Oct 14, 2025
cfd4475
Add support for converting Map's leafs from/to Arrays
soininen Oct 15, 2025
567e095
Merge branch 'master' into WIP-data-transition
soininen Nov 4, 2025
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Compiled python modules.
*.pyc
/.idea/
/.spyproject/
/docs/build/
/docs/source/autoapi/
/docs/source/db_mapping_schema.rst
Expand Down
24 changes: 24 additions & 0 deletions README_dev.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Developing Data Transition

## Testing `alembic` Migration

1. Edit `./spinedb_api/alembic.ini`, point `sqlalchemy.url` to a (copy of a) SQLite test database. ⚠️ Its data will be altered by the migration script.
1. Edit `./spinedb_api/alembic/versions/a973ab537da2_reencode_parameter_values.py` and temporarily change
```python
new_value = transition_data(old_value)
```
to
```python
new_value = b'prepend_me ' + old_value
```
1. Within the `./spinedb_api` folder, execute
```bash
alembic upgrade head
```
1. Open your SQLite test database in a database editor and check for changed `paramater_value`s.

## Developing the Data Transition Module

1. Edit `./spinedb_api/compat/data_transition.py` for development.
1. In a Python REPL, call its function `transition_data(old_json_bytes)` and check for correct output of our test cases.
1. Once this works, revert the changes of `./spinedb_api/alembic/versions/a973ab537da2_reencode_parameter_values.py` and test the above `alembic` migration again.
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ dependencies = [
"chardet >=4.0.0",
"PyMySQL[rsa] >=1.0.2",
"psycopg2-binary",
"pyarrow >= 19.0",
"pyarrow >= 20.0",
"pydantic >= 2",
"pandas >= 2.2.3",
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from alembic import op
import sqlalchemy as sa
import sqlalchemy.orm
from spinedb_api.compatibility import convert_tool_feature_method_to_active_by_default
from spinedb_api.compat.compatibility_transformations import convert_tool_feature_method_to_active_by_default

# revision identifiers, used by Alembic.
revision = "8b0eff478bcb"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
from sqlalchemy.sql.expression import bindparam
from spinedb_api.exception import SpineIntegrityError
from spinedb_api.helpers import group_concat
from spinedb_api.parameter_value import ParameterValueFormatError, dump_db_value, from_database
from spinedb_api.incomplete_values import dump_db_value
from spinedb_api.parameter_value import ParameterValueFormatError, from_database

# revision identifiers, used by Alembic.
revision = "989fccf80441"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"""reencode parameter_values

Revision ID: a973ab537da2
Revises: e9f2c2330cf8
Create Date: 2025-05-21 12:49:16.861670

"""

from typing import Any, Optional, SupportsFloat
from alembic import op
import sqlalchemy as sa
from spinedb_api.compat.converters import parse_duration
from spinedb_api.parameter_value import DateTime, Duration, from_dict, to_database
from spinedb_api.value_support import load_db_value

# revision identifiers, used by Alembic.
revision = "a973ab537da2"
down_revision = "e9f2c2330cf8"
branch_labels = None
depends_on = None


TYPES_TO_REENCODE = {"duration", "date_time", "time_pattern", "time_series", "array", "map"}


def upgrade():
conn = op.get_bind()
metadata = sa.MetaData()
metadata.reflect(bind=conn)
_upgrade_table_types(metadata.tables["parameter_definition"], "default_value", "default_type", conn)
_upgrade_table_types(metadata.tables["parameter_value"], "value", "type", conn)
_upgrade_table_types(metadata.tables["list_value"], "value", "type", conn)


def downgrade():
pass


def _upgrade_table_types(table, value_label, type_label, connection):
value_column = getattr(table.c, value_label)
type_column = getattr(table.c, type_label)
update_statement = (
table.update()
.where(table.c.id == sa.bindparam("id"))
.values(
{
"id": sa.bindparam("id"),
value_label: sa.bindparam(value_label),
}
)
)
batch_data = []
for id_, type_, old_blob in connection.execute(
sa.select(table.c.id, type_column, value_column).where(type_column.in_(TYPES_TO_REENCODE))
):
legacy_value = _from_database_legacy(old_blob, type_)
new_blob, _ = to_database(legacy_value)
batch_data.append({"id": id_, value_label: new_blob})
if len(batch_data) == 100:
connection.execute(update_statement, batch_data)
batch_data.clear()
if batch_data:
connection.execute(update_statement, batch_data)


def _from_database_legacy(value: bytes, type_: Optional[str]) -> Optional[Any]:
parsed = load_db_value(value)
if isinstance(parsed, dict):
return from_dict(parsed, type_)
if type_ == DateTime.TYPE:
return DateTime(parsed)
if type_ == Duration.TYPE:
return Duration(parse_duration(parsed))
raise RuntimeError(f"migration for {type_} missing")
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import json
from alembic import op
import sqlalchemy as sa
from spinedb_api.parameter_value import type_for_scalar
from spinedb_api.incomplete_values import type_for_scalar

# revision identifiers, used by Alembic.
revision = "ca7a13da8ff6"
Expand Down Expand Up @@ -40,7 +40,10 @@ def _update_scalar_type_info(table, value_label, type_label, connection):
parsed_value = json.loads(value)
if parsed_value is None:
continue
value_type = type_for_scalar(parsed_value)
if isinstance(parsed_value, dict):
value_type = parsed_value["type"]
else:
value_type = type_for_scalar(parsed_value)
connection.execute(update_statement.where(table.c.id == id_), {type_label: value_type})


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from alembic import op
import sqlalchemy as sa
from spinedb_api.compatibility import convert_tool_feature_method_to_entity_alternative
from spinedb_api.compat.compatibility_transformations import convert_tool_feature_method_to_entity_alternative

# revision identifiers, used by Alembic.
revision = "ce9faa82ed59"
Expand Down
Loading
Loading