diff --git a/ckanext/__init__.py b/ckanext/__init__.py index 35ee891..6d83202 100644 --- a/ckanext/__init__.py +++ b/ckanext/__init__.py @@ -1,5 +1,3 @@ -# encoding: utf-8 - # this is a namespace package try: import pkg_resources diff --git a/ckanext/relationship/helpers.py b/ckanext/relationship/helpers.py index ffc6143..9995fd7 100644 --- a/ckanext/relationship/helpers.py +++ b/ckanext/relationship/helpers.py @@ -22,7 +22,8 @@ def relationship_get_entity_list(entity, entity_type, include_private=True): entity_list = entity_list["results"] else: entity_list = tk.get_action("relationship_get_entity_list")( - context, {"entity": entity, "entity_type": entity_type} + context, + {"entity": entity, "entity_type": entity_type}, ) entity_list = [ {"id": id, "name": name, "title": title} for id, name, title in entity_list @@ -78,10 +79,12 @@ def relationship_get_selected_json(selected_ids: list = []) -> str: def relationship_get_choices_for_related_entity_field( - field: dict[str, Any], current_entity_id: str | None + field: dict[str, Any], + current_entity_id: str | None, ) -> list[str | None]: entities = relationship_get_entity_list( - field["related_entity"], field["related_entity_type"] + field["related_entity"], + field["related_entity_type"], ) choices = [] @@ -91,7 +94,8 @@ def relationship_get_choices_for_related_entity_field( continue if field.get("updatable_only", False) and not tk.h.check_access( - field["related_entity"] + "_update", {"id": entity["id"]} + field["related_entity"] + "_update", + {"id": entity["id"]}, ): continue @@ -118,6 +122,6 @@ def relationship_format_autocomplete(packages: dict[str, Any]) -> dict[str, Any] "title": pkg["title"], } for pkg in packages - ] - } + ], + }, } diff --git a/ckanext/relationship/logic/action.py b/ckanext/relationship/logic/action.py index 494b5db..c52d149 100644 --- a/ckanext/relationship/logic/action.py +++ b/ckanext/relationship/logic/action.py @@ -3,13 +3,13 @@ from flask import jsonify from sqlalchemy import or_ -import ckan.logic as logic import ckan.plugins.toolkit as tk -from ckan import authz +from ckan import authz, logic from ckan.logic import validate -from ckan.types import Context, DataDict +from ckan.types import Action, Context, DataDict -import ckanext.relationship.logic.schema as schema +from ckanext.relationship import utils +from ckanext.relationship.logic import schema from ckanext.relationship.model.relationship import Relationship from ckanext.relationship.utils import entity_name_by_id @@ -18,7 +18,8 @@ @validate(schema.relation_create) def relationship_relation_create( - context: Context, data_dict: DataDict + context: Context, + data_dict: DataDict, ) -> list[dict[str, str]]: """Create relation with specified type (relation_type) between two entities specified by ids (subject_id, object_id). Also create reverse relation.""" @@ -29,10 +30,12 @@ def relationship_relation_create( relation_type = data_dict.get("relation_type") if Relationship.by_object_id(subject_id, object_id, relation_type): - return + return None relation = Relationship( - subject_id=subject_id, object_id=object_id, relation_type=relation_type + subject_id=subject_id, + object_id=object_id, + relation_type=relation_type, ) reverse_relation = Relationship( @@ -50,7 +53,8 @@ def relationship_relation_create( @validate(schema.relation_delete) def relationship_relation_delete( - context: Context, data_dict: DataDict + context: Context, + data_dict: DataDict, ) -> list[dict[str, str]]: """Delete relation with specified type (relation_type) between two entities specified by ids (subject_id, object_id). Also delete reverse relation.""" @@ -100,7 +104,7 @@ def relationship_relation_delete( if relation_type: reverse_relation = reverse_relation.filter( Relationship.relation_type - == Relationship.reverse_relation_type[relation_type] + == Relationship.reverse_relation_type[relation_type], ) reverse_relation = reverse_relation.all() @@ -113,11 +117,12 @@ def relationship_relation_delete( @validate(schema.relations_list) def relationship_relations_list( - context: Context, data_dict: DataDict + context: Context, + data_dict: DataDict, ) -> list[dict[str, str]]: - """Return dicts list of relation of specified entity (object_entity, object_type) - related with specified type of relation (relation_type) with entity specified - by id (subject_id). + """Return a list of dictionaries representing the relations of a specified entity + (object_entity, object_type) related to the specified type of relation + (relation_type) with an entity specified by its id (subject_id). """ tk.check_access("relationship_relations_list", context, data_dict) @@ -130,7 +135,10 @@ def relationship_relations_list( relation_type = data_dict.get("relation_type") relations = Relationship.by_subject_id( - subject_id, object_entity, object_type, relation_type + subject_id, + object_entity, + object_type, + relation_type, ) if not relations: return [] @@ -159,21 +167,16 @@ def relationship_get_entity_list(context: Context, data_dict: DataDict) -> list[ entity = data_dict["entity"] entity = entity if entity != "organization" else "group" - - entity_type = data_dict["entity_type"] - entity_class = logic.model_name_to_class(model, entity) - entity_list = ( + return ( context["session"] .query(entity_class.id, entity_class.name, entity_class.title) .filter(entity_class.state != "deleted") - .filter(entity_class.type == entity_type) + .filter(entity_class.type == data_dict["entity_type"]) .all() ) - return entity_list - @validate(schema.autocomplete) def relationship_autocomplete(context: Context, data_dict: DataDict) -> DataDict: @@ -210,3 +213,35 @@ def relationship_autocomplete(context: Context, data_dict: DataDict) -> DataDict ) return jsonify(format_autocomplete_helper(packages)) + + +@tk.chained_action +@tk.side_effect_free +def package_show(next_: Action, context: Context, data_dict: DataDict) -> DataDict: + result = next_(context, data_dict) + if "with_relationships" not in data_dict: + return result + pkg_id = result["id"] + pkg_type = result["type"] + relations_info = utils.get_relations_info(pkg_type) + for ( + related_entity, + related_entity_type, + relation_type, + ) in relations_info: + field = utils.get_relation_field( + pkg_type, + related_entity, + related_entity_type, + relation_type, + ) + result[field["field_name"]] = tk.get_action("relationship_relations_list")( + context, + { + "subject_id": pkg_id, + "object_entity": related_entity, + "object_type": related_entity_type, + "relation_type": relation_type, + }, + ) + return result diff --git a/ckanext/relationship/logic/validators.py b/ckanext/relationship/logic/validators.py index 5cb6646..807dc0a 100644 --- a/ckanext/relationship/logic/validators.py +++ b/ckanext/relationship/logic/validators.py @@ -26,7 +26,10 @@ def validator(key, data, errors, context): entity_id = data.get(("id",)) current_relations = get_current_relations( - entity_id, related_entity, related_entity_type, relation_type + entity_id, + related_entity, + related_entity_type, + relation_type, ) selected_relations = get_selected_relations(data[key]) @@ -46,7 +49,10 @@ def validator(key, data, errors, context): def get_current_relations( - entity_id, related_entity, related_entity_type, relation_type + entity_id, + related_entity, + related_entity_type, + relation_type, ): if entity_id: current_relations = tk.get_action("relationship_relations_list")( diff --git a/ckanext/relationship/migration/relationship/env.py b/ckanext/relationship/migration/relationship/env.py index 99106b8..2d1f539 100644 --- a/ckanext/relationship/migration/relationship/env.py +++ b/ckanext/relationship/migration/relationship/env.py @@ -1,7 +1,3 @@ -# -*- coding: utf-8 -*- - -from __future__ import with_statement - import os from logging.config import fileConfig @@ -48,7 +44,7 @@ def run_migrations_offline(): url=url, target_metadata=target_metadata, literal_binds=True, - version_table="{}_alembic_version".format(name), + version_table=f"{name}_alembic_version", ) with context.begin_transaction(): @@ -72,7 +68,7 @@ def run_migrations_online(): context.configure( connection=connection, target_metadata=target_metadata, - version_table="{}_alembic_version".format(name), + version_table=f"{name}_alembic_version", ) with context.begin_transaction(): diff --git a/ckanext/relationship/model/relationship.py b/ckanext/relationship/model/relationship.py index ad53d6f..257e9e8 100644 --- a/ckanext/relationship/model/relationship.py +++ b/ckanext/relationship/model/relationship.py @@ -1,7 +1,6 @@ from sqlalchemy import Column, Text, or_ -import ckan.logic as logic -import ckan.model as model +from ckan import logic, model from ckan.model.types import make_uuid from .base import Base @@ -53,7 +52,7 @@ def by_object_id(cls, subject_id, object_id, relation_type): or_( cls.subject_id == subject_id, cls.subject_id == subject_name, - ) + ), ) .filter(or_(cls.object_id == object_id, cls.object_id == object_name)) .filter(cls.relation_type == relation_type) @@ -62,12 +61,16 @@ def by_object_id(cls, subject_id, object_id, relation_type): @classmethod def by_subject_id( - cls, subject_id, object_entity, object_type=None, relation_type=None + cls, + subject_id, + object_entity, + object_type=None, + relation_type=None, ): subject_name = _entity_name_by_id(subject_id) q = model.Session.query(cls).filter( - or_(cls.subject_id == subject_id, cls.subject_id == subject_name) + or_(cls.subject_id == subject_id, cls.subject_id == subject_name), ) if object_entity: @@ -76,7 +79,7 @@ def by_subject_id( or_( object_class.id == cls.object_id, object_class.name == cls.object_id, - ) + ), ) if object_type: diff --git a/ckanext/relationship/plugin.py b/ckanext/relationship/plugin.py index 1e1a0b0..904f408 100644 --- a/ckanext/relationship/plugin.py +++ b/ckanext/relationship/plugin.py @@ -1,11 +1,11 @@ -import ckan.plugins as plugins import ckan.plugins.toolkit as tk +from ckan import plugins from ckan.lib.search import rebuild from ckan.logic import NotFound import ckanext.scheming.helpers as sch -import ckanext.relationship.utils as utils +from ckanext.relationship import utils @tk.blanket.actions @@ -41,12 +41,14 @@ def after_dataset_delete(self, context, pkg_dict): subject_id = pkg_dict["id"] relations_ids_list = tk.get_action("relationship_relations_ids_list")( - context, {"subject_id": subject_id} + context, + {"subject_id": subject_id}, ) for object_id in relations_ids_list: tk.get_action("relationship_relation_delete")( - context, {"subject_id": subject_id, "object_id": object_id} + context, + {"subject_id": subject_id, "object_id": object_id}, ) try: @@ -80,16 +82,18 @@ def before_dataset_index(self, pkg_dict): if not relations_ids: continue field = utils.get_relation_field( - pkg_type, related_entity, related_entity_type, relation_type + pkg_type, + related_entity, + related_entity_type, + relation_type, ) pkg_dict[f'vocab_{field["field_name"]}'] = relations_ids - del pkg_dict[field["field_name"]] + pkg_dict.pop(field["field_name"], None) return pkg_dict def after_dataset_show(self, context, pkg_dict): - pkg_id = pkg_dict["id"] pkg_type = pkg_dict["type"] relations_info = utils.get_relations_info(pkg_type) for ( @@ -98,19 +102,12 @@ def after_dataset_show(self, context, pkg_dict): relation_type, ) in relations_info: field = utils.get_relation_field( - pkg_type, related_entity, related_entity_type, relation_type - ) - pkg_dict[field["field_name"]] = tk.get_action( - "relationship_relations_ids_list" - )( - context, - { - "subject_id": pkg_id, - "object_entity": related_entity, - "object_type": related_entity_type, - "relation_type": relation_type, - }, + pkg_type, + related_entity, + related_entity_type, + relation_type, ) + pkg_dict.pop(field["field_name"], None) def _update_relations(context, pkg_dict): diff --git a/ckanext/relationship/tests/conftest.py b/ckanext/relationship/tests/conftest.py index 9d43ed8..b194d4d 100644 --- a/ckanext/relationship/tests/conftest.py +++ b/ckanext/relationship/tests/conftest.py @@ -1,7 +1,7 @@ import pytest -@pytest.fixture +@pytest.fixture() def clean_db(reset_db, migrate_db_for, with_plugins): reset_db() migrate_db_for("relationship") diff --git a/ckanext/relationship/tests/logic/test_action.py b/ckanext/relationship/tests/logic/test_action.py index 406d986..ca3d3fb 100644 --- a/ckanext/relationship/tests/logic/test_action.py +++ b/ckanext/relationship/tests/logic/test_action.py @@ -79,10 +79,14 @@ def test_relation_is_added_to_db(self): ) relation_straight = Relationship.by_object_id( - subject_id, object_id, relation_type + subject_id, + object_id, + relation_type, ) relation_reverse = Relationship.by_object_id( - object_id, subject_id, relation_type + object_id, + subject_id, + relation_type, ) assert relation_straight.subject_id == subject_id @@ -187,10 +191,14 @@ def test_relation_deleted_from_db(self): ) relation_straight = Relationship.by_object_id( - subject_id, object_id, relation_type + subject_id, + object_id, + relation_type, ) relation_reverse = Relationship.by_object_id( - object_id, subject_id, relation_type + object_id, + subject_id, + relation_type, ) assert not relation_straight @@ -216,10 +224,14 @@ def test_relation_delete_after_dataset_delete(self): tk.get_action("package_delete")({"ignore_auth": True}, {"id": subject_id}) relation_straight = Relationship.by_object_id( - subject_id, object_id, relation_type + subject_id, + object_id, + relation_type, ) relation_reverse = Relationship.by_object_id( - object_id, subject_id, relation_type + object_id, + subject_id, + relation_type, ) assert not relation_straight @@ -248,7 +260,11 @@ class TestRelationList: ], ) def test_relation_list( - self, subject_factory, object_factory, object_entity, object_type + self, + subject_factory, + object_factory, + object_entity, + object_type, ): subject = subject_factory() object = object_factory() diff --git a/ckanext/relationship/views.py b/ckanext/relationship/views.py index 7b2431a..d93feb9 100644 --- a/ckanext/relationship/views.py +++ b/ckanext/relationship/views.py @@ -25,7 +25,7 @@ def relationships_autocomplete(): "owned_only": tk.asbool(request_args.get("owned_only")), "check_sysadmin": tk.asbool(request_args.get("check_sysadmin")), "format_autocomplete_helper": request_args.get( - "format_autocomplete_helper" + "format_autocomplete_helper", ), }, ) diff --git a/pyproject.toml b/pyproject.toml index 411dda1..8867eb1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,26 +8,26 @@ select = [ # "B", # likely bugs and design problems # "BLE", # do not catch blind exception # "C40", # better list/set/dict comprehensions - # "C90", # check McCabe complexity - # "COM", # trailing commas + "C90", # check McCabe complexity + "COM", # trailing commas "E", # pycodestyle error - # "W", # pycodestyle warning + "W", # pycodestyle warning "F", # pyflakes - # "G", # format strings for logging statements - # "N", # naming conventions - # "PL", # pylint + "G", # format strings for logging statements + "N", # naming conventions + "PL", # pylint # "PT", # pytest style - # "PIE", # misc lints - # "Q", # preferred quoting style + "PIE", # misc lints + "Q", # preferred quoting style # "RET", # improvements for return statements - # "RSE", # improvements for rise statements + "RSE", # improvements for rise statements # "S", # security testing # "SIM", # simplify code - # "T10", # debugging statements - # "T20", # print statements - # "TID", # tidier imports - # "TRY", # better exceptions - # "UP", # upgrade syntax for newer versions of the language + "T10", # debugging statements + "T20", # print statements + "TID", # tidier imports + "TRY", # better exceptions + "UP", # upgrade syntax for newer versions of the language ] ignore = [ "E712", # comparison to bool: violated by SQLAlchemy filters diff --git a/setup.py b/setup.py index 3ef751a..4e1bbc2 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Always prefer setuptools over distutils from codecs import open # To use a consistent encoding from os import path