From 7e034374aea9922b31404c494ccad725dcf481c5 Mon Sep 17 00:00:00 2001 From: Erkan Ozgur Yilmaz Date: Mon, 18 Nov 2024 11:46:28 +0000 Subject: [PATCH] [#112] Shot and Scene relation is now many-to-one. --- ...8697b5b_rename_depends_to_to_depends_on.py | 5 +- ...71fa852_scene_is_now_deriving_from_task.py | 36 +++--- ..._shot_scene_relation_is_now_many_to_one.py | 69 ++++++++++ ..._shot_sequence_relation_is_now_many_to_.py | 32 ++--- ...b9_rename_version_take_name_to_version_.py | 13 +- .../feca9bac7d5a_renamed_osx_to_macos.py | 5 +- examples/flat_project_example.py | 6 +- src/stalker/db/setup.py | 2 +- src/stalker/models/scene.py | 4 +- src/stalker/models/shot.py | 51 ++++---- src/stalker/models/task.py | 8 +- tests/db/test_db.py | 12 +- tests/models/test_asset.py | 8 +- tests/models/test_filename_template.py | 2 +- tests/models/test_scene.py | 4 +- tests/models/test_shot.py | 119 ++++++++++-------- tests/models/test_task.py | 4 +- tests/models/test_version.py | 18 +-- 18 files changed, 231 insertions(+), 167 deletions(-) create mode 100644 alembic/versions/5078390e5527_shot_scene_relation_is_now_many_to_one.py diff --git a/alembic/versions/019378697b5b_rename_depends_to_to_depends_on.py b/alembic/versions/019378697b5b_rename_depends_to_to_depends_on.py index 085099f8..7acc4557 100644 --- a/alembic/versions/019378697b5b_rename_depends_to_to_depends_on.py +++ b/alembic/versions/019378697b5b_rename_depends_to_to_depends_on.py @@ -10,10 +10,9 @@ import sqlalchemy as sa - # revision identifiers, used by Alembic. -revision = '019378697b5b' -down_revision = 'feca9bac7d5a' +revision = "019378697b5b" +down_revision = "feca9bac7d5a" def upgrade(): diff --git a/alembic/versions/4400871fa852_scene_is_now_deriving_from_task.py b/alembic/versions/4400871fa852_scene_is_now_deriving_from_task.py index 6eac444b..342a8117 100644 --- a/alembic/versions/4400871fa852_scene_is_now_deriving_from_task.py +++ b/alembic/versions/4400871fa852_scene_is_now_deriving_from_task.py @@ -12,10 +12,9 @@ import sqlalchemy as sa - # revision identifiers, used by Alembic. -revision = '4400871fa852' -down_revision = 'ec1eb2151bb9' +revision = "4400871fa852" +down_revision = "ec1eb2151bb9" def upgrade(): @@ -57,21 +56,24 @@ def upgrade(): ) ) # Insert the same data to the Entities - op.execute(""" + op.execute( + """ INSERT INTO "Entities" (id) VALUES ( (SELECT "SimpleEntities".id FROM "SimpleEntities" WHERE "SimpleEntities".name = 'Scene Statuses') ) - """) + """ + ) # Insert the same to the StatusLists op.execute( - """INSERT INTO "StatusLists" (id, target_entity_type) VALUES ( + """INSERT INTO "StatusLists" (id, target_entity_type) VALUES ( (SELECT "SimpleEntities".id FROM "SimpleEntities" WHERE "SimpleEntities".name = 'Scene Statuses'), 'Scene' ) - """) + """ + ) # Create the same StatusList -> Status relation of a Task op.execute( - """INSERT INTO "StatusList_Statuses" (status_list_id, status_id) + """INSERT INTO "StatusList_Statuses" (status_list_id, status_id) SELECT "SimpleEntities".id, "StatusList_Statuses".status_id @@ -87,7 +89,8 @@ def upgrade(): # we need create a Task for each Scene in the database, # with the same id of the Scene # carry on the data: id, project_id - op.execute(""" + op.execute( + """ INSERT INTO "Tasks" ( id, project_id, @@ -107,7 +110,8 @@ def upgrade(): 'effort', 0 FROM "Scenes" - """) + """ + ) # drop the project_id column in Scenes table with op.batch_alter_table("Scenes", schema=None) as batch_op: @@ -129,9 +133,7 @@ def downgrade(): # set the project_id column not nullable op.execute("""ALTER TABLE "Scenes" ALTER COLUMN project_id SET NOT NULL""") # Remove the scene entries from Tasks table - op.execute( - """DELETE FROM "Tasks" WHERE id IN (SELECT id FROM "Scenes")""" - ) + op.execute("""DELETE FROM "Tasks" WHERE id IN (SELECT id FROM "Scenes")""") # Remove the StatusList entries from StatusList_Statuses op.execute( """DELETE FROM "StatusList_Statuses" WHERE status_list_id = ( @@ -141,9 +143,7 @@ def downgrade(): """ ) # Remove the StatusList from StatusLists Table - op.execute( - """DELETE FROM "StatusLists" WHERE target_entity_type = 'Scene'""" - ) + op.execute("""DELETE FROM "StatusLists" WHERE target_entity_type = 'Scene'""") # Remove the StatusList from Entities Table op.execute( """DELETE FROM "Entities" WHERE id IN ( @@ -155,9 +155,7 @@ def downgrade(): """ ) # Remove the StatusList from SimpleEntities Table - op.execute( - """DELETE FROM "SimpleEntities" WHERE name = 'Scene Statuses'""" - ) + op.execute("""DELETE FROM "SimpleEntities" WHERE name = 'Scene Statuses'""") # Update the Scenes.id to be a foreign key to Entities.id op.drop_constraint("Scenes_id_fkey", "Scenes", type_="foreignkey") diff --git a/alembic/versions/5078390e5527_shot_scene_relation_is_now_many_to_one.py b/alembic/versions/5078390e5527_shot_scene_relation_is_now_many_to_one.py new file mode 100644 index 00000000..739d3a37 --- /dev/null +++ b/alembic/versions/5078390e5527_shot_scene_relation_is_now_many_to_one.py @@ -0,0 +1,69 @@ +"""Shot Scene relation is now many-to-one + +Revision ID: 5078390e5527 +Revises: e25ec9930632 +Create Date: 2024-11-18 11:35:10.872216 +""" + +from alembic import op + +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "5078390e5527" +down_revision = "e25ec9930632" + + +def upgrade(): + """Upgrade the tables.""" + # Add scene_id column + op.add_column("Shots", sa.Column("scene_id", sa.Integer(), nullable=True)) + + # Create foreign key constraint + op.create_foreign_key(None, "Shots", "Scenes", ["scene_id"], ["id"]) + + # Migrate the data + op.execute( + """UPDATE "Shots" SET scene_id = ( + SELECT scene_id + FROM "Shot_Scenes" + WHERE "Shot_Scenes".shot_id = "Shots".id LIMIT 1 + )""" + ) + + # Drop Shot_Scenes Table + op.execute("""DROP TABLE "Shot_Scenes" """) + + +def downgrade(): + """Downgrade the tables.""" + # Add Shot_Scenes Table + op.create_table( + "Shot_Scenes", + sa.Column("shot_id", sa.Integer(), nullable=False), + sa.Column("scene_id", sa.Integer(), nullable=False), + sa.ForeignKeyConstraint( + ["shot_id"], + ["Shots.id"], + ), + sa.ForeignKeyConstraint( + ["scene_id"], + ["Scenes.id"], + ), + ) + + # Transfer Data + op.execute( + """ + UPDATE "Shot_Scenes" SET shot_id, scene_id = ( + SELECT id, scene_id FROM "Shots" WHERE "Shots".scene_id != NULL + ) + """ + ) + + # Drop foreign key constraint + op.drop_constraint("Shots_scene_id_fkey", "Shots", type_="foreignkey") + + # drop Shots.scene_id column + op.drop_column("Shots", "scene_id") diff --git a/alembic/versions/e25ec9930632_shot_sequence_relation_is_now_many_to_.py b/alembic/versions/e25ec9930632_shot_sequence_relation_is_now_many_to_.py index 1729c7c6..106d1dc2 100644 --- a/alembic/versions/e25ec9930632_shot_sequence_relation_is_now_many_to_.py +++ b/alembic/versions/e25ec9930632_shot_sequence_relation_is_now_many_to_.py @@ -11,24 +11,26 @@ # revision identifiers, used by Alembic. -revision = 'e25ec9930632' -down_revision = '4400871fa852' +revision = "e25ec9930632" +down_revision = "4400871fa852" def upgrade(): """Upgrade the tables.""" - # Add sequence_id column - op.add_column('Shots', sa.Column('sequence_id', sa.Integer(), nullable=True)) + # Add sequence_id column + op.add_column("Shots", sa.Column("sequence_id", sa.Integer(), nullable=True)) - # Create foreign key constraint - op.create_foreign_key(None, 'Shots', 'Sequences', ['sequence_id'], ['id']) + # Create foreign key constraint + op.create_foreign_key(None, "Shots", "Sequences", ["sequence_id"], ["id"]) - # Migrate the data - op.execute("""UPDATE "Shots" SET sequence_id = ( + # Migrate the data + op.execute( + """UPDATE "Shots" SET sequence_id = ( SELECT sequence_id FROM "Shot_Sequences" WHERE "Shot_Sequences".shot_id = "Shots".id LIMIT 1 - )""") + )""" + ) # Drop Shot_Sequences Table op.execute("""DROP TABLE "Shot_Sequences" """) @@ -36,7 +38,7 @@ def upgrade(): def downgrade(): """Downgrade the tables.""" - # Add Shot_Sequences Table + # Add Shot_Sequences Table op.create_table( "Shot_Sequences", sa.Column("shot_id", sa.Integer(), nullable=False), @@ -51,15 +53,17 @@ def downgrade(): ), ) - # Transfer Data - op.execute(""" + # Transfer Data + op.execute( + """ UPDATE "Shot_Sequences" SET shot_id, sequence_id = ( SELECT id, sequence_id FROM "Shots" WHERE "Shots".sequence_id != NULL ) - """) + """ + ) # Drop foreign key constraint op.drop_constraint("Shots_sequence_id_fkey", "Shots", type_="foreignkey") # drop Shots.sequence_id column - op.drop_column("Shots", "sequence_id") \ No newline at end of file + op.drop_column("Shots", "sequence_id") diff --git a/alembic/versions/ec1eb2151bb9_rename_version_take_name_to_version_.py b/alembic/versions/ec1eb2151bb9_rename_version_take_name_to_version_.py index 8c35deb4..8b917fa0 100644 --- a/alembic/versions/ec1eb2151bb9_rename_version_take_name_to_version_.py +++ b/alembic/versions/ec1eb2151bb9_rename_version_take_name_to_version_.py @@ -10,21 +10,16 @@ import sqlalchemy as sa - # revision identifiers, used by Alembic. -revision = 'ec1eb2151bb9' -down_revision = '019378697b5b' +revision = "ec1eb2151bb9" +down_revision = "019378697b5b" def upgrade(): """Upgrade the tables.""" - op.alter_column( - "Versions", "take_name", new_column_name="variant_name" - ) + op.alter_column("Versions", "take_name", new_column_name="variant_name") def downgrade(): """Downgrade the tables.""" - op.alter_column( - "Versions", "variant_name", new_column_name="take_name" - ) + op.alter_column("Versions", "variant_name", new_column_name="take_name") diff --git a/alembic/versions/feca9bac7d5a_renamed_osx_to_macos.py b/alembic/versions/feca9bac7d5a_renamed_osx_to_macos.py index b858a75e..3f6550ed 100644 --- a/alembic/versions/feca9bac7d5a_renamed_osx_to_macos.py +++ b/alembic/versions/feca9bac7d5a_renamed_osx_to_macos.py @@ -10,10 +10,9 @@ import sqlalchemy as sa - # revision identifiers, used by Alembic. -revision = 'feca9bac7d5a' -down_revision = 'bf67e6a234b4' +revision = "feca9bac7d5a" +down_revision = "bf67e6a234b4" def upgrade(): diff --git a/examples/flat_project_example.py b/examples/flat_project_example.py index 6ab04882..a685790b 100644 --- a/examples/flat_project_example.py +++ b/examples/flat_project_example.py @@ -52,8 +52,8 @@ flat_struct = Structure( name="Flat Project Structure", templates=[flat_task_template] # we need another template for Assets, - # Shots and Sequences but I'm skipping it - # for now + # Shots and Sequences but I'm skipping it + # for now ) # query a couple of statuses @@ -155,7 +155,7 @@ # now create new tasks for the normal project seq1 = Sequence(name="Sequence", code="SEQ001", project=p2) -shot1 = Shot(name="SEQ001_0010", code="SEQ001_0010", parent=seq1, sequences=[seq1]) +shot1 = Shot(name="SEQ001_0010", code="SEQ001_0010", parent=seq1, sequence=seq1) comp = Task(name="Comp", parent=shot1) # you probably will supply a different name/code diff --git a/src/stalker/db/setup.py b/src/stalker/db/setup.py index aa6f9df0..737bb04d 100644 --- a/src/stalker/db/setup.py +++ b/src/stalker/db/setup.py @@ -36,7 +36,7 @@ logger: logging.Logger = log.get_logger(__name__) # TODO: Try to get it from the API (it was not working inside a package before) -alembic_version: str = "e25ec9930632" +alembic_version: str = "5078390e5527" def setup(settings: Optional[Dict[str, Any]] = None) -> None: diff --git a/src/stalker/models/scene.py b/src/stalker/models/scene.py index 07a8ff7d..acf6e610 100644 --- a/src/stalker/models/scene.py +++ b/src/stalker/models/scene.py @@ -40,8 +40,8 @@ class Scene(Task, CodeMixin): ) shots: Mapped[Optional[List["Shot"]]] = relationship( - secondary="Shot_Scenes", - back_populates="scenes", + primaryjoin="Shots.c.scene_id==Scenes.c.id", + back_populates="scene", doc="""The :class:`.Shot` s that is related with this Scene. It is a list of :class:`.Shot` instances. diff --git a/src/stalker/models/shot.py b/src/stalker/models/shot.py index 6a69fea3..56aa1fea 100644 --- a/src/stalker/models/shot.py +++ b/src/stalker/models/shot.py @@ -32,6 +32,13 @@ class Shot(Task, CodeMixin): """Manages Shot related data. + A shot is a continuous, unbroken sequence of images that makes up a single + part of a film. Shots are organized into :class:`.Sequence` s and + :class:`.Scene` s :class:`.Sequence` s group shots together based on time, + such as montage or flashback. :class:`.Scene` s group shots together based + on location and narrative, marking where and when a specific story event + occurs. + .. warning:: .. deprecated:: 0.1.2 @@ -80,14 +87,10 @@ class Shot(Task, CodeMixin): .. note:: - .. versionadded:: 0.2.0 + .. versionadded:: 1.0.0 - Shots now have a new attribute called ``scenes``, holding - :class:`.Scene` instances which is another grouping attribute like - ``sequence``. Where Sequence is grouping the Shots according to their - temporal position to each other, Scenes are grouping the Shots according - to their view to the world, that is shots taking place in the same set - configuration can be grouped together by using Scenes. + Shot and Scene relation is now many-to-one, meaning a Shot can only be + connected to a single Scene instance through the `Shot.scene` attribute. Two shots with the same :attr:`.code` cannot be assigned to the same :class:`.Sequence`. @@ -180,10 +183,10 @@ class Shot(Task, CodeMixin): back_populates="shots", ) - scenes: Mapped[Optional[List["Scene"]]] = relationship( - secondary="Shot_Scenes", - primaryjoin="Shots.c.id==Shot_Scenes.c.shot_id", - secondaryjoin="Shot_Scenes.c.scene_id==Scenes.c.id", + scene_id: Mapped[Optional[int]] = mapped_column(ForeignKey("Scenes.id")) + + scene: Mapped[Optional["Scene"]] = relationship( + primaryjoin="Shots.c.scene_id==Scenes.c.id", back_populates="shots", ) @@ -241,7 +244,7 @@ def __init__( code: Optional[str] = None, project: Optional["Project"] = None, sequence: Optional["Sequence"] = None, - scenes: Optional[List["Scene"]] = None, + scene: Optional["Scene"] = None, cut_in: Optional[int] = None, cut_out: Optional[int] = None, source_in: Optional[int] = None, @@ -262,11 +265,7 @@ def __init__( CodeMixin.__init__(self, **kwargs) self.sequence = sequence - - if scenes is None: - scenes = [] - self.scenes = scenes - + self.scene = scene self.image_format = image_format if cut_in is None: @@ -694,8 +693,8 @@ def _validate_sequence(self, key: str, sequence: "Sequence") -> "Sequence": ) return sequence - @validates("scenes") - def _validate_scenes(self, key: str, scene: "Scene") -> "Scene": + @validates("scene") + def _validate_scene(self, key: str, scene: "Scene") -> "Scene": """Validate the given scene value. Args: @@ -710,10 +709,10 @@ def _validate_scenes(self, key: str, scene: "Scene") -> "Scene": """ from stalker.models.scene import Scene - if not isinstance(scene, Scene): + if scene is not None and not isinstance(scene, Scene): raise TypeError( - f"{self.__class__.__name__}.scenes should all be " - "stalker.models.scene.Scene instances, " + f"{self.__class__.__name__}.scene should be a " + "stalker.models.scene.Scene instance, " f"not {scene.__class__.__name__}: '{scene}'" ) return scene @@ -795,11 +794,3 @@ def _validate_code(self, key: str, code: str) -> str: raise ValueError(f"There is a Shot with the same code: {code}") return code - - -Shot_Scenes = Table( - "Shot_Scenes", - Base.metadata, - Column("shot_id", Integer, ForeignKey("Shots.id"), primary_key=True), - Column("scene_id", Integer, ForeignKey("Scenes.id"), primary_key=True), -) diff --git a/src/stalker/models/task.py b/src/stalker/models/task.py index 176a737f..2560ae71 100644 --- a/src/stalker/models/task.py +++ b/src/stalker/models/task.py @@ -3090,12 +3090,12 @@ def _template_variables(self) -> dict: asset = None sequence = None - scenes = [] + scene = None shot = None if isinstance(self, Shot): shot = self sequence = self.sequence - scenes = self.scenes + scene = self.scene elif isinstance(self, Asset): asset = self else: @@ -3103,7 +3103,7 @@ def _template_variables(self) -> dict: for parent in self.parents: if isinstance(parent, Shot): sequence = parent.sequence - scenes = parent.scenes + scene = parent.scene break elif isinstance(parent, Asset): asset = parent @@ -3117,7 +3117,7 @@ def _template_variables(self) -> dict: return { "project": self.project, "sequence": sequence, - "scenes": scenes, + "scene": scene, "shot": shot, "asset": asset, "task": self, diff --git a/tests/db/test_db.py b/tests/db/test_db.py index affbff2e..c09c3bbd 100644 --- a/tests/db/test_db.py +++ b/tests/db/test_db.py @@ -3125,19 +3125,19 @@ def test_persistence_of_scene(setup_postgresql_db): shot1 = Shot( code="SH001", project=test_project1, - scenes=[test_scene], + scene=test_scene, responsible=[user1], ) shot2 = Shot( code="SH002", project=test_project1, - scenes=[test_scene], + scene=test_scene, responsible=[user1], ) shot3 = Shot( code="SH003", project=test_project1, - scenes=[test_scene], + scene=test_scene, responsible=[user1], ) DBSession.add_all([shot1, shot2, shot3]) @@ -3323,7 +3323,7 @@ def test_persistence_of_shot(setup_postgresql_db): "code": "SH001", "project": test_project1, "sequence": test_seq1, - "scenes": [test_sce1, test_sce2], + "scene": test_sce1, "status": 0, "responsible": [lead], } @@ -3346,7 +3346,7 @@ def test_persistence_of_shot(setup_postgresql_db): notes = test_shot.notes references = test_shot.references sequence = test_shot.sequence - scenes = test_shot.scenes + scene = test_shot.scene status = test_shot.status status_list = test_shot.status_list tags = test_shot.tags @@ -3371,7 +3371,7 @@ def test_persistence_of_shot(setup_postgresql_db): assert test_shot_db.nice_name == nice_name assert test_shot_db.notes == notes assert test_shot_db.references == references - assert test_shot_db.scenes == scenes + assert test_shot_db.scene == scene assert test_shot_db.sequence == sequence assert test_shot_db.status == status assert test_shot_db.status_list == status_list diff --git a/tests/models/test_asset.py b/tests/models/test_asset.py index 1c57a584..08f0816c 100644 --- a/tests/models/test_asset.py +++ b/tests/models/test_asset.py @@ -300,11 +300,11 @@ def test_template_variables_for_asset_related_task(setup_asset_tests): "asset": data["asset1"], "parent_tasks": [data["asset1"], data["task2"]], "project": data["project1"], - "scenes": [], + "scene": None, "sequence": None, "shot": None, "task": data["task2"], - "type": None + "type": None, } @@ -315,9 +315,9 @@ def test_template_variables_for_asset_itself(setup_asset_tests): "asset": data["asset1"], "parent_tasks": [data["asset1"]], "project": data["project1"], - "scenes": [], + "scene": None, "sequence": None, "shot": None, "task": data["asset1"], - "type": data["asset_type1"] + "type": data["asset_type1"], } diff --git a/tests/models/test_filename_template.py b/tests/models/test_filename_template.py index f78aac0d..addda202 100644 --- a/tests/models/test_filename_template.py +++ b/tests/models/test_filename_template.py @@ -341,7 +341,7 @@ def test_naming_case(setup_postgresql_db): c001 = Task(name="c001", parent=s001) DBSession.add(c001) - effects_scene = Task(name="effectScenes", parent=c001) + effects_scene = Task(name="effect scene", parent=c001) DBSession.add(effects_scene) fxA = Task(name="fxA", parent=effects_scene) diff --git a/tests/models/test_scene.py b/tests/models/test_scene.py index c269aef0..ecb04aca 100644 --- a/tests/models/test_scene.py +++ b/tests/models/test_scene.py @@ -120,7 +120,7 @@ def test_shots_attribute_elements_tried_to_be_set_to_non_shot_object( def test_equality(setup_scene_db_tests): - """equality of scenes.""" + """equality of scene instances.""" data = setup_scene_db_tests new_seq1 = Scene(**data["kwargs"]) new_seq2 = Scene(**data["kwargs"]) @@ -135,7 +135,7 @@ def test_equality(setup_scene_db_tests): def test_inequality(setup_scene_db_tests): - """inequality of scenes.""" + """inequality of scene instances.""" data = setup_scene_db_tests new_seq1 = Scene(**data["kwargs"]) new_seq2 = Scene(**data["kwargs"]) diff --git a/tests/models/test_shot.py b/tests/models/test_shot.py index adbe1814..021cb672 100644 --- a/tests/models/test_shot.py +++ b/tests/models/test_shot.py @@ -167,7 +167,7 @@ def setup_shot_db_tests(setup_postgresql_db): description="This is a test Shot", project=data["test_project1"], sequence=data["test_sequence1"], - scenes=[data["test_scene1"], data["test_scene2"]], + scene=data["test_scene1"], cut_in=112, cut_out=149, source_in=120, @@ -463,117 +463,126 @@ def test_sequence_attribute_is_working_as_expected(setup_shot_db_tests): assert new_shot.sequence == seq2 -def test_scenes_argument_is_skipped(setup_shot_db_tests): - """scenes attribute an empty list if the scenes argument is skipped.""" +def test_scene_argument_is_skipped(setup_shot_db_tests): + """scene attribute is None if the scene argument is skipped.""" data = setup_shot_db_tests - data["kwargs"].pop("scenes") + data["kwargs"].pop("scene") data["kwargs"]["code"] = "DifferentCode" new_shot = Shot(**data["kwargs"]) - assert new_shot.scenes == [] + assert new_shot.scene is None -def test_scenes_argument_is_None(setup_shot_db_tests): - """scenes attribute an empty list if the scenes argument is set to None.""" +def test_scene_argument_is_None(setup_shot_db_tests): + """scene attribute is None if the scene argument is set to None.""" data = setup_shot_db_tests - data["kwargs"]["scenes"] = None + data["kwargs"]["scene"] = None data["kwargs"]["code"] = "NewCode" new_shot = Shot(**data["kwargs"]) - assert new_shot.scenes == [] + assert new_shot.scene is None -def test_scenes_attribute_is_set_to_None(setup_shot_db_tests): - """TypeError raised if the scenes attribute is set to None.""" +def test_scene_attribute_is_set_to_None(setup_shot_db_tests): + """TypeError is not raised if the scene attribute is set to None.""" data = setup_shot_db_tests - with pytest.raises(TypeError) as cm: - data["test_shot"].scenes = None - - assert str(cm.value) == "Incompatible collection type: None is not list-like" + assert data["test_shot"].scene is not None + # no error should be raised + data["test_shot"].scene = None + assert data["test_shot"].scene is None -def test_scenes_argument_is_not_a_list(setup_shot_db_tests): - """TypeError raised if the scenes argument is not a list.""" +def test_scene_argument_is_not_a_scene(setup_shot_db_tests): + """TypeError raised if the scene argument is not a scene.""" data = setup_shot_db_tests - data["kwargs"]["scenes"] = "not a list" + data["kwargs"]["scene"] = "not a scene" data["kwargs"]["code"] = "NewCode" with pytest.raises(TypeError) as cm: Shot(**data["kwargs"]) - assert str(cm.value) == "Incompatible collection type: str is not list-like" + assert str(cm.value) == ( + "Shot.scene should be a stalker.models.scene.Scene instance, not str: " + "'not a scene'" + ) -def test_scenes_attribute_is_not_a_list(setup_shot_db_tests): - """TypeError raised if the scenes attribute is not a list.""" +def test_scene_attribute_is_not_a_scene(setup_shot_db_tests): + """TypeError raised if the scene attribute is not a Scene.""" data = setup_shot_db_tests with pytest.raises(TypeError) as cm: - data["test_shot"].scenes = "not a list" + data["test_shot"].scene = "not a scene" - assert str(cm.value) == "Incompatible collection type: str is not list-like" + assert str(cm.value) == ( + "Shot.scene should be a stalker.models.scene.Scene instance, not str: " + "'not a scene'" + ) -def test_scenes_argument_is_not_a_list_of_Scene_instances(setup_shot_db_tests): - """TypeError raised if the scenes argument is not a list of Scenes.""" +def test_scene_argument_is_a_list_of_scene_instances(setup_shot_db_tests): + """TypeError raised if the scene argument is a list of Scene instances.""" data = setup_shot_db_tests - data["kwargs"]["scenes"] = ["not", 1, "list", "of", "scenes"] + data["kwargs"]["scene"] = [ + Scene(name="sce1", code="sce1", project=data["test_project1"]), + Scene(name="sce2", code="sce2", project=data["test_project1"]), + Scene(name="sce3", code="sce3", project=data["test_project1"]), + ] data["kwargs"]["code"] = "NewShot" with pytest.raises(TypeError) as cm: Shot(**data["kwargs"]) assert str(cm.value) == ( - "Shot.scenes should all be stalker.models.scene.Scene instances, not str: 'not'" + "Shot.scene should be a stalker.models.scene.Scene instance, " + "not list: '[, , ]'" ) -def test_scenes_attribute_is_not_a_list_of_Scene_instances(setup_shot_db_tests): - """TypeError raised if the scenes attribute is not a list of Scene instances.""" +def test_scene_attribute_is_a_list_of_Scene_instances(setup_shot_db_tests): + """TypeError raised if the scene attribute is not a list of Scene instances.""" data = setup_shot_db_tests with pytest.raises(TypeError) as cm: - data["test_shot"].scenes = ["not", 1, "list", "of", "scenes"] + data["test_shot"].scene = [ + Scene(name="sce1", code="sce1", project=data["test_project1"]), + Scene(name="sce2", code="sce2", project=data["test_project1"]), + Scene(name="sce3", code="sce3", project=data["test_project1"]), + ] assert str(cm.value) == ( - "Shot.scenes should all be stalker.models.scene.Scene instances, not str: 'not'" + "Shot.scene should be a stalker.models.scene.Scene instance, " + "not list: '[, , ]'" ) -def test_scenes_argument_is_working_as_expected(setup_shot_db_tests): - """scenes attribute is working as expected.""" +def test_scene_argument_is_working_as_expected(setup_shot_db_tests): + """scene argument value is passed to scene attribute as expected.""" data = setup_shot_db_tests data["kwargs"]["code"] = "NewShot" sce1 = Scene(name="sce1", code="sce1", project=data["test_project1"]) sce2 = Scene(name="sce2", code="sce2", project=data["test_project1"]) sce3 = Scene(name="sce3", code="sce3", project=data["test_project1"]) - scenes = [sce1, sce2, sce3] - DBSession.add_all(scenes) - data["kwargs"]["scenes"] = scenes + DBSession.add_all([sce1, sce2, sce3]) + data["kwargs"]["scene"] = sce1 new_shot = Shot(**data["kwargs"]) DBSession.add(new_shot) - assert sorted(new_shot.scenes, key=lambda x: x.name) == sorted( - scenes, key=lambda x: x.name - ) + assert new_shot.scene == sce1 -def test_scenes_attribute_is_working_as_expected(setup_shot_db_tests): - """scenes attribute is working as expected.""" +def test_scene_attribute_is_working_as_expected(setup_shot_db_tests): + """scene attribute is working as expected.""" data = setup_shot_db_tests data["kwargs"]["code"] = "NewShot" sce1 = Scene(name="sce1", code="sce1", project=data["test_project1"]) sce2 = Scene(name="sce2", code="sce2", project=data["test_project1"]) sce3 = Scene(name="sce3", code="sce3", project=data["test_project1"]) - scenes = [sce1, sce2, sce3] - DBSession.add_all(scenes) + DBSession.add_all([sce1, sce2, sce3]) + data["kwargs"]["scene"] = sce1 new_shot = Shot(**data["kwargs"]) DBSession.add(new_shot) - new_shot.scenes = [sce1] - new_shot.scenes.append(sce2) - new_shot.scenes.append(sce3) - - assert sorted(new_shot.scenes, key=lambda x: x.name) == sorted( - scenes, key=lambda x: x.name - ) + assert new_shot.scene != sce2 + new_shot.scene = sce2 + assert new_shot.scene == sce2 def test_cut_in_argument_is_skipped(setup_shot_db_tests): @@ -1515,14 +1524,14 @@ def test__init_on_load__works_as_expected(setup_shot_db_tests): assert isinstance(shot, Shot) -def test_template_variables_include_scenes_for_shots(setup_shot_db_tests): - """_template_variables include scenes for shots.""" +def test_template_variables_include_scene_for_shots(setup_shot_db_tests): + """_template_variables include scene for shots.""" data = setup_shot_db_tests assert isinstance(data["test_shot"], Shot) template_variables = data["test_shot"]._template_variables() - assert "scenes" in template_variables - assert data["test_shot"].scenes != [] - assert template_variables["scenes"] == data["test_shot"].scenes + assert "scene" in template_variables + assert data["test_shot"].scene is not None + assert template_variables["scene"] == data["test_shot"].scene def test_template_variables_include_sequence_for_shots(setup_shot_db_tests): diff --git a/tests/models/test_task.py b/tests/models/test_task.py index 26b1826a..782d3836 100644 --- a/tests/models/test_task.py +++ b/tests/models/test_task.py @@ -5579,9 +5579,9 @@ def test_template_variables_for_non_shot_related_task(setup_task_db_tests): "asset": None, "parent_tasks": [task], "project": data["test_project1"], - "scenes": [], + "scene": None, "sequence": None, "shot": None, "task": task, - "type": None + "type": None, } diff --git a/tests/models/test_version.py b/tests/models/test_version.py index ca8bd21a..6946eed3 100644 --- a/tests/models/test_version.py +++ b/tests/models/test_version.py @@ -123,7 +123,7 @@ def setup_version_db_tests(setup_postgresql_db): code="SH001", project=data["test_project"], sequence=data["test_sequence"], - scenes=[data["test_scene"]], + scene=data["test_scene"], ) DBSession.add(data["test_shot1"]) DBSession.commit() @@ -953,11 +953,11 @@ def test_template_variables_sequence(setup_version_db_tests): assert kwargs["sequence"] == data["test_sequence"] -def test_template_variables_scenes(setup_version_db_tests): - """scenes in template variables is correct.""" +def test_template_variables_scene(setup_version_db_tests): + """scene in template variables is correct.""" data = setup_version_db_tests kwargs = data["test_version"]._template_variables() - assert kwargs["scenes"] == [data["test_scene"]] + assert kwargs["scene"] == data["test_scene"] def test_template_variables_shot(setup_version_db_tests): @@ -1004,14 +1004,14 @@ def test_template_variables_type(setup_version_db_tests): assert kwargs["type"] == data["test_version"].type -def test_template_variables_for_a_shot_version_contains_scenes(setup_version_db_tests): - """template_variables for a Shot version contains scenes.""" +def test_template_variables_for_a_shot_version_contains_scene(setup_version_db_tests): + """template_variables for a Shot version contains scene.""" data = setup_version_db_tests v = Version(task=data["test_shot1"]) template_variables = v._template_variables() - assert data["test_shot1"].scenes != [] - assert "scenes" in template_variables - assert template_variables["scenes"] == data["test_shot1"].scenes + assert data["test_shot1"].scene is not None + assert "scene" in template_variables + assert template_variables["scene"] == data["test_shot1"].scene def test_template_variables_for_a_shot_version_contains_sequence(