From 94915d458a6596e29b9813e81cdc5f16b5e30833 Mon Sep 17 00:00:00 2001 From: Evan Blaudy Date: Mon, 21 Oct 2024 01:48:08 +0200 Subject: [PATCH 1/6] [config] for MAIL_DEBUG convert to bool --- zou/app/config.py | 2 +- zou/app/utils/string.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/zou/app/config.py b/zou/app/config.py index a435561e48..21b2ed92fe 100644 --- a/zou/app/config.py +++ b/zou/app/config.py @@ -81,7 +81,7 @@ MAIL_PORT = os.getenv("MAIL_PORT", 25) MAIL_USERNAME = os.getenv("MAIL_USERNAME", "") MAIL_PASSWORD = os.getenv("MAIL_PASSWORD", "") -MAIL_DEBUG = int(os.getenv("MAIL_DEBUG", "0")) +MAIL_DEBUG = envtobool("MAIL_DEBUG", False) MAIL_DEBUG_BODY = envtobool("MAIL_DEBUG_BODY", False) MAIL_USE_TLS = envtobool("MAIL_USE_TLS", False) MAIL_USE_SSL = envtobool("MAIL_USE_SSL", False) diff --git a/zou/app/utils/string.py b/zou/app/utils/string.py index fe8c8cdd3b..10241a875e 100644 --- a/zou/app/utils/string.py +++ b/zou/app/utils/string.py @@ -6,6 +6,8 @@ def strtobool(val): """ if isinstance(val, bool): return val + if isinstance(val, int): + return bool(val) elif val.lower() in ("y", "yes", "t", "true", "on", "1"): return True elif val.lower() in ("n", "no", "f", "false", "off", "0"): From bec41d7fb1fe0b21adf5a55b911a9ae2053a95d4 Mon Sep 17 00:00:00 2001 From: Evan Blaudy Date: Mon, 21 Oct 2024 03:00:14 +0200 Subject: [PATCH 2/6] [requirements] upgrade --- setup.cfg | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/setup.cfg b/setup.cfg index 7eb36cf79c..1eb642033f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -47,7 +47,7 @@ install_requires = flask==3.0.3 gazu==0.10.16 gevent-websocket==0.10.1 - gevent==24.10.2 + gevent==24.10.3 gunicorn==23.0.0 isoweek==1.3.3 itsdangerous==2.2.0 @@ -60,9 +60,9 @@ install_requires = opencv-python==4.10.0.84 OpenTimelineIO==0.17.0 OpenTimelineIO-Plugins==0.17.0 - orjson==3.10.7 + orjson==3.10.9 pillow==11.0.0 - psutil==6.0.0 + psutil==6.1.0 psycopg[binary]==3.2.3 pyotp==2.9.0 pysaml2==7.5.0 @@ -103,7 +103,7 @@ test = monitoring = prometheus-flask-exporter==0.23.1 pygelf==0.4.2 - sentry-sdk==2.16.0 + sentry-sdk==2.17.0 lint = autoflake==2.3.1 From 40e75450302a14d2a187beefad9fe51cce110611 Mon Sep 17 00:00:00 2001 From: Evan Blaudy Date: Mon, 21 Oct 2024 03:10:00 +0200 Subject: [PATCH 3/6] [qa] remove useless print --- zou/app/blueprints/user/resources.py | 1 - 1 file changed, 1 deletion(-) diff --git a/zou/app/blueprints/user/resources.py b/zou/app/blueprints/user/resources.py index e52995d3ae..a2d14c0238 100644 --- a/zou/app/blueprints/user/resources.py +++ b/zou/app/blueprints/user/resources.py @@ -783,7 +783,6 @@ def put(self, filter_group_id): ) data = self.clear_empty_fields(data) - print("data", data) user_filter = user_service.update_filter_group(filter_group_id, data) return user_filter, 200 From ffaf6f319e903d6ac63610e127e3d0c3dd6f2b0a Mon Sep 17 00:00:00 2001 From: Evan Blaudy Date: Mon, 21 Oct 2024 03:12:37 +0200 Subject: [PATCH 4/6] [migration] fix downgrade for foreign keys --- ...c6_add_department_keys_to_filter_models.py | 57 ++++++++++++++----- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/zou/migrations/versions/9d3bb33c6fc6_add_department_keys_to_filter_models.py b/zou/migrations/versions/9d3bb33c6fc6_add_department_keys_to_filter_models.py index 08d5fa60d6..7ba9bbcc67 100644 --- a/zou/migrations/versions/9d3bb33c6fc6_add_department_keys_to_filter_models.py +++ b/zou/migrations/versions/9d3bb33c6fc6_add_department_keys_to_filter_models.py @@ -5,6 +5,7 @@ Create Date: 2024-10-11 15:52:29.740222 """ + from alembic import op import sqlalchemy as sa import sqlalchemy_utils @@ -12,33 +13,61 @@ import uuid # revision identifiers, used by Alembic. -revision = '9d3bb33c6fc6' -down_revision = '8e67c183bed7' +revision = "9d3bb33c6fc6" +down_revision = "8e67c183bed7" branch_labels = None depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - with op.batch_alter_table('search_filter', schema=None) as batch_op: - batch_op.add_column(sa.Column('department_id', sqlalchemy_utils.types.uuid.UUIDType(binary=False), default=uuid.uuid4, nullable=True)) - batch_op.create_foreign_key(None, 'department', ['department_id'], ['id']) + with op.batch_alter_table("search_filter", schema=None) as batch_op: + batch_op.add_column( + sa.Column( + "department_id", + sqlalchemy_utils.types.uuid.UUIDType(binary=False), + default=uuid.uuid4, + nullable=True, + ) + ) + batch_op.create_foreign_key( + "search_filter_department_id_fkey", + "department", + ["department_id"], + ["id"], + ) - with op.batch_alter_table('search_filter_group', schema=None) as batch_op: - batch_op.add_column(sa.Column('department_id', sqlalchemy_utils.types.uuid.UUIDType(binary=False), default=uuid.uuid4, nullable=True)) - batch_op.create_foreign_key(None, 'department', ['department_id'], ['id']) + with op.batch_alter_table("search_filter_group", schema=None) as batch_op: + batch_op.add_column( + sa.Column( + "department_id", + sqlalchemy_utils.types.uuid.UUIDType(binary=False), + default=uuid.uuid4, + nullable=True, + ) + ) + batch_op.create_foreign_key( + "search_filter_group_department_id_fkey", + "department", + ["department_id"], + ["id"], + ) # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### - with op.batch_alter_table('search_filter_group', schema=None) as batch_op: - batch_op.drop_constraint(None, type_='foreignkey') - batch_op.drop_column('department_id') + with op.batch_alter_table("search_filter_group", schema=None) as batch_op: + batch_op.drop_constraint( + "search_filter_group_department_id_fkey", type_="foreignkey" + ) + batch_op.drop_column("department_id") - with op.batch_alter_table('search_filter', schema=None) as batch_op: - batch_op.drop_constraint(None, type_='foreignkey') - batch_op.drop_column('department_id') + with op.batch_alter_table("search_filter", schema=None) as batch_op: + batch_op.drop_constraint( + "search_filter_department_id_fkey", type_="foreignkey" + ) + batch_op.drop_column("department_id") # ### end Alembic commands ### From 15e70834c1d6ec1d99a266e7cdb6fa112849580c Mon Sep 17 00:00:00 2001 From: Evan Blaudy Date: Mon, 21 Oct 2024 03:13:01 +0200 Subject: [PATCH 5/6] [qa] black --- tests/user/test_route_context.py | 57 ++++++++++++++++----------- zou/app/blueprints/index/resources.py | 2 +- zou/app/services/user_service.py | 52 +++++++++++++----------- 3 files changed, 63 insertions(+), 48 deletions(-) diff --git a/tests/user/test_route_context.py b/tests/user/test_route_context.py index 2ebcb60af0..a5148988a1 100644 --- a/tests/user/test_route_context.py +++ b/tests/user/test_route_context.py @@ -602,7 +602,7 @@ def test_shared_filters(self): } filter_2 = self.post(path, filter_2) - # Artist can see their filters and the shared filters + # Artist can see their filters and the shared filters self.log_in_cg_artist() result = self.get(path) self.assertEqual(len(result["asset"][project_id]), 2) @@ -616,7 +616,7 @@ def test_shared_filters(self): ) self.log_in_cg_artist() - # Admin can update filter + # Admin can update filter self.log_in_admin() self.put( "data/user/filters/%s" % filter_2["id"], @@ -624,14 +624,15 @@ def test_shared_filters(self): ) result = self.get(path) user_service.clear_filter_cache() - self.assertEqual(result["asset"][project_id][0]["name"], "team updated") + self.assertEqual( + result["asset"][project_id][0]["name"], "team updated" + ) self.assertEqual(result["asset"][project_id][0]["is_shared"], True) # Artist cannot update admin's filter self.log_in_cg_artist() self.put( - "data/user/filters/%s" - % result["asset"][project_id][0]["id"], + "data/user/filters/%s" % result["asset"][project_id][0]["id"], {"name": "updated", "is_shared": True}, 404, ) @@ -658,7 +659,9 @@ def test_shared_filters(self): user_service.clear_filter_cache() self.assertEqual(len(result["asset"][project_id]), 2) self.assertEqual(result["asset"][project_id][0]["name"], "my filter") - self.assertEqual(result["asset"][project_id][1]["name"], "team updated") + self.assertEqual( + result["asset"][project_id][1]["name"], "team updated" + ) # Filter is shared with the artist's department self.log_in_admin() @@ -667,14 +670,18 @@ def test_shared_filters(self): { "name": "department updated", "is_shared": True, - "department_id": self.department.id + "department_id": self.department.id, }, ) result = self.get(path) user_service.clear_filter_cache() self.assertEqual(len(result["asset"][project_id]), 2) - self.assertEqual(result["asset"][project_id][0]["name"], "team updated") - self.assertEqual(result["asset"][project_id][1]["name"], "department updated") + self.assertEqual( + result["asset"][project_id][0]["name"], "team updated" + ) + self.assertEqual( + result["asset"][project_id][1]["name"], "department updated" + ) # Now artist can see the department filter self.log_in_cg_artist() @@ -682,11 +689,12 @@ def test_shared_filters(self): result = self.get(path) self.assertEqual(len(result["asset"][project_id]), 3) self.assertEqual( - result["asset"][project_id][2]["name"], "department updated") - self.assertEqual( - result["asset"][project_id][1]["name"], "team updated") + result["asset"][project_id][2]["name"], "department updated" + ) self.assertEqual( - result["asset"][project_id][0]["name"], "my filter") + result["asset"][project_id][1]["name"], "team updated" + ) + self.assertEqual(result["asset"][project_id][0]["name"], "my filter") def test_shared_group_filters(self): project_id = str(self.project.id) @@ -704,12 +712,12 @@ def test_shared_group_filters(self): } self.post(path, filter_group_1) - # Admin cannot see artist's filter group + # Admin cannot see artist's filter group self.log_in_admin() result = self.get(path) self.assertEqual(result, {}) - # Artist can see their filter groups + # Artist can see their filter groups self.log_in_cg_artist() result = self.get(path) self.assertEqual(len(result["asset"][project_id]), 1) @@ -735,7 +743,7 @@ def test_shared_group_filters(self): } self.post(path, filter_group_2) - # Artist can see their groups and the shared groups + # Artist can see their groups and the shared groups self.log_in_cg_artist() result = self.get(path) self.assertEqual(len(result["asset"][project_id]), 2) @@ -744,7 +752,7 @@ def test_shared_group_filters(self): self.assertEqual(result["asset"][project_id][1]["name"], "my group") self.assertEqual(result["asset"][project_id][1]["is_shared"], False) - # Admin can update filter group + # Admin can update filter group self.log_in_admin() self.put( "data/user/filter-groups/%s" @@ -796,13 +804,15 @@ def test_shared_group_filters(self): { "name": "department updated", "is_shared": True, - "department_id": self.department.id + "department_id": self.department.id, }, ) user_service.clear_filter_group_cache() result = self.get(path) self.assertEqual(len(result["asset"][project_id]), 2) - self.assertEqual(result["asset"][project_id][0]["name"], "department updated") + self.assertEqual( + result["asset"][project_id][0]["name"], "department updated" + ) self.assertEqual(result["asset"][project_id][1]["name"], "updated") # Now artist can see the department filter group @@ -811,11 +821,10 @@ def test_shared_group_filters(self): result = self.get(path) self.assertEqual(len(result["asset"][project_id]), 3) self.assertEqual( - result["asset"][project_id][0]["name"], "department updated") - self.assertEqual( - result["asset"][project_id][1]["name"], "updated") - self.assertEqual( - result["asset"][project_id][2]["name"], "my group") + result["asset"][project_id][0]["name"], "department updated" + ) + self.assertEqual(result["asset"][project_id][1]["name"], "updated") + self.assertEqual(result["asset"][project_id][2]["name"], "my group") def create_test_folder(self): return super().create_test_folder() diff --git a/zou/app/blueprints/index/resources.py b/zou/app/blueprints/index/resources.py index 3c80033793..87433f699f 100644 --- a/zou/app/blueprints/index/resources.py +++ b/zou/app/blueprints/index/resources.py @@ -283,7 +283,7 @@ def get(self): responses: 200: description: Configuration object including self-hosted status, - Crisp token, indexer configuration, SAML status, and dark theme + Crisp token, indexer configuration, SAML status, and dark theme status """ organisation = persons_service.get_organisation() diff --git a/zou/app/services/user_service.py b/zou/app/services/user_service.py index e949983dd7..8f24c09cf0 100644 --- a/zou/app/services/user_service.py +++ b/zou/app/services/user_service.py @@ -895,8 +895,8 @@ def get_user_filters(current_user_id): for search_filter in filters: department_id = search_filter.department_id is_in_departments = ( - department_id is not None and \ - str(department_id) in current_user["departments"] + department_id is not None + and str(department_id) in current_user["departments"] ) if department_id is None or is_manager or is_in_departments: @@ -996,15 +996,17 @@ def update_filter(search_filter_id, data): f"No department found with id: {department_id}" ) - if data.get("is_shared", None) is not None and \ - search_filter.is_shared != data["is_shared"] and \ - ( - data.get("project_id", None) is None or - ( + if ( + data.get("is_shared", None) is not None + and search_filter.is_shared != data["is_shared"] + and ( + data.get("project_id", None) is None + or ( data["project_id"] is not None and not has_manager_project_access(data["project_id"]) - ) - ): + ) + ) + ): data["is_shared"] = False if ( @@ -1105,8 +1107,8 @@ def get_user_filter_groups(current_user_id): department_id = search_filter_group.department_id is_in_departments = ( - department_id is not None and \ - str(department_id) in current_user["departments"] + department_id is not None + and str(department_id) in current_user["departments"] ) if department_id is None or is_manager or is_in_departments: subresult = result[search_filter_group.list_type] @@ -1130,7 +1132,7 @@ def create_filter_group( project_id=None, entity_type=None, is_shared=False, - department_id=None + department_id=None, ): """ Add a new search filter group to the database. @@ -1193,22 +1195,26 @@ def update_filter_group(search_filter_group_id, data): if search_filter_group is None: raise SearchFilterGroupNotFoundException - if data.get("is_shared", None) is not None and \ - search_filter_group.is_shared != data["is_shared"] and \ - ( - data.get("project_id", None) is None or - ( + if ( + data.get("is_shared", None) is not None + and search_filter_group.is_shared != data["is_shared"] + and ( + data.get("project_id", None) is None + or ( data["project_id"] is not None and not has_manager_project_access(data["project_id"]) - ) - ): - data["is_shared"] = False + ) + ) + ): + data["is_shared"] = False search_filter_group.update(data) - if data.get("is_shared", None) is not None \ - and data.get("project_id", None) is not None \ - and has_manager_project_access(data["project_id"]): + if ( + data.get("is_shared", None) is not None + and data.get("project_id", None) is not None + and has_manager_project_access(data["project_id"]) + ): if ( SearchFilter.query.filter_by( search_filter_group_id=search_filter_group_id From 629966157ae8d78bb027fc74c31347fd4ab1f612 Mon Sep 17 00:00:00 2001 From: Evan Blaudy Date: Mon, 21 Oct 2024 03:16:45 +0200 Subject: [PATCH 6/6] [openAPI] fix API specs --- zou/app/blueprints/index/resources.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zou/app/blueprints/index/resources.py b/zou/app/blueprints/index/resources.py index 87433f699f..37eaaaf1ce 100644 --- a/zou/app/blueprints/index/resources.py +++ b/zou/app/blueprints/index/resources.py @@ -283,8 +283,8 @@ def get(self): responses: 200: description: Configuration object including self-hosted status, - Crisp token, indexer configuration, SAML status, and dark theme - status + Crisp token, indexer configuration, SAML status, and dark + theme status. """ organisation = persons_service.get_organisation() conf = {