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 Use entity rather than object and relationship #216

Closed
wants to merge 74 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
a4a685c
Introduce the dimension and element concepts
manuelma Feb 8, 2023
be03b61
Select and insert stuff objects and relationships
manuelma Feb 8, 2023
cbaf3b2
Also update and remove objects and relationships via entities
manuelma Feb 8, 2023
285771e
Add migration script to drop object and relationship
manuelma Feb 9, 2023
7571279
Update DBCache to work with entity rather than object and relationship
manuelma Feb 9, 2023
043bc13
Fix entity queries
manuelma Feb 13, 2023
8b8e021
Initial support to add and update entities
manuelma Feb 14, 2023
5c814e5
Update export/import functions to work with entity
manuelma Feb 14, 2023
9a30d04
Fix import entities
manuelma Feb 14, 2023
f6faf5c
Introduce entity byname
manuelma Feb 15, 2023
d1d51eb
Merge branch 'master' into issue_215_drop_object_and_relationship
manuelma Feb 15, 2023
e4011a5
Merge branch 'master' into issue_215_drop_object_and_relationship
manuelma Feb 16, 2023
0b18887
Merge branch 'master' into issue_215_drop_object_and_relationship
manuelma Mar 13, 2023
504baf6
Fix some tests
manuelma Mar 13, 2023
5070d7c
Fix lots of tests and missing bits
manuelma Mar 29, 2023
c80717f
Merge branch 'master' into issue_215_drop_object_and_relationship
manuelma Mar 29, 2023
1bc8aff
Fix more tests
manuelma Mar 29, 2023
3e41455
Fix remove_items and descendant_tablenames to use entity
manuelma Mar 29, 2023
c6c9b53
Fix a couple more tests
manuelma Mar 29, 2023
e0532cc
Fix details in import entities etc
manuelma Mar 30, 2023
7131aea
Adapt ImportMapping to the entity structure
manuelma Mar 31, 2023
daaee5c
Adapt part of the ExportMapping to entity design
manuelma Apr 1, 2023
676b88a
Fix a couple more tests
manuelma Apr 1, 2023
b54242f
Some renaming and fix export entity classes
manuelma Apr 2, 2023
8e73a80
Introduce id generator and order entities in import mapping so it works
manuelma Apr 5, 2023
703e8c1
Fix tests
manuelma Apr 5, 2023
0c93654
Remove print
manuelma Apr 5, 2023
19571f9
Fix importing entities
manuelma Apr 5, 2023
ab63397
Fix one more test
manuelma Apr 5, 2023
d907349
Improve check for dimension count in export_mapping.EntityMapping
manuelma Apr 5, 2023
11ab458
Merge remote-tracking branch 'origin/master' into issue_215_drop_obje…
manuelma Apr 5, 2023
9613863
Fix migration script to ignore non existing constraints
manuelma Apr 5, 2023
b9202a8
Fix import entities
manuelma Apr 5, 2023
40edc28
Introduce dry_run argument as replacement for committing
manuelma Apr 5, 2023
9d1c2d5
Fix typo
manuelma Apr 12, 2023
0098905
Merge branch 'master' into issue_215_drop_object_and_relationship
manuelma Apr 21, 2023
05caf49
Sort entities and classes by dimensionality in export data
manuelma Apr 24, 2023
b73b4b6
WIP: introduce entity_alternative
manuelma May 2, 2023
091b394
Rationalize the use of the cache
manuelma May 3, 2023
c91c7b1
Move fetching to DBCache, get rid of Diff, commit from cache
manuelma May 5, 2023
2a9fc0c
Rationalize integrity checks
manuelma May 9, 2023
6cc604f
Get rid of the session attribute, implement our own Query
manuelma May 10, 2023
6310e41
Complete previous commit
manuelma May 10, 2023
d8b5550
Introduce Status to cache items, complete Query, fix a lot of tests
manuelma May 12, 2023
1323f16
Merge branch 'master' into issue_215_drop_object_and_relationship
manuelma May 12, 2023
f7bc863
Introduce _TempId to generate uncommitted ids without the DB
manuelma May 16, 2023
00ed8b3
Fix various bugs found via tests
manuelma May 16, 2023
7d4d0fe
Get rid of DBMappingBase.connection and move asynch back to toolbox
manuelma May 17, 2023
7525f0a
Add commit_id to committed items
manuelma May 17, 2023
bb3556d
Merge branch 'master' into issue_215_drop_object_and_relationship
soininen May 22, 2023
3b0e110
Fix tests
manuelma May 22, 2023
757a4b1
Merge branch 'issue_215_drop_object_and_relationship' of github.com:s…
soininen May 22, 2023
a0efed6
Merge branch 'issue_215_drop_object_and_relationship' of github.com:s…
soininen May 22, 2023
3172ee1
Black formatting
soininen May 22, 2023
8e95ce8
Implement rollback
manuelma May 23, 2023
4c6a1ea
Import zero dim entities before the others, so it works
manuelma May 23, 2023
c29b4b6
Consolidate DatabaseMapping tests
manuelma May 23, 2023
68f3ffe
Implement refresh_session, fix query and add rollback_session tests
manuelma May 23, 2023
4da0da2
Fix minor things and add tests for refresh_session
manuelma May 24, 2023
1ff78a4
Fix refresh and implement dirty_ids
manuelma May 24, 2023
c483aca
Fix management of unique keys
manuelma May 24, 2023
69d15ce
Document CacheItemBase
manuelma May 25, 2023
2a2c7a6
Fix db server query entry point
manuelma May 25, 2023
cbbb1d6
Merge remote-tracking branch 'origin/master' into issue_215_drop_obje…
manuelma Jun 1, 2023
ad5ec65
Fix calling callbacks while others are added from another thread
manuelma Jun 12, 2023
7400979
Fix some issues with temp id replacement
manuelma Jun 14, 2023
176cac4
Return layout coordinates
manuelma Jun 14, 2023
13c8238
Fix CacheItem status change
manuelma Jun 14, 2023
a33d529
Fix issues with list_value_id
manuelma Jun 14, 2023
9fb98cf
Make cascade restore sensitive to the removal source
manuelma Jun 15, 2023
61de5ff
Fix ParameterValue unique key
manuelma Jun 20, 2023
7a276ae
Merge branch 'master' into issue_215_drop_object_and_relationship
manuelma Jul 4, 2023
36acf4f
Simplify TempId resolving and parameter queries
manuelma Jul 13, 2023
60f7134
Merge branch 'master' into issue_215_drop_object_and_relationship
manuelma Aug 29, 2023
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
26 changes: 4 additions & 22 deletions spinedb_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
######################################################################################################################

from .db_mapping import DatabaseMapping
from .diff_db_mapping import DiffDatabaseMapping
from .exception import (
SpineDBAPIError,
SpineIntegrityError,
Expand All @@ -33,21 +32,13 @@
forward_sweep,
Asterisk,
)
from .check_functions import (
check_alternative,
check_scenario,
check_scenario_alternative,
check_object_class,
check_object,
check_wide_relationship_class,
check_wide_relationship,
check_parameter_definition,
check_parameter_value,
check_parameter_value_list,
)
from .import_functions import (
import_alternatives,
import_data,
import_entity_classes,
import_entities,
import_parameter_definitions,
import_parameter_values,
import_object_classes,
import_objects,
import_object_parameters,
Expand All @@ -59,10 +50,6 @@
import_relationships,
import_scenarios,
import_scenario_alternatives,
import_tools,
import_features,
import_tool_features,
import_tool_feature_methods,
import_metadata,
import_object_metadata,
import_relationship_metadata,
Expand All @@ -84,10 +71,6 @@
export_relationships,
export_scenario_alternatives,
export_scenarios,
export_tools,
export_features,
export_tool_features,
export_tool_feature_methods,
)
from .import_mapping.import_mapping_compat import import_mapping_from_dict
from .import_mapping.generator import get_mapped_data
Expand All @@ -114,7 +97,6 @@
)
from .filters.alternative_filter import apply_alternative_filter_to_parameter_value_sq
from .filters.scenario_filter import apply_scenario_filter_to_subqueries
from .filters.tool_filter import apply_tool_filter_to_entity_sq
from .filters.execution_filter import apply_execution_filter
from .filters.renamer import apply_renaming_to_parameter_definition_sq, apply_renaming_to_entity_class_sq
from .filters.tools import (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
"""drop_object_and_relationship_tables

Revision ID: 6b7c994c1c61
Revises: 989fccf80441
Create Date: 2023-02-09 06:48:46.585108

"""
from alembic import op
import sqlalchemy as sa
from spinedb_api.helpers import naming_convention

# revision identifiers, used by Alembic.
revision = '6b7c994c1c61'
down_revision = '989fccf80441'
branch_labels = None
depends_on = None


def upgrade():
op.create_table(
'entity_class_dimension',
sa.Column('entity_class_id', sa.Integer(), nullable=False),
sa.Column('dimension_id', sa.Integer(), nullable=False),
sa.Column('position', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(
['dimension_id'],
['entity_class.id'],
name=op.f('fk_entity_class_dimension_dimension_id_entity_class'),
onupdate='CASCADE',
ondelete='CASCADE',
),
sa.ForeignKeyConstraint(
['entity_class_id'],
['entity_class.id'],
name=op.f('fk_entity_class_dimension_entity_class_id_entity_class'),
onupdate='CASCADE',
ondelete='CASCADE',
),
sa.PrimaryKeyConstraint('entity_class_id', 'dimension_id', 'position', name=op.f('pk_entity_class_dimension')),
sa.UniqueConstraint('entity_class_id', 'dimension_id', 'position', name='uq_entity_class_dimension'),
)
op.create_table(
'entity_element',
sa.Column('entity_id', sa.Integer(), nullable=False),
sa.Column('entity_class_id', sa.Integer(), nullable=False),
sa.Column('element_id', sa.Integer(), nullable=False),
sa.Column('dimension_id', sa.Integer(), nullable=False),
sa.Column('position', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(
['element_id', 'dimension_id'],
['entity.id', 'entity.class_id'],
name=op.f('fk_entity_element_element_id_entity'),
onupdate='CASCADE',
ondelete='CASCADE',
),
sa.ForeignKeyConstraint(
['entity_class_id', 'dimension_id', 'position'],
[
'entity_class_dimension.entity_class_id',
'entity_class_dimension.dimension_id',
'entity_class_dimension.position',
],
name=op.f('fk_entity_element_entity_class_id_entity_class_dimension'),
onupdate='CASCADE',
ondelete='CASCADE',
),
sa.ForeignKeyConstraint(
['entity_id', 'entity_class_id'],
['entity.id', 'entity.class_id'],
name=op.f('fk_entity_element_entity_id_entity'),
onupdate='CASCADE',
ondelete='CASCADE',
),
sa.PrimaryKeyConstraint('entity_id', 'position', name=op.f('pk_entity_element')),
)
_persist_data()
# NOTE: some constraints are only created by the create_new_spine_database() function,
# not by the corresponding migration script. Thus we need to check before removing those constraints.
# We should avoid this in the future.
entity_class_constraints, entity_constraints = _get_constraints()
with op.batch_alter_table("entity", naming_convention=naming_convention) as batch_op:
for cname in ('uq_entity_idclass_id', 'uq_entity_idtype_idclass_id'):
if cname in entity_constraints:
batch_op.drop_constraint(cname, type_='unique')
batch_op.drop_constraint('fk_entity_type_id_entity_type', type_='foreignkey')
batch_op.drop_column('type_id')
with op.batch_alter_table("entity_class", naming_convention=naming_convention) as batch_op:
for cname in ('uq_entity_class_idtype_id', 'uq_entity_class_type_idname'):
if cname in entity_class_constraints:
batch_op.drop_constraint(cname, type_='unique')
batch_op.drop_constraint('fk_entity_class_type_id_entity_class_type', type_='foreignkey')
batch_op.drop_constraint('fk_entity_class_commit_id_commit', type_='foreignkey')
batch_op.drop_column('commit_id')
batch_op.drop_column('type_id')
op.drop_table('object_class')
op.drop_table('entity_class_type')
# op.drop_table('next_id')
op.drop_table('object')
op.drop_table('relationship_entity_class')
op.drop_table('relationship')
op.drop_table('entity_type')
op.drop_table('relationship_class')
op.drop_table('relationship_entity')


def _get_constraints():
conn = op.get_bind()
meta = sa.MetaData(conn)
meta.reflect()
return [[c.name for c in meta.tables[tname].constraints] for tname in ["entity_class", "entity"]]


def _persist_data():
conn = op.get_bind()
meta = sa.MetaData(conn)
meta.reflect()
ecd_items = [
{"entity_class_id": x["entity_class_id"], "dimension_id": x["member_class_id"], "position": x["dimension"]}
for x in conn.execute("SELECT * FROM relationship_entity_class")
]
ee_items = [
{
"entity_id": x["entity_id"],
"entity_class_id": x["entity_class_id"],
"element_id": x["member_id"],
"dimension_id": x["member_class_id"],
"position": x["dimension"],
}
for x in conn.execute("SELECT * FROM relationship_entity")
]
op.bulk_insert(meta.tables["entity_class_dimension"], ecd_items)
op.bulk_insert(meta.tables["entity_element"], ee_items)


def downgrade():
pass
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@
from sqlalchemy import MetaData
from sqlalchemy.sql.expression import bindparam
from sqlalchemy.orm import sessionmaker
from spinedb_api.check_functions import (
replace_default_values_with_list_references,
replace_parameter_values_with_list_references,
)
from spinedb_api.parameter_value import from_database
from spinedb_api.parameter_value import dump_db_value, from_database, ParameterValueFormatError
from spinedb_api.helpers import group_concat
from spinedb_api.exception import SpineIntegrityError

Expand Down Expand Up @@ -71,3 +67,51 @@ def upgrade():

def downgrade():
pass


def replace_default_values_with_list_references(item, parameter_value_lists, list_values):
parameter_value_list_id = item.get("parameter_value_list_id")
return _replace_values_with_list_references(
"parameter_definition", item, parameter_value_list_id, parameter_value_lists, list_values
)


def replace_parameter_values_with_list_references(item, parameter_definitions, parameter_value_lists, list_values):
parameter_definition_id = item["parameter_definition_id"]
parameter_definition = parameter_definitions[parameter_definition_id]
parameter_value_list_id = parameter_definition["parameter_value_list_id"]
return _replace_values_with_list_references(
"parameter_value", item, parameter_value_list_id, parameter_value_lists, list_values
)


def _replace_values_with_list_references(item_type, item, parameter_value_list_id, parameter_value_lists, list_values):
if parameter_value_list_id is None:
return False
if parameter_value_list_id not in parameter_value_lists:
raise SpineIntegrityError("Parameter value list not found.")
value_id_list = parameter_value_lists[parameter_value_list_id]
if value_id_list is None:
raise SpineIntegrityError("Parameter value list is empty!")
value_key, type_key = {
"parameter_value": ("value", "type"),
"parameter_definition": ("default_value", "default_type"),
}[item_type]
value = dict.get(item, value_key)
value_type = dict.get(item, type_key)
try:
parsed_value = from_database(value, value_type)
except ParameterValueFormatError as err:
raise SpineIntegrityError(f"Invalid {value_key} '{value}': {err}") from None
if parsed_value is None:
return False
list_value_id = next((id_ for id_ in value_id_list if list_values.get(id_) == parsed_value), None)
if list_value_id is None:
valid_values = ", ".join(f"{dump_db_value(list_values.get(id_))[0].decode('utf8')!r}" for id_ in value_id_list)
raise SpineIntegrityError(
f"Invalid {value_key} '{parsed_value}' - it should be one from the parameter value list: {valid_values}."
)
item[value_key] = str(list_value_id).encode("UTF8")
item[type_key] = "list_value_ref"
item["list_value_id"] = list_value_id
return True
Loading