diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 83b956f73..54e3f91b2 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -3,7 +3,11 @@ name: Testing -on: push +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] jobs: lint: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7f10f4dc2..6a523d177 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,7 +25,7 @@ repos: name: pyupgrade description: Run PyUpgrade on Python code. - repo: https://github.com/pycqa/isort - rev: 5.10.1 + rev: 5.13.2 hooks: - id: isort args: [--settings-path setup.cfg] @@ -39,7 +39,7 @@ repos: entry: brunette --config=setup.cfg language: system types: [python] - - repo: https://gitlab.com/pycqa/flake8 + - repo: https://github.com/pycqa/flake8 rev: 3.8.3 hooks: - id: flake8 diff --git a/_frontend.codex b/_frontend.codex index 7b087d1c8..5dc1dc14d 160000 --- a/_frontend.codex +++ b/_frontend.codex @@ -1 +1 @@ -Subproject commit 7b087d1c8fd4ce5df8039dec4e56b326947533af +Subproject commit 5dc1dc14d92839ad7d80a51b037cf9caa358d192 diff --git a/app/modules/annotations/models.py b/app/modules/annotations/models.py index bf7e72d57..61344228b 100644 --- a/app/modules/annotations/models.py +++ b/app/modules/annotations/models.py @@ -21,7 +21,9 @@ class AnnotationKeywords(db.Model, HoustonModel): annotation_guid = db.Column( db.GUID, db.ForeignKey('annotation.guid'), primary_key=True ) - keyword_guid = db.Column(db.GUID, db.ForeignKey('keyword.guid'), primary_key=True) + keyword_guid = db.Column( + db.GUID, db.ForeignKey('keyword.guid', ondelete='CASCADE'), primary_key=True + ) annotation = db.relationship('Annotation', back_populates='keyword_refs') keyword = db.relationship('Keyword') diff --git a/app/modules/assets/models.py b/app/modules/assets/models.py index 7cd7fb64a..d70ad593b 100644 --- a/app/modules/assets/models.py +++ b/app/modules/assets/models.py @@ -24,7 +24,9 @@ class AssetTags(db.Model, HoustonModel): asset_guid = db.Column(db.GUID, db.ForeignKey('asset.guid'), primary_key=True) - tag_guid = db.Column(db.GUID, db.ForeignKey('keyword.guid'), primary_key=True) + tag_guid = db.Column( + db.GUID, db.ForeignKey('keyword.guid', ondelete='CASCADE'), primary_key=True + ) asset = db.relationship('Asset', back_populates='tag_refs') tag = db.relationship('Keyword') diff --git a/app/modules/keywords/resources.py b/app/modules/keywords/resources.py index 9d29147d4..aec892ddf 100644 --- a/app/modules/keywords/resources.py +++ b/app/modules/keywords/resources.py @@ -28,7 +28,7 @@ class Keywords(Resource): Manipulations with Keywords. """ - @api.response(schemas.BaseKeywordSchema(many=True)) + @api.response(schemas.DetailedKeywordSchema(many=True)) @api.paginate() def get(self, args): """ diff --git a/app/modules/keywords/schemas.py b/app/modules/keywords/schemas.py index 55c1074de..52a449587 100644 --- a/app/modules/keywords/schemas.py +++ b/app/modules/keywords/schemas.py @@ -5,6 +5,8 @@ """ +from flask_marshmallow import base_fields + from flask_restx_patched import ModelSchema from .models import Keyword @@ -31,10 +33,15 @@ class DetailedKeywordSchema(BaseKeywordSchema): Detailed Keyword schema exposes all useful fields. """ + usageCount = base_fields.Function( + lambda kw: kw.number_referenced_dependencies(), dump_only=True + ) + class Meta(BaseKeywordSchema.Meta): fields = BaseKeywordSchema.Meta.fields + ( Keyword.created.key, Keyword.updated.key, + 'usageCount', # This is a read-only field ) dump_only = BaseKeywordSchema.Meta.dump_only + ( Keyword.created.key, diff --git a/app/modules/users/permissions/rules.py b/app/modules/users/permissions/rules.py index e999f3d72..8480b0faa 100644 --- a/app/modules/users/permissions/rules.py +++ b/app/modules/users/permissions/rules.py @@ -178,6 +178,7 @@ 'is_admin', ], ('Keyword', AccessOperation.READ): ['is_active'], + ('Keyword', AccessOperation.DELETE): ['is_admin'], ('Sighting', AccessOperation.WRITE_INTERNAL): ['is_internal'], ('Sighting', AccessOperation.READ_PRIVILEGED): ['is_staff'], ('Sighting', AccessOperation.READ): ['is_admin'], diff --git a/migrations/versions/dc91b517a7a4_.py b/migrations/versions/dc91b517a7a4_.py new file mode 100644 index 000000000..e620eb790 --- /dev/null +++ b/migrations/versions/dc91b517a7a4_.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +"""empty message + +Revision ID: dc91b517a7a4 +Revises: 53b0fec87272 +Create Date: 2024-01-22 22:51:34.690766 + +""" + +from alembic import op + +# revision identifiers, used by Alembic. +revision = 'dc91b517a7a4' +down_revision = '53b0fec87272' + + +def upgrade(): + """ + Upgrade Semantic Description: + ENTER DESCRIPTION HERE + """ + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('annotation_keywords', schema=None) as batch_op: + batch_op.drop_constraint( + 'fk_annotation_keywords_keyword_guid_keyword', type_='foreignkey' + ) + batch_op.create_foreign_key( + batch_op.f('fk_annotation_keywords_keyword_guid_keyword'), + 'keyword', + ['keyword_guid'], + ['guid'], + ondelete='CASCADE', + ) + + with op.batch_alter_table('asset_tags', schema=None) as batch_op: + batch_op.drop_constraint('fk_asset_tags_tag_guid_keyword', type_='foreignkey') + batch_op.create_foreign_key( + batch_op.f('fk_asset_tags_tag_guid_keyword'), + 'keyword', + ['tag_guid'], + ['guid'], + ondelete='CASCADE', + ) + + # ### end Alembic commands ### + + +def downgrade(): + """ + Downgrade Semantic Description: + ENTER DESCRIPTION HERE + """ + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('asset_tags', schema=None) as batch_op: + batch_op.drop_constraint( + batch_op.f('fk_asset_tags_tag_guid_keyword'), type_='foreignkey' + ) + batch_op.create_foreign_key( + 'fk_asset_tags_tag_guid_keyword', 'keyword', ['tag_guid'], ['guid'] + ) + + with op.batch_alter_table('annotation_keywords', schema=None) as batch_op: + batch_op.drop_constraint( + batch_op.f('fk_annotation_keywords_keyword_guid_keyword'), type_='foreignkey' + ) + batch_op.create_foreign_key( + 'fk_annotation_keywords_keyword_guid_keyword', + 'keyword', + ['keyword_guid'], + ['guid'], + ) + + # ### end Alembic commands ### diff --git a/tests/modules/keywords/resources/test_keywords.py b/tests/modules/keywords/resources/test_keywords.py index aeab94849..197b218a7 100644 --- a/tests/modules/keywords/resources/test_keywords.py +++ b/tests/modules/keywords/resources/test_keywords.py @@ -70,6 +70,7 @@ def test_modify_keyword(db, flask_app_client, researcher_1, staff_user): # doublecheck by reading back in response = keyword_utils.read_keyword(flask_app_client, researcher_1, guid) assert response.json.get('value', None) == val2 + assert response.json.get('usageCount', None) == 0 # both of these should not be allowed (403) response = keyword_utils.patch_keyword(