diff --git a/etc/keycloak/auth.json b/etc/keycloak/auth.json index 9b366b1f..6c661bbc 100644 --- a/etc/keycloak/auth.json +++ b/etc/keycloak/auth.json @@ -748,6 +748,30 @@ } ] }, + { + "name": "item_keyword", + "ownerManagedAccess": false, + "displayName": "Item_keyword", + "attributes": {}, + "_id": "558d5b97-1c6a-40b4-9f53-b597ba42dd64", + "uris": [ + "/api/item_keyword/{id}" + ], + "scopes": [ + { + "name": "item_keyword:update" + }, + { + "name": "item_keyword:delete" + }, + { + "name": "item_keyword:add" + }, + { + "name": "item_keyword:view" + } + ] + }, { "name": "item_marker", "ownerManagedAccess": false, @@ -916,6 +940,30 @@ } ] }, + { + "name": "keyword", + "ownerManagedAccess": false, + "displayName": "Keyword", + "attributes": {}, + "_id": "b61cec50-7643-4e89-953a-f6f73514a365", + "uris": [ + "/api/keyword/{id}" + ], + "scopes": [ + { + "name": "keyword:update" + }, + { + "name": "keyword:delete" + }, + { + "name": "keyword:add" + }, + { + "name": "keyword:view" + } + ] + }, { "name": "language", "ownerManagedAccess": false, @@ -1361,7 +1409,7 @@ "decisionStrategy": "AFFIRMATIVE", "config": { "roles": "[{\"id\":\"francoralite-users\",\"required\":true}]", - "scopes": "[\"location:view\",\"collection_location:view\",\"domain_tale:view\",\"item_usefulness:view\",\"media_item:view\",\"item_thematic:view\",\"item_informer:view\",\"musical_group:view\",\"instrument:view\",\"item_domain_tale:view\",\"authority:view\",\"fond:view\",\"performance_collection_musician:view\",\"acquisition_mode:view\",\"hornbostelsachs:view\",\"item_marker:view\",\"domain_song:view\",\"mission:view\",\"enumeration:view\",\"legal_rights:view\",\"publisher:view\",\"institution:view\",\"user:view\",\"collection_informer:view\",\"collection_publisher:view\",\"thematic:view\",\"item_domain_vocal:view\",\"item_musical_group:view\",\"musical_organization:view\",\"location_gis:view\",\"mediatype:view\",\"usefulness:view\",\"domain_vocal:view\",\"ext_media_item:view\",\"item_domain_music:view\",\"performance_collection:view\",\"item_collector:view\",\"collection_language:view\",\"emit_vox:view\",\"recording_context:view\",\"timeside_item:view\",\"item_analysis:view\",\"item_dance:view\",\"item:view\",\"coupe:view\",\"collectioncollectors:view\",\"language:view\",\"item_transcoding_flag:view\",\"dance:view\",\"collection:view\",\"item_musical_organization:view\",\"domain_music:view\",\"item_domain_song:view\",\"block:view\"]" + "scopes": "[\"location:view\",\"collection_location:view\",\"domain_tale:view\",\"item_usefulness:view\",\"media_item:view\",\"item_thematic:view\",\"item_informer:view\",\"musical_group:view\",\"instrument:view\",\"item_domain_tale:view\",\"authority:view\",\"fond:view\",\"performance_collection_musician:view\",\"acquisition_mode:view\",\"hornbostelsachs:view\",\"item_marker:view\",\"domain_song:view\",\"mission:view\",\"enumeration:view\",\"legal_rights:view\",\"publisher:view\",\"institution:view\",\"user:view\",\"collection_informer:view\",\"collection_publisher:view\",\"thematic:view\",\"item_domain_vocal:view\",\"item_musical_group:view\",\"musical_organization:view\",\"location_gis:view\",\"mediatype:view\",\"usefulness:view\",\"domain_vocal:view\",\"ext_media_item:view\",\"item_domain_music:view\",\"performance_collection:view\",\"item_collector:view\",\"collection_language:view\",\"emit_vox:view\",\"recording_context:view\",\"timeside_item:view\",\"item_analysis:view\",\"item_dance:view\",\"item:view\",\"coupe:view\",\"collectioncollectors:view\",\"language:view\",\"item_transcoding_flag:view\",\"dance:view\",\"collection:view\",\"item_musical_organization:view\",\"domain_music:view\",\"item_domain_song:view\",\"block:view\",\"keyword:view\",\"item_keyword:view\"]" } }, { @@ -1372,7 +1420,7 @@ "decisionStrategy": "AFFIRMATIVE", "config": { "roles": "[{\"id\":\"francoralite-contributors\",\"required\":true}]", - "scopes": "[\"collection_language:delete\",\"fond:add\",\"performance_collection:update\",\"item_usefulness:update\",\"location:view\",\"item_usefulness:delete\",\"authority:update\",\"collection_location:view\",\"item_usefulness:view\",\"item_domain_tale:delete\",\"item_domain_tale:add\",\"item_thematic:view\",\"item_domain_music:add\",\"item_informer:view\",\"collection_publisher:update\",\"item_thematic:add\",\"item_domain_tale:view\",\"authority:view\",\"collection_informer:add\",\"fond:view\",\"item_collector:add\",\"collection_location:update\",\"performance_collection_musician:view\",\"collectioncollectors:delete\",\"collection_language:update\",\"collectioncollectors:update\",\"item_usefulness:add\",\"mission:view\",\"location:add\",\"mission:update\",\"fond:delete\",\"collection_location:add\",\"item_informer:add\",\"authority:add\",\"item_collector:delete\",\"item_informer:delete\",\"performance_collection_musician:delete\",\"performance_collection_musician:add\",\"collection_publisher:add\",\"item_collector:update\",\"collection_informer:view\",\"collection_publisher:view\",\"item_domain_song:update\",\"mission:delete\",\"item:update\",\"collection:add\",\"item_thematic:delete\",\"collection_language:add\",\"location:delete\",\"fond:update\",\"mission:add\",\"item_domain_tale:update\",\"item_domain_music:view\",\"performance_collection:delete\",\"performance_collection:view\",\"collectioncollectors:add\",\"item_collector:view\",\"performance_collection_musician:update\",\"collection_language:view\",\"collection_informer:update\",\"item_dance:delete\",\"item_thematic:update\",\"location:update\",\"performance_collection:add\",\"item_dance:view\",\"item:view\",\"collection_publisher:delete\",\"collectioncollectors:view\",\"collection:update\",\"item_domain_music:delete\",\"item_informer:update\",\"item_domain_song:add\",\"collection_location:delete\",\"collection:delete\",\"collection:view\",\"item:delete\",\"item_domain_song:delete\",\"item_dance:add\",\"authority:delete\",\"collection_informer:delete\",\"item_dance:update\",\"item_domain_music:update\",\"item_domain_song:view\",\"item:add\",\"block:view\",\"location_gis:add\",\"location_gis:update\",\"location_gis:delete\",\"instrument:view\",\"legal_rights:view\",\"mediatype:view\",\"publisher:view\",\"recording_context:view\",\"musical_group:view\",\"item:view\"]" + "scopes": "[\"collection_language:delete\",\"fond:add\",\"performance_collection:update\",\"item_usefulness:update\",\"location:view\",\"item_usefulness:delete\",\"authority:update\",\"collection_location:view\",\"item_usefulness:view\",\"item_domain_tale:delete\",\"item_domain_tale:add\",\"item_thematic:view\",\"item_domain_music:add\",\"item_informer:view\",\"collection_publisher:update\",\"item_thematic:add\",\"item_domain_tale:view\",\"authority:view\",\"collection_informer:add\",\"fond:view\",\"item_collector:add\",\"collection_location:update\",\"performance_collection_musician:view\",\"collectioncollectors:delete\",\"collection_language:update\",\"collectioncollectors:update\",\"item_usefulness:add\",\"mission:view\",\"location:add\",\"mission:update\",\"fond:delete\",\"collection_location:add\",\"item_informer:add\",\"authority:add\",\"item_collector:delete\",\"item_informer:delete\",\"performance_collection_musician:delete\",\"performance_collection_musician:add\",\"collection_publisher:add\",\"item_collector:update\",\"collection_informer:view\",\"collection_publisher:view\",\"item_domain_song:update\",\"mission:delete\",\"item:update\",\"collection:add\",\"item_thematic:delete\",\"collection_language:add\",\"location:delete\",\"fond:update\",\"mission:add\",\"item_domain_tale:update\",\"item_domain_music:view\",\"performance_collection:delete\",\"performance_collection:view\",\"collectioncollectors:add\",\"item_collector:view\",\"performance_collection_musician:update\",\"collection_language:view\",\"collection_informer:update\",\"item_dance:delete\",\"item_thematic:update\",\"location:update\",\"performance_collection:add\",\"item_dance:view\",\"item:view\",\"collection_publisher:delete\",\"collectioncollectors:view\",\"collection:update\",\"item_domain_music:delete\",\"item_informer:update\",\"item_domain_song:add\",\"collection_location:delete\",\"collection:delete\",\"collection:view\",\"item:delete\",\"item_domain_song:delete\",\"item_dance:add\",\"authority:delete\",\"collection_informer:delete\",\"item_dance:update\",\"item_domain_music:update\",\"item_domain_song:view\",\"item:add\",\"block:view\",\"location_gis:add\",\"location_gis:update\",\"location_gis:delete\",\"instrument:view\",\"legal_rights:view\",\"mediatype:view\",\"publisher:view\",\"recording_context:view\",\"musical_group:view\",\"item:view\",\"keyword:add\",\"keyword:delete\",\"keyword:update\",\"keyword:view\",\"item_keyword:add\",\"item_keyword:delete\",\"item_keyword:update\",\"item_keyword:view\"]" } }, { @@ -1383,7 +1431,7 @@ "decisionStrategy": "AFFIRMATIVE", "config": { "roles": "[{\"id\":\"francoralite-administrators\",\"required\":true}]", - "scopes": "[\"fond:add\",\"performance_collection:update\",\"user:update\",\"item_usefulness:delete\",\"item_informer:view\",\"musical_group:view\",\"publisher:update\",\"instrument:view\",\"item_musical_organization:add\",\"fond:view\",\"collectioncollectors:delete\",\"acquisition_mode:view\",\"hornbostelsachs:view\",\"item_analysis:delete\",\"hornbostelsachs:add\",\"domain_song:view\",\"timeside_item:delete\",\"enumeration:view\",\"fond:delete\",\"authority:add\",\"item_musical_group:delete\",\"item_collector:delete\",\"publisher:view\",\"item_informer:delete\",\"usefulness:add\",\"coupe:add\",\"collection_publisher:view\",\"item_musical_organization:delete\",\"thematic:view\",\"dance:add\",\"item:update\",\"user:add\",\"musical_organization:update\",\"coupe:delete\",\"legal_rights:update\",\"collection_language:add\",\"instrument:delete\",\"location:delete\",\"domain_music:add\",\"ext_media_item:view\",\"item_collector:view\",\"recording_context:view\",\"usefulness:delete\",\"collection_informer:update\",\"hornbostelsachs:update\",\"domain_vocal:update\",\"ext_media_item:update\",\"item_domain_music:delete\",\"collection:delete\",\"instrument:update\",\"collection_language:delete\",\"enumeration:add\",\"item_usefulness:update\",\"domain_tale:view\",\"item_domain_tale:add\",\"musical_group:delete\",\"domain_song:update\",\"domain_tale:add\",\"location_gis:delete\",\"acquisition_mode:delete\",\"item_domain_tale:view\",\"item_collector:add\",\"emit_vox:delete\",\"collectioncollectors:update\",\"item_marker:view\",\"item_usefulness:add\",\"language:delete\",\"collection_location:add\",\"item_analysis:update\",\"domain_tale:update\",\"institution:view\",\"performance_collection_musician:add\",\"collection_publisher:add\",\"user:view\",\"item_domain_song:update\",\"item_marker:delete\",\"mission:delete\",\"usefulness:update\",\"domain_music:update\",\"hornbostelsachs:delete\",\"fond:update\",\"acquisition_mode:add\",\"mediatype:view\",\"location_gis:add\",\"recording_context:add\",\"institution:add\",\"emit_vox:view\",\"item_dance:delete\",\"performance_collection:add\",\"item:view\",\"collection_publisher:delete\",\"collection:update\",\"dance:view\",\"item:delete\",\"item_domain_music:update\",\"item_domain_song:view\",\"item:add\",\"mediatype:delete\",\"musical_organization:add\",\"location:view\",\"authority:update\",\"collection_location:view\",\"item_usefulness:view\",\"thematic:update\",\"item_thematic:view\",\"mediatype:add\",\"item_domain_music:add\",\"authority:view\",\"musical_organization:delete\",\"performance_collection_musician:view\",\"media_item:update\",\"item_domain_vocal:delete\",\"mission:view\",\"domain_vocal:delete\",\"location:add\",\"language:add\",\"emit_vox:update\",\"ext_media_item:delete\",\"dance:update\",\"item_analysis:add\",\"emit_vox:add\",\"performance_collection_musician:delete\",\"ext_media_item:add\",\"item_collector:update\",\"dance:delete\",\"item_transcoding_flag:add\",\"item_domain_vocal:view\",\"acquisition_mode:update\",\"item_thematic:delete\",\"domain_vocal:add\",\"item_musical_group:view\",\"musical_organization:view\",\"item_domain_vocal:add\",\"mission:add\",\"usefulness:view\",\"domain_vocal:view\",\"item_domain_tale:update\",\"enumeration:delete\",\"collectioncollectors:add\",\"performance_collection_musician:update\",\"collection_language:view\",\"timeside_item:view\",\"timeside_item:add\",\"item_analysis:view\",\"item_dance:view\",\"coupe:view\",\"collectioncollectors:view\",\"language:view\",\"item_domain_song:add\",\"media_item:delete\",\"item_musical_organization:update\",\"item_musical_organization:view\",\"collection_informer:delete\",\"domain_music:view\",\"domain_tale:delete\",\"media_item:view\",\"item_domain_tale:delete\",\"location_gis:update\",\"media_item:add\",\"recording_context:delete\",\"collection_publisher:update\",\"item_thematic:add\",\"collection_informer:add\",\"item_musical_group:update\",\"collection_location:update\",\"collection_language:update\",\"item_marker:update\",\"item_transcoding_flag:update\",\"musical_group:update\",\"user:delete\",\"institution:delete\",\"thematic:add\",\"mission:update\",\"publisher:add\",\"coupe:update\",\"item_informer:add\",\"legal_rights:view\",\"legal_rights:add\",\"domain_song:delete\",\"collection_informer:view\",\"domain_music:delete\",\"enumeration:update\",\"instrument:add\",\"domain_song:add\",\"collection:add\",\"item_musical_group:add\",\"item_transcoding_flag:delete\",\"publisher:delete\",\"item_marker:add\",\"location_gis:view\",\"language:update\",\"mediatype:update\",\"timeside_item:update\",\"item_domain_music:view\",\"performance_collection:delete\",\"institution:update\",\"performance_collection:view\",\"musical_group:add\",\"item_thematic:update\",\"location:update\",\"legal_rights:delete\",\"item_informer:update\",\"item_transcoding_flag:view\",\"recording_context:update\",\"collection_location:delete\",\"collection:view\",\"item_domain_song:delete\",\"item_dance:add\",\"item_domain_vocal:update\",\"authority:delete\",\"thematic:delete\",\"item_dance:update\"]" + "scopes": "[\"fond:add\",\"performance_collection:update\",\"user:update\",\"item_usefulness:delete\",\"item_informer:view\",\"musical_group:view\",\"publisher:update\",\"instrument:view\",\"item_musical_organization:add\",\"fond:view\",\"collectioncollectors:delete\",\"acquisition_mode:view\",\"hornbostelsachs:view\",\"item_analysis:delete\",\"hornbostelsachs:add\",\"domain_song:view\",\"timeside_item:delete\",\"enumeration:view\",\"fond:delete\",\"authority:add\",\"item_musical_group:delete\",\"item_collector:delete\",\"publisher:view\",\"item_informer:delete\",\"usefulness:add\",\"coupe:add\",\"collection_publisher:view\",\"item_musical_organization:delete\",\"thematic:view\",\"dance:add\",\"item:update\",\"user:add\",\"musical_organization:update\",\"coupe:delete\",\"legal_rights:update\",\"collection_language:add\",\"instrument:delete\",\"location:delete\",\"domain_music:add\",\"ext_media_item:view\",\"item_collector:view\",\"recording_context:view\",\"usefulness:delete\",\"collection_informer:update\",\"hornbostelsachs:update\",\"domain_vocal:update\",\"ext_media_item:update\",\"item_domain_music:delete\",\"collection:delete\",\"instrument:update\",\"collection_language:delete\",\"enumeration:add\",\"item_usefulness:update\",\"domain_tale:view\",\"item_domain_tale:add\",\"musical_group:delete\",\"domain_song:update\",\"domain_tale:add\",\"location_gis:delete\",\"acquisition_mode:delete\",\"item_domain_tale:view\",\"item_collector:add\",\"emit_vox:delete\",\"collectioncollectors:update\",\"item_marker:view\",\"item_usefulness:add\",\"language:delete\",\"collection_location:add\",\"item_analysis:update\",\"domain_tale:update\",\"institution:view\",\"performance_collection_musician:add\",\"collection_publisher:add\",\"user:view\",\"item_domain_song:update\",\"item_marker:delete\",\"mission:delete\",\"usefulness:update\",\"domain_music:update\",\"hornbostelsachs:delete\",\"fond:update\",\"acquisition_mode:add\",\"mediatype:view\",\"location_gis:add\",\"recording_context:add\",\"institution:add\",\"emit_vox:view\",\"item_dance:delete\",\"performance_collection:add\",\"item:view\",\"collection_publisher:delete\",\"collection:update\",\"dance:view\",\"item:delete\",\"item_domain_music:update\",\"item_domain_song:view\",\"item:add\",\"mediatype:delete\",\"musical_organization:add\",\"location:view\",\"authority:update\",\"collection_location:view\",\"item_usefulness:view\",\"thematic:update\",\"item_thematic:view\",\"mediatype:add\",\"item_domain_music:add\",\"authority:view\",\"musical_organization:delete\",\"performance_collection_musician:view\",\"media_item:update\",\"item_domain_vocal:delete\",\"mission:view\",\"domain_vocal:delete\",\"location:add\",\"language:add\",\"emit_vox:update\",\"ext_media_item:delete\",\"dance:update\",\"item_analysis:add\",\"emit_vox:add\",\"performance_collection_musician:delete\",\"ext_media_item:add\",\"item_collector:update\",\"dance:delete\",\"item_transcoding_flag:add\",\"item_domain_vocal:view\",\"acquisition_mode:update\",\"item_thematic:delete\",\"domain_vocal:add\",\"item_musical_group:view\",\"musical_organization:view\",\"item_domain_vocal:add\",\"mission:add\",\"usefulness:view\",\"domain_vocal:view\",\"item_domain_tale:update\",\"enumeration:delete\",\"collectioncollectors:add\",\"performance_collection_musician:update\",\"collection_language:view\",\"timeside_item:view\",\"timeside_item:add\",\"item_analysis:view\",\"item_dance:view\",\"coupe:view\",\"collectioncollectors:view\",\"language:view\",\"item_domain_song:add\",\"media_item:delete\",\"item_musical_organization:update\",\"item_musical_organization:view\",\"collection_informer:delete\",\"domain_music:view\",\"domain_tale:delete\",\"media_item:view\",\"item_domain_tale:delete\",\"location_gis:update\",\"media_item:add\",\"recording_context:delete\",\"collection_publisher:update\",\"item_thematic:add\",\"collection_informer:add\",\"item_musical_group:update\",\"collection_location:update\",\"collection_language:update\",\"item_marker:update\",\"item_transcoding_flag:update\",\"musical_group:update\",\"user:delete\",\"institution:delete\",\"thematic:add\",\"mission:update\",\"publisher:add\",\"coupe:update\",\"item_informer:add\",\"legal_rights:view\",\"legal_rights:add\",\"domain_song:delete\",\"collection_informer:view\",\"domain_music:delete\",\"enumeration:update\",\"instrument:add\",\"domain_song:add\",\"collection:add\",\"item_musical_group:add\",\"item_transcoding_flag:delete\",\"publisher:delete\",\"item_marker:add\",\"location_gis:view\",\"language:update\",\"mediatype:update\",\"timeside_item:update\",\"item_domain_music:view\",\"performance_collection:delete\",\"institution:update\",\"performance_collection:view\",\"musical_group:add\",\"item_thematic:update\",\"location:update\",\"legal_rights:delete\",\"item_informer:update\",\"item_transcoding_flag:view\",\"recording_context:update\",\"collection_location:delete\",\"collection:view\",\"item_domain_song:delete\",\"item_dance:add\",\"item_domain_vocal:update\",\"authority:delete\",\"thematic:delete\",\"item_dance:update\",\"keyword:add\",\"keyword:delete\",\"keyword:update\",\"keyword:view\",\"item_keyword:add\",\"item_keyword:delete\",\"item_keyword:update\",\"item_keyword:view\"]" } }, { @@ -1393,7 +1441,7 @@ "logic": "POSITIVE", "decisionStrategy": "UNANIMOUS", "config": { - "scopes": "[\"skos_concept:view\",\"location:view\",\"collection_location:view\",\"domain_tale:view\",\"item_usefulness:view\",\"media_item:view\",\"item_thematic:view\",\"item_informer:view\",\"musical_group:view\",\"instrument:view\",\"item_domain_tale:view\",\"authority:view\",\"fond:view\",\"performance_collection_musician:view\",\"acquisition_mode:view\",\"hornbostelsachs:view\",\"item_marker:view\",\"domain_song:view\",\"mission:view\",\"enumeration:view\",\"coirault:view\",\"legal_rights:view\",\"publisher:view\",\"institution:view\",\"user:view\",\"collection_informer:view\",\"collection_publisher:view\",\"globalsearch\",\"thematic:view\",\"item_domain_vocal:view\",\"item_musical_group:view\",\"musical_organization:view\",\"location_gis:view\",\"mediatype:view\",\"usefulness:view\",\"domain_vocal:view\",\"ext_media_item:view\",\"item_domain_music:view\",\"performance_collection:view\",\"item_collector:view\",\"collection_language:view\",\"emit_vox:view\",\"recording_context:view\",\"timeside_item:view\",\"item_analysis:view\",\"item_dance:view\",\"coupe:view\",\"collectioncollectors:view\",\"language:view\",\"item_transcoding_flag:view\",\"dance:view\",\"collection:view\",\"item_musical_organization:view\",\"domain_music:view\",\"item_domain_song:view\",\"block:view\",\"item:view\"]", + "scopes": "[\"skos_concept:view\",\"location:view\",\"collection_location:view\",\"domain_tale:view\",\"item_usefulness:view\",\"media_item:view\",\"item_thematic:view\",\"item_informer:view\",\"musical_group:view\",\"instrument:view\",\"item_domain_tale:view\",\"authority:view\",\"fond:view\",\"performance_collection_musician:view\",\"acquisition_mode:view\",\"hornbostelsachs:view\",\"item_marker:view\",\"domain_song:view\",\"mission:view\",\"enumeration:view\",\"coirault:view\",\"legal_rights:view\",\"publisher:view\",\"institution:view\",\"user:view\",\"collection_informer:view\",\"collection_publisher:view\",\"globalsearch\",\"thematic:view\",\"item_domain_vocal:view\",\"item_musical_group:view\",\"musical_organization:view\",\"location_gis:view\",\"mediatype:view\",\"usefulness:view\",\"domain_vocal:view\",\"ext_media_item:view\",\"item_domain_music:view\",\"performance_collection:view\",\"item_collector:view\",\"collection_language:view\",\"emit_vox:view\",\"recording_context:view\",\"timeside_item:view\",\"item_analysis:view\",\"item_dance:view\",\"coupe:view\",\"collectioncollectors:view\",\"language:view\",\"item_transcoding_flag:view\",\"dance:view\",\"collection:view\",\"item_musical_organization:view\",\"domain_music:view\",\"item_domain_song:view\",\"block:view\",\"item:view\",\"keyword:view\",\"item_keyword:view\"]", "applyPolicies": "[\"francoralite-users-pol\"]" } }, @@ -1416,7 +1464,7 @@ "logic": "POSITIVE", "decisionStrategy": "UNANIMOUS", "config": { - "scopes": "[\"fond:add\",\"item_coirault:add\",\"performance_collection:update\",\"document:add\",\"location:view\",\"item_usefulness:delete\",\"authority:update\",\"collection_location:view\",\"item_usefulness:view\",\"document_fonds:delete\",\"item_thematic:view\",\"item_domain_music:add\",\"item_informer:view\",\"item_musical_organization:add\",\"item_coirault:delete\",\"authority:view\",\"fond:view\",\"item_author:update\",\"performance_collection_musician:view\",\"collectioncollectors:delete\",\"acquisition_mode:view\",\"hornbostelsachs:view\",\"item_domain_vocal:delete\",\"domain_song:view\",\"mission:view\",\"enumeration:view\",\"item_author:delete\",\"location:add\",\"document_collection:add\",\"fond:delete\",\"authority:add\",\"item_analysis:add\",\"item_language:add\",\"item_musical_group:delete\",\"item_coirault:update\",\"item_collector:delete\",\"item_informer:delete\",\"performance_collection_musician:delete\",\"item_collector:update\",\"collection_publisher:view\",\"item_musical_organization:delete\",\"document_item:add\",\"thematic:view\",\"item_compositor:add\",\"item_domain_vocal:view\",\"item:update\",\"item_thematic:delete\",\"document:view\",\"document:update\",\"collection_language:add\",\"location:delete\",\"item_musical_group:view\",\"musical_organization:view\",\"item_domain_vocal:add\",\"mission:add\",\"item_coirault:view\",\"usefulness:view\",\"domain_vocal:view\",\"ext_media_item:view\",\"item_domain_tale:update\",\"item_compositor:update\",\"collectioncollectors:add\",\"item_collector:view\",\"performance_collection_musician:update\",\"collection_language:view\",\"collection_informer:update\",\"document_collection:view\",\"item_dance:view\",\"coupe:view\",\"collectioncollectors:view\",\"language:view\",\"document_mission:view\",\"item_domain_music:delete\",\"item_domain_song:add\",\"item_musical_organization:update\",\"collection:delete\",\"item_musical_organization:view\",\"collection_informer:delete\",\"domain_music:view\",\"document_fonds:update\",\"collection_language:delete\",\"item_performance:update\",\"skos_concept:view\",\"item_usefulness:update\",\"domain_tale:view\",\"item_performance:add\",\"document_mission:add\",\"item_domain_tale:delete\",\"item_domain_tale:add\",\"document_mission:delete\",\"document_item:update\",\"document_collection:delete\",\"item_compositor:delete\",\"item_thematic:add\",\"item_domain_tale:view\",\"collection_informer:add\",\"item_musical_group:update\",\"item_collector:add\",\"document_collection:update\",\"collection_location:update\",\"collection_language:update\",\"collectioncollectors:update\",\"item_usefulness:add\",\"mission:update\",\"item_performance:view\",\"item_language:delete\",\"collection_location:add\",\"coirault:view\",\"item_informer:add\",\"document_item:view\",\"institution:view\",\"performance_collection_musician:add\",\"collection_publisher:add\",\"collection_informer:view\",\"document_mission:update\",\"item_language:update\",\"item_domain_song:update\",\"item_author:view\",\"globalsearch\",\"document_fonds:add\",\"mission:delete\",\"collection:add\",\"item_musical_group:add\",\"fond:update\",\"location_gis:view\",\"document_item:delete\",\"item_domain_music:view\",\"performance_collection:delete\",\"performance_collection:view\",\"emit_vox:view\",\"item_dance:delete\",\"document_fonds:view\",\"item_thematic:update\",\"location:update\",\"performance_collection:add\",\"item:view\",\"collection_publisher:delete\",\"collection:update\",\"item_informer:update\",\"item_author:add\",\"item_language:view\",\"dance:view\",\"document:delete\",\"collection_location:delete\",\"collection:view\",\"item:delete\",\"item_domain_song:delete\",\"item_dance:add\",\"item_domain_vocal:update\",\"authority:delete\",\"item_performance:delete\",\"item_dance:update\",\"item_domain_music:update\",\"item_domain_song:view\",\"item:add\",\"item_compositor:view\",\"block:view\",\"location_gis:add\",\"location_gis:update\",\"location_gis:delete\",\"instrument:view\",\"legal_rights:view\",\"mediatype:view\",\"publisher:view\",\"recording_context:view\",\"musical_group:view\"]", + "scopes": "[\"fond:add\",\"item_coirault:add\",\"performance_collection:update\",\"document:add\",\"location:view\",\"item_usefulness:delete\",\"authority:update\",\"collection_location:view\",\"item_usefulness:view\",\"document_fonds:delete\",\"item_thematic:view\",\"item_domain_music:add\",\"item_informer:view\",\"item_musical_organization:add\",\"item_coirault:delete\",\"authority:view\",\"fond:view\",\"item_author:update\",\"performance_collection_musician:view\",\"collectioncollectors:delete\",\"acquisition_mode:view\",\"hornbostelsachs:view\",\"item_domain_vocal:delete\",\"domain_song:view\",\"mission:view\",\"enumeration:view\",\"item_author:delete\",\"location:add\",\"document_collection:add\",\"fond:delete\",\"authority:add\",\"item_analysis:add\",\"item_language:add\",\"item_musical_group:delete\",\"item_coirault:update\",\"item_collector:delete\",\"item_informer:delete\",\"performance_collection_musician:delete\",\"item_collector:update\",\"collection_publisher:view\",\"item_musical_organization:delete\",\"document_item:add\",\"thematic:view\",\"item_compositor:add\",\"item_domain_vocal:view\",\"item:update\",\"item_thematic:delete\",\"document:view\",\"document:update\",\"collection_language:add\",\"location:delete\",\"item_musical_group:view\",\"musical_organization:view\",\"item_domain_vocal:add\",\"mission:add\",\"item_coirault:view\",\"usefulness:view\",\"domain_vocal:view\",\"ext_media_item:view\",\"item_domain_tale:update\",\"item_compositor:update\",\"collectioncollectors:add\",\"item_collector:view\",\"performance_collection_musician:update\",\"collection_language:view\",\"collection_informer:update\",\"document_collection:view\",\"item_dance:view\",\"coupe:view\",\"collectioncollectors:view\",\"language:view\",\"document_mission:view\",\"item_domain_music:delete\",\"item_domain_song:add\",\"item_musical_organization:update\",\"collection:delete\",\"item_musical_organization:view\",\"collection_informer:delete\",\"domain_music:view\",\"document_fonds:update\",\"collection_language:delete\",\"item_performance:update\",\"skos_concept:view\",\"item_usefulness:update\",\"domain_tale:view\",\"item_performance:add\",\"document_mission:add\",\"item_domain_tale:delete\",\"item_domain_tale:add\",\"document_mission:delete\",\"document_item:update\",\"document_collection:delete\",\"item_compositor:delete\",\"item_thematic:add\",\"item_domain_tale:view\",\"collection_informer:add\",\"item_musical_group:update\",\"item_collector:add\",\"document_collection:update\",\"collection_location:update\",\"collection_language:update\",\"collectioncollectors:update\",\"item_usefulness:add\",\"mission:update\",\"item_performance:view\",\"item_language:delete\",\"collection_location:add\",\"coirault:view\",\"item_informer:add\",\"document_item:view\",\"institution:view\",\"performance_collection_musician:add\",\"collection_publisher:add\",\"collection_informer:view\",\"document_mission:update\",\"item_language:update\",\"item_domain_song:update\",\"item_author:view\",\"globalsearch\",\"document_fonds:add\",\"mission:delete\",\"collection:add\",\"item_musical_group:add\",\"fond:update\",\"location_gis:view\",\"document_item:delete\",\"item_domain_music:view\",\"performance_collection:delete\",\"performance_collection:view\",\"emit_vox:view\",\"item_dance:delete\",\"document_fonds:view\",\"item_thematic:update\",\"location:update\",\"performance_collection:add\",\"item:view\",\"collection_publisher:delete\",\"collection:update\",\"item_informer:update\",\"item_author:add\",\"item_language:view\",\"dance:view\",\"document:delete\",\"collection_location:delete\",\"collection:view\",\"item:delete\",\"item_domain_song:delete\",\"item_dance:add\",\"item_domain_vocal:update\",\"authority:delete\",\"item_performance:delete\",\"item_dance:update\",\"item_domain_music:update\",\"item_domain_song:view\",\"item:add\",\"item_compositor:view\",\"block:view\",\"location_gis:add\",\"location_gis:update\",\"location_gis:delete\",\"instrument:view\",\"legal_rights:view\",\"mediatype:view\",\"publisher:view\",\"recording_context:view\",\"musical_group:view\",\"keyword:add\",\"keyword:delete\",\"keyword:update\",\"keyword:view\",\"item_keyword:add\",\"item_keyword:delete\",\"item_keyword:update\",\"item_keyword:view\"]", "applyPolicies": "[\"francoralite-contributors-pol\"]" } }, @@ -1427,7 +1475,7 @@ "logic": "POSITIVE", "decisionStrategy": "UNANIMOUS", "config": { - "scopes": "[\"fond:add\",\"performance_collection:update\",\"item_usefulness:delete\",\"document_fonds:delete\",\"publisher:update\",\"item_informer:view\",\"musical_group:view\",\"item_musical_organization:add\",\"instrument:view\",\"item_coirault:delete\",\"fond:view\",\"item_author:update\",\"collectioncollectors:delete\",\"acquisition_mode:view\",\"hornbostelsachs:view\",\"item_analysis:delete\",\"hornbostelsachs:add\",\"domain_song:view\",\"enumeration:view\",\"fond:delete\",\"authority:add\",\"item_language:add\",\"item_musical_group:delete\",\"item_coirault:update\",\"publisher:view\",\"item_collector:delete\",\"item_informer:delete\",\"usefulness:add\",\"coupe:add\",\"collection_publisher:view\",\"item_musical_organization:delete\",\"thematic:view\",\"dance:add\",\"item:update\",\"musical_organization:update\",\"document:view\",\"coupe:delete\",\"legal_rights:update\",\"collection_language:add\",\"instrument:delete\",\"location:delete\",\"domain_music:add\",\"ext_media_item:view\",\"item_collector:view\",\"recording_context:view\",\"usefulness:delete\",\"collection_informer:update\",\"document_collection:view\",\"hornbostelsachs:update\",\"domain_vocal:update\",\"ext_media_item:update\",\"item_domain_music:delete\",\"collection:delete\",\"instrument:update\",\"document_fonds:update\",\"collection_language:delete\",\"enumeration:add\",\"skos_concept:view\",\"item_usefulness:update\",\"domain_tale:view\",\"item_performance:add\",\"item_domain_tale:add\",\"document_item:update\",\"musical_group:delete\",\"domain_song:update\",\"domain_tale:add\",\"document_collection:delete\",\"location_gis:delete\",\"acquisition_mode:delete\",\"item_domain_tale:view\",\"item_collector:add\",\"document_collection:update\",\"emit_vox:delete\",\"collectioncollectors:update\",\"item_usefulness:add\",\"item_marker:view\",\"language:delete\",\"item_performance:view\",\"collection_location:add\",\"document_item:view\",\"item_analysis:update\",\"domain_tale:update\",\"institution:view\",\"performance_collection_musician:add\",\"collection_publisher:add\",\"document_mission:update\",\"item_language:update\",\"item_domain_song:update\",\"item_author:view\",\"item_marker:delete\",\"usefulness:update\",\"domain_music:update\",\"hornbostelsachs:delete\",\"fond:update\",\"acquisition_mode:add\",\"mediatype:view\",\"location_gis:add\",\"document_item:delete\",\"recording_context:add\",\"institution:add\",\"emit_vox:view\",\"item_dance:delete\",\"performance_collection:add\",\"item:view\",\"collection_publisher:delete\",\"collection:update\",\"dance:view\",\"document:delete\",\"item:delete\",\"item_performance:delete\",\"item_domain_song:view\",\"item_domain_music:update\",\"item:add\",\"item_compositor:view\",\"mediatype:delete\",\"musical_organization:add\",\"item_coirault:add\",\"document:add\",\"location:view\",\"authority:update\",\"item_usefulness:view\",\"collection_location:view\",\"thematic:update\",\"item_thematic:view\",\"mediatype:add\",\"item_domain_music:add\",\"authority:view\",\"musical_organization:delete\",\"performance_collection_musician:view\",\"item_author:delete\",\"domain_vocal:delete\",\"location:add\",\"language:add\",\"document_collection:add\",\"emit_vox:update\",\"ext_media_item:delete\",\"dance:update\",\"item_analysis:add\",\"emit_vox:add\",\"performance_collection_musician:delete\",\"ext_media_item:add\",\"item_collector:update\",\"dance:delete\",\"document_item:add\",\"item_transcoding_flag:add\",\"item_compositor:add\",\"acquisition_mode:update\",\"item_thematic:delete\",\"document:update\",\"domain_vocal:add\",\"item_musical_group:view\",\"musical_organization:view\",\"item_domain_vocal:add\",\"item_coirault:view\",\"usefulness:view\",\"domain_vocal:view\",\"item_domain_tale:update\",\"enumeration:delete\",\"item_compositor:update\",\"collectioncollectors:add\",\"performance_collection_musician:update\",\"collection_language:view\",\"item_analysis:view\",\"item_dance:view\",\"coupe:view\",\"collectioncollectors:view\",\"language:view\",\"document_mission:view\",\"item_domain_song:add\",\"item_musical_organization:update\",\"item_musical_organization:view\",\"collection_informer:delete\",\"domain_music:view\",\"item_performance:update\",\"domain_tale:delete\",\"document_mission:add\",\"location_gis:update\",\"item_domain_tale:delete\",\"document_mission:delete\",\"recording_context:delete\",\"item_compositor:delete\",\"collection_publisher:update\",\"item_thematic:add\",\"collection_informer:add\",\"item_musical_group:update\",\"collection_location:update\",\"collection_language:update\",\"item_transcoding_flag:update\",\"item_marker:update\",\"musical_group:update\",\"institution:delete\",\"thematic:add\",\"item_language:delete\",\"publisher:add\",\"coirault:view\",\"item_informer:add\",\"coupe:update\",\"legal_rights:view\",\"legal_rights:add\",\"domain_song:delete\",\"collection_informer:view\",\"domain_music:delete\",\"enumeration:update\",\"instrument:add\",\"globalsearch\",\"domain_song:add\",\"document_fonds:add\",\"collection:add\",\"item_musical_group:add\",\"publisher:delete\",\"item_transcoding_flag:delete\",\"item_marker:add\",\"location_gis:view\",\"mediatype:update\",\"language:update\",\"item_domain_music:view\",\"performance_collection:delete\",\"institution:update\",\"performance_collection:view\",\"musical_group:add\",\"document_fonds:view\",\"item_thematic:update\",\"location:update\",\"legal_rights:delete\",\"item_informer:update\",\"item_author:add\",\"item_transcoding_flag:view\",\"recording_context:update\",\"item_language:view\",\"collection_location:delete\",\"collection:view\",\"item_domain_song:delete\",\"item_dance:add\",\"authority:delete\",\"thematic:delete\",\"item_dance:update\",\"block:view\",\"block:add\",\"block:delete\",\"block:update\",\"mission:view\",\"mission:add\",\"mission:update\",\"mission:delete\"]", + "scopes": "[\"fond:add\",\"performance_collection:update\",\"item_usefulness:delete\",\"document_fonds:delete\",\"publisher:update\",\"item_informer:view\",\"musical_group:view\",\"item_musical_organization:add\",\"instrument:view\",\"item_coirault:delete\",\"fond:view\",\"item_author:update\",\"collectioncollectors:delete\",\"acquisition_mode:view\",\"hornbostelsachs:view\",\"item_analysis:delete\",\"hornbostelsachs:add\",\"domain_song:view\",\"enumeration:view\",\"fond:delete\",\"authority:add\",\"item_language:add\",\"item_musical_group:delete\",\"item_coirault:update\",\"publisher:view\",\"item_collector:delete\",\"item_informer:delete\",\"usefulness:add\",\"coupe:add\",\"collection_publisher:view\",\"item_musical_organization:delete\",\"thematic:view\",\"dance:add\",\"item:update\",\"musical_organization:update\",\"document:view\",\"coupe:delete\",\"legal_rights:update\",\"collection_language:add\",\"instrument:delete\",\"location:delete\",\"domain_music:add\",\"ext_media_item:view\",\"item_collector:view\",\"recording_context:view\",\"usefulness:delete\",\"collection_informer:update\",\"document_collection:view\",\"hornbostelsachs:update\",\"domain_vocal:update\",\"ext_media_item:update\",\"item_domain_music:delete\",\"collection:delete\",\"instrument:update\",\"document_fonds:update\",\"collection_language:delete\",\"enumeration:add\",\"skos_concept:view\",\"item_usefulness:update\",\"domain_tale:view\",\"item_performance:add\",\"item_domain_tale:add\",\"document_item:update\",\"musical_group:delete\",\"domain_song:update\",\"domain_tale:add\",\"document_collection:delete\",\"location_gis:delete\",\"acquisition_mode:delete\",\"item_domain_tale:view\",\"item_collector:add\",\"document_collection:update\",\"emit_vox:delete\",\"collectioncollectors:update\",\"item_usefulness:add\",\"item_marker:view\",\"language:delete\",\"item_performance:view\",\"collection_location:add\",\"document_item:view\",\"item_analysis:update\",\"domain_tale:update\",\"institution:view\",\"performance_collection_musician:add\",\"collection_publisher:add\",\"document_mission:update\",\"item_language:update\",\"item_domain_song:update\",\"item_author:view\",\"item_marker:delete\",\"usefulness:update\",\"domain_music:update\",\"hornbostelsachs:delete\",\"fond:update\",\"acquisition_mode:add\",\"mediatype:view\",\"location_gis:add\",\"document_item:delete\",\"recording_context:add\",\"institution:add\",\"emit_vox:view\",\"item_dance:delete\",\"performance_collection:add\",\"item:view\",\"collection_publisher:delete\",\"collection:update\",\"dance:view\",\"document:delete\",\"item:delete\",\"item_performance:delete\",\"item_domain_song:view\",\"item_domain_music:update\",\"item:add\",\"item_compositor:view\",\"mediatype:delete\",\"musical_organization:add\",\"item_coirault:add\",\"document:add\",\"location:view\",\"authority:update\",\"item_usefulness:view\",\"collection_location:view\",\"thematic:update\",\"item_thematic:view\",\"mediatype:add\",\"item_domain_music:add\",\"authority:view\",\"musical_organization:delete\",\"performance_collection_musician:view\",\"item_author:delete\",\"domain_vocal:delete\",\"location:add\",\"language:add\",\"document_collection:add\",\"emit_vox:update\",\"ext_media_item:delete\",\"dance:update\",\"item_analysis:add\",\"emit_vox:add\",\"performance_collection_musician:delete\",\"ext_media_item:add\",\"item_collector:update\",\"dance:delete\",\"document_item:add\",\"item_transcoding_flag:add\",\"item_compositor:add\",\"acquisition_mode:update\",\"item_thematic:delete\",\"document:update\",\"domain_vocal:add\",\"item_musical_group:view\",\"musical_organization:view\",\"item_domain_vocal:add\",\"item_coirault:view\",\"usefulness:view\",\"domain_vocal:view\",\"item_domain_tale:update\",\"enumeration:delete\",\"item_compositor:update\",\"collectioncollectors:add\",\"performance_collection_musician:update\",\"collection_language:view\",\"item_analysis:view\",\"item_dance:view\",\"coupe:view\",\"collectioncollectors:view\",\"language:view\",\"document_mission:view\",\"item_domain_song:add\",\"item_musical_organization:update\",\"item_musical_organization:view\",\"collection_informer:delete\",\"domain_music:view\",\"item_performance:update\",\"domain_tale:delete\",\"document_mission:add\",\"location_gis:update\",\"item_domain_tale:delete\",\"document_mission:delete\",\"recording_context:delete\",\"item_compositor:delete\",\"collection_publisher:update\",\"item_thematic:add\",\"collection_informer:add\",\"item_musical_group:update\",\"collection_location:update\",\"collection_language:update\",\"item_transcoding_flag:update\",\"item_marker:update\",\"musical_group:update\",\"institution:delete\",\"thematic:add\",\"item_language:delete\",\"publisher:add\",\"coirault:view\",\"item_informer:add\",\"coupe:update\",\"legal_rights:view\",\"legal_rights:add\",\"domain_song:delete\",\"collection_informer:view\",\"domain_music:delete\",\"enumeration:update\",\"instrument:add\",\"globalsearch\",\"domain_song:add\",\"document_fonds:add\",\"collection:add\",\"item_musical_group:add\",\"publisher:delete\",\"item_transcoding_flag:delete\",\"item_marker:add\",\"location_gis:view\",\"mediatype:update\",\"language:update\",\"item_domain_music:view\",\"performance_collection:delete\",\"institution:update\",\"performance_collection:view\",\"musical_group:add\",\"document_fonds:view\",\"item_thematic:update\",\"location:update\",\"legal_rights:delete\",\"item_informer:update\",\"item_author:add\",\"item_transcoding_flag:view\",\"recording_context:update\",\"item_language:view\",\"collection_location:delete\",\"collection:view\",\"item_domain_song:delete\",\"item_dance:add\",\"authority:delete\",\"thematic:delete\",\"item_dance:update\",\"block:view\",\"block:add\",\"block:delete\",\"block:update\",\"mission:view\",\"mission:add\",\"mission:update\",\"mission:delete\",\"keyword:add\",\"keyword:delete\",\"keyword:update\",\"keyword:view\",\"item_keyword:add\",\"item_keyword:delete\",\"item_keyword:update\",\"item_keyword:view\"]", "applyPolicies": "[\"francoralite-administrators-pol\"]" } } @@ -1917,6 +1965,22 @@ "id": "8529432f-7acc-4f75-bc7e-aa7af2f068aa", "name": "item_informer:update" }, + { + "id": "6a4947e5-6936-49cd-84a2-dfe1f907b058", + "name": "item_keyword:delete" + }, + { + "id": "c95ca5b1-2da4-468e-ac24-a7b666dbbc0e", + "name": "item_keyword:view" + }, + { + "id": "4eec8675-4b86-4df7-9f7f-7429b131b58c", + "name": "item_keyword:add" + }, + { + "id": "0390d13a-8896-43d0-9ae1-f687b7fcc6b5", + "name": "item_keyword:update" + }, { "id": "835503d8-7873-4139-bbf5-9bd46a4d7555", "name": "item_marker:delete" @@ -2029,6 +2093,22 @@ "id": "8d38ebb7-2e06-49e8-83cc-ff0831ec5fe4", "name": "item_usefulness:update" }, + { + "id": "bc3ea9f0-08ec-4f9d-baea-1e23a5220168", + "name": "keyword:delete" + }, + { + "id": "3dcf3e0e-1cf0-4946-af4f-4f0db415a0e1", + "name": "keyword:view" + }, + { + "id": "e2d47bca-3b53-400e-82b9-e70cad0d81d7", + "name": "keyword:add" + }, + { + "id": "a6f202d2-1d4d-4b80-b90f-1f512b5a933d", + "name": "keyword:update" + }, { "id": "c098d34e-a341-4d3b-b4dd-eb3551b96ebd", "name": "language:delete" diff --git a/francoralite/apps/francoralite_api/migrations/0010_itemkeyword_keyword.py b/francoralite/apps/francoralite_api/migrations/0010_itemkeyword_keyword.py new file mode 100644 index 00000000..0783a297 --- /dev/null +++ b/francoralite/apps/francoralite_api/migrations/0010_itemkeyword_keyword.py @@ -0,0 +1,41 @@ +# Generated by Django 3.1.14 on 2023-12-11 17:49 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('francoralite_api', '0009_block'), + ] + + operations = [ + migrations.CreateModel( + name='Keyword', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, unique=True, verbose_name='Nom')), + ('notes', models.TextField(blank=True, null=True, verbose_name='Notes')), + ], + options={ + 'verbose_name_plural': 'keywords', + 'db_table': 'api_keyword', + 'ordering': ['name'], + }, + ), + migrations.CreateModel( + name='ItemKeyword', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='francoralite_api.item', verbose_name='Item')), + ('keyword', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='francoralite_api.keyword', verbose_name='Keyword')), + ], + options={ + 'verbose_name_plural': 'item_keywords', + 'db_table': 'api_item_keyword', + 'ordering': [], + 'unique_together': {('item', 'keyword')}, + }, + ), + ] diff --git a/francoralite/apps/francoralite_api/models/__init__.py b/francoralite/apps/francoralite_api/models/__init__.py index 5d79401a..904d6f0f 100644 --- a/francoralite/apps/francoralite_api/models/__init__.py +++ b/francoralite/apps/francoralite_api/models/__init__.py @@ -21,6 +21,7 @@ from .item_domain_tale import ItemDomainTale from .item_usefulness import ItemUsefulness from .item_dance import ItemDance +from .item_keyword import ItemKeyword from .item_thematic import ItemThematic from .item_language import ItemLanguage from .item_musical_organization import ItemMusicalOrganization @@ -46,6 +47,7 @@ from .item_performance import ItemPerformance from .musical_organization import MusicalOrganization from .musical_group import MusicalGroup +from .keyword import Keyword from .thematic import Thematic from .dance import Dance from .domain_tale import DomainTale diff --git a/francoralite/apps/francoralite_api/models/item_keyword.py b/francoralite/apps/francoralite_api/models/item_keyword.py new file mode 100644 index 00000000..993bcf5c --- /dev/null +++ b/francoralite/apps/francoralite_api/models/item_keyword.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# Authors: Luc LEGER / Coopérative ARTEFACTS + + +from django.db import models +from django.utils.translation import gettext_lazy as _ + +# Add nested/related tables +from .item import Item +from .keyword import Keyword + + +class ItemKeyword(models.Model): + # Description of the table + "Relation between an item and its keywords" + + # List of the fields + keyword = models.ForeignKey(Keyword, verbose_name=_( + 'Keyword'), on_delete=models.CASCADE) + item = models.ForeignKey(Item, verbose_name=_('Item'), + on_delete=models.CASCADE) + + class Meta: + app_label = 'francoralite_api' + db_table = 'api_item_keyword' + verbose_name_plural = _('item_keywords') + ordering = [] + unique_together = (('item', 'keyword'), ) diff --git a/francoralite/apps/francoralite_api/models/keyword.py b/francoralite/apps/francoralite_api/models/keyword.py new file mode 100644 index 00000000..019c5974 --- /dev/null +++ b/francoralite/apps/francoralite_api/models/keyword.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# Authors: Luc LEGER / Cooperative Artefacts + + +from django.db import models +from django.utils.translation import gettext_lazy as _ + + +class Keyword(models.Model): + # Description of the table + "Keyword used by a deposit (for each item)" + + # List of the fields + name = models.CharField(_('Nom'), unique=True, max_length=255) + notes = models.TextField(_('Notes'), null=True, blank=True) + + class Meta: + app_label = 'francoralite_api' + db_table = 'api_keyword' + verbose_name_plural = _('keywords') + ordering = ['name'] + + def __unicode__(self): + return self.name diff --git a/francoralite/apps/francoralite_api/serializers/item_keyword.py b/francoralite/apps/francoralite_api/serializers/item_keyword.py new file mode 100644 index 00000000..9c5d3cca --- /dev/null +++ b/francoralite/apps/francoralite_api/serializers/item_keyword.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# Authors: Luc LEGER / Coopérative ARTEFACTS +from rest_framework import serializers + +from .asymetric_related_field import AsymetricRelatedField + +# Add nested/related serializers +from .item import ItemSerializer +from .keyword import KeywordSerializer + +from ..models.item_keyword import ( + ItemKeyword as ItemKeywordModel) + + +class ItemKeywordSerializer(serializers.ModelSerializer): + """ + Common serializer for all ItemKeyword actions + """ + + # fields of the serializer + item = AsymetricRelatedField.from_serializer( + ItemSerializer, kwargs={'required': True}) + keyword = AsymetricRelatedField.from_serializer( + KeywordSerializer, kwargs={'required': True}) + + class Meta: + model = ItemKeywordModel + fields = '__all__' + + # TODO : use it with with a complete create + # def create(self, validated_data): + # """ + # Overriding the default create method of the Model serializer. + # :param validated_data: data containing all the details + # of ItemKeyword + # :return: returns a successfully created ext_collection record + # """ + # + # # FIXIT : Add nested/related data + # collection_data = validated_data.pop('collection') + # # Create an oject Mediacollection with the data converted in dict + # collection = MediacollectionModel.objects.create(**collection_data) + # + # collector_data = validated_data.pop('collector') + # # Create an oject collector (Authority) + # # with the data converted in dict + # collector = AuthorityModel.objects.create(**collector_data) + # + # # Create an oject item_keywords + # item_keywords = \ + # ItemKeywordModel.objects.create( + # collection=collection, collector=collector, **validated_data) + # + # return item_keywords diff --git a/francoralite/apps/francoralite_api/serializers/keyword.py b/francoralite/apps/francoralite_api/serializers/keyword.py new file mode 100644 index 00000000..2f326cd0 --- /dev/null +++ b/francoralite/apps/francoralite_api/serializers/keyword.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# Authors: Luc LEGER / Coopérative ARTEFACTS + +from rest_framework import serializers +from rest_framework.validators import UniqueValidator +from ..models.keyword import ( + Keyword as KeywordModel) + + +class KeywordSerializer(serializers.ModelSerializer): + """ + Common serializer for all Keyword actions + """ + + name = serializers.CharField( + required=True, + validators=[UniqueValidator(queryset=KeywordModel.objects.all())] + ) + notes = serializers.CharField(required=False, allow_blank=True) + + class Meta: + model = KeywordModel + fields = '__all__' diff --git a/francoralite/apps/francoralite_api/tests/factories/item_keyword.py b/francoralite/apps/francoralite_api/tests/factories/item_keyword.py new file mode 100644 index 00000000..b12d7d40 --- /dev/null +++ b/francoralite/apps/francoralite_api/tests/factories/item_keyword.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# Authors: Luc LEGER / Coopérative ARTEFACTS +""" +item_keyword factory to execute tests +""" + +import factory +from ...models.item_keyword import ItemKeyword +# import nested/related factories +from .keyword import KeywordFactory + + +class ItemKeywordFactory(factory.django.DjangoModelFactory): + """ + ItemKeyword factory + """ + + class Meta: + model = ItemKeyword + django_get_or_create = ( + 'item', + 'keyword',) + + # Nested/related factories + item = factory.SubFactory("francoralite.apps.francoralite_api.tests.factories.item.ItemFactory") + keyword = factory.SubFactory(KeywordFactory) diff --git a/francoralite/apps/francoralite_api/tests/factories/keyword.py b/francoralite/apps/francoralite_api/tests/factories/keyword.py new file mode 100644 index 00000000..10d24294 --- /dev/null +++ b/francoralite/apps/francoralite_api/tests/factories/keyword.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# Authors: Luc LEGER / Coopérative ARTEFACTS +""" +Keyword factory to execute tests +""" + +import factory +from ...models.keyword import Keyword + + +class KeywordFactory(factory.django.DjangoModelFactory): + """ + Keyword factory + """ + + class Meta: + model = Keyword + + name = factory.Sequence(lambda n: 'theme%d' % n) + notes = factory.Faker('paragraph', nb_sentences=1) diff --git a/francoralite/apps/francoralite_api/tests/test_item_keyword.py b/francoralite/apps/francoralite_api/tests/test_item_keyword.py new file mode 100644 index 00000000..d89db814 --- /dev/null +++ b/francoralite/apps/francoralite_api/tests/test_item_keyword.py @@ -0,0 +1,175 @@ +# -*- coding: utf-8 -*- +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# Authors: Luc LEGER / Coopérative ARTEFACTS + +""" +Item Keyword tests +""" + +import factory +import pytest + +from django.urls import reverse +from parameterized import parameterized +from rest_framework import status +from rest_framework.test import APITestCase + +from .factories.item_keyword import ItemKeywordFactory +from .fake_data.fake_sound import CleanMediaMixin +from ..models.item_keyword import ItemKeyword + +from .keycloak import get_token + +# Expected structure for Item_keyword objects +ITEMKEYWORD_STRUCTURE = [ + ('id', int), + ('item', dict), + ('keyword', dict), +] + +# Expected keys for MODEL objects +ITEMKEYWORD_FIELDS = sorted( + [item[0] for item in ITEMKEYWORD_STRUCTURE]) + + +@pytest.mark.django_db +class TestItemKeywordList(CleanMediaMixin, APITestCase): + """ + This class manage all ItemKeyword tests + """ + + def setUp(self): + """ + Run needed commands to have a fully working project + """ + get_token(self) + + # Create a set of sample data + ItemKeywordFactory.create_batch(6) + + def test_can_get_item_keyword_list(self): + """ + Ensure ItemKeyword objects exists + """ + # kwargs for the related tables + url = reverse('itemkeyword-list', kwargs={ + 'item_pk': 1}) + + # ORM side + item_keywords = ItemKeyword.objects.all() + self.assertEqual(len(item_keywords), 6) + + # API side + response = self.client.get(url) + + self.assertIsInstance(response.data, list) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data), 1) + + @parameterized.expand(ITEMKEYWORD_STRUCTURE) + def test_has_valid_item_keyword_values(self, attribute, attribute_type): + """ + Ensure ItemKeyword objects have valid values + """ + + # kwargs for the related tables + url = reverse('itemkeyword-list', kwargs={ + 'item_pk': 1}) + response = self.client.get(url) + + self.assertIsInstance(response.data, list) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + for item_keyword in response.data: + # Check only expected attributes returned + self.assertEqual( + sorted(item_keyword.keys()), ITEMKEYWORD_FIELDS) + + # Ensure type of each attribute + if attribute_type == str: + self.assertIsInstance(item_keyword[attribute], str) + else: + self.assertIsInstance(item_keyword[attribute], attribute_type) + self.assertIsNot(item_keyword[attribute], '') + + def test_get_an_item_keyword(self): + """ + Ensure we can get an ItemKeyword objects + using an existing id + """ + + item = ItemKeyword.objects.first() + # kwargs for the related tables + url = reverse('itemkeyword-detail', kwargs={ + 'item_pk': item.item.id, + 'pk': item.keyword.id}) + response = self.client.get(url) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIsInstance(response.data, dict) + + def test_create_an_item_keyword(self): + """ + Ensure we can create an ItemKeyword object + """ + + data = factory.build( + dict, + FACTORY_CLASS=ItemKeywordFactory) + + # Convert the related entity in dictionnary. + # Then they will be easily converted in JSON format. + data['item'] = 1 + data['keyword'] = 2 + + url = reverse('itemkeyword-list', kwargs={ + 'item_pk': data['item']}) + response = self.client.post(url, data, format='json') + + # Check only expected attributes returned + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertIsInstance(response.data, dict) + self.assertEqual( + sorted(response.data.keys()), + ITEMKEYWORD_FIELDS) + + # kwargs for the related tables + url = reverse( + 'itemkeyword-detail', + kwargs={'item_pk': response.data['item']['id'], + 'pk': response.data['id']} + ) + response_get = self.client.get(url) + + self.assertEqual(response_get.status_code, status.HTTP_200_OK) + self.assertIsInstance(response_get.data, dict) + + def test_delete_an_item_keyword(self): + """ + Ensure we can delete an ItemKeyword object + """ + + item = ItemKeyword.objects.first() + + # Delete this object + # kwargs for the related tables + url = reverse( + 'itemkeyword-detail', kwargs={ + 'item_pk': item.item.id, + 'pk': item.keyword.id} + ) + response = self.client.delete(url) + + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + + # Ensure ItemKeyword removed + # kwargs for the related tables + url_get = reverse( + 'itemkeyword-detail', kwargs={ + 'item_pk': item.item.id, + 'pk': item.keyword.id} + ) + response_get = self.client.get(url_get) + self.assertEqual(response_get.status_code, status.HTTP_404_NOT_FOUND) diff --git a/francoralite/apps/francoralite_api/tests/test_keyword.py b/francoralite/apps/francoralite_api/tests/test_keyword.py new file mode 100644 index 00000000..5bd4b8d7 --- /dev/null +++ b/francoralite/apps/francoralite_api/tests/test_keyword.py @@ -0,0 +1,211 @@ +# -*- coding: utf-8 -*- +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# Authors: Luc LEGER / Coopérative ARTEFACTS + +""" +Keyword tests +""" + +import factory +import pytest + +from django.urls import reverse +from parameterized import parameterized +from rest_framework import status +from rest_framework.test import APITestCase + +from .factories.keyword import KeywordFactory +from ..models.keyword import Keyword + +from .keycloak import get_token + +# Expected structure for Keyword objects +KEYWORD_STRUCTURE = [ + ('id', int), + ('name', str), + ('notes', str), +] + +# Expected keys for MODEL objects +KEYWORD_FIELDS = sorted( + [item[0] for item in KEYWORD_STRUCTURE]) + + +@pytest.mark.django_db +class TestKeywordList(APITestCase): + """ + This class manage all Keyword tests + """ + + def setUp(self): + """ + Run needed commands to have a fully working project + """ + get_token(self, username="administrateur") + + # Create a set of sample data + KeywordFactory.create_batch(6) + + def test_can_get_keyword_list(self): + """ + Ensure Keyword objects exists + """ + url = reverse('keyword-list') + + # ORM side + keywords = Keyword.objects.all() + self.assertEqual(len(keywords), 6) + + # API side + response = self.client.get(url) + + self.assertIsInstance(response.data, list) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data), 6) + + @parameterized.expand(KEYWORD_STRUCTURE) + def test_has_valid_keyword_values( + self, attribute, attribute_type): + """ + Ensure Keyword objects have valid values + """ + + url = reverse('keyword-list') + response = self.client.get(url) + + self.assertIsInstance(response.data, list) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + for keyword in response.data: + # Check only expected attributes returned + self.assertEqual( + sorted( + keyword.keys()), KEYWORD_FIELDS) + + # Ensure type of each attribute + if attribute_type == str: + self.assertIsInstance(keyword[attribute], str) + else: + self.assertIsInstance( + keyword[attribute], attribute_type) + self.assertIsNot(keyword[attribute], '') + + def test_get_an_keyword(self): + """ + Ensure we can get an Keyword objects + using an existing id + """ + + item = Keyword.objects.first() + url = reverse('keyword-detail', + kwargs={'pk': item.id}) + response = self.client.get(url) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIsInstance(response.data, dict) + + def test_create_an_keyword(self): + """ + Ensure we can create an Keyword object + """ + + data = factory.build( + dict, + FACTORY_CLASS=KeywordFactory) + url = reverse('keyword-list') + print("+-+-+-+") + print(data) + response = self.client.post(url, data, format='json') + + # Check only expected attributes returned + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertIsInstance(response.data, dict) + self.assertEqual( + sorted(response.data.keys()), + KEYWORD_FIELDS) + + url = reverse( + 'keyword-detail', + kwargs={'pk': response.data['id']} + ) + response_get = self.client.get(url) + + self.assertEqual(response_get.status_code, status.HTTP_200_OK) + self.assertIsInstance(response_get.data, dict) + + def test_update_an_keyword(self): + """ + Ensure we can update an Keyword object + """ + + item = Keyword.objects.first() + self.assertNotEqual(item.name, 'foobar_test_put') + + # Get existing object from API + url_get = reverse( + 'keyword-detail', + kwargs={'pk': item.id}) + data = self.client.get(url_get).data + + data['name'] = 'foobar_test_put' + url = reverse( + 'keyword-detail', + kwargs={'pk': item.id}) + response = self.client.put(url, data, format='json') + + # Ensure new name returned + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIsInstance(response.data, dict) + self.assertEqual( + sorted(response.data.keys()), + KEYWORD_FIELDS) + + self.assertEqual(response.data['name'], 'foobar_test_put') + + def test_patch_an_keyword(self): + """ + Ensure we can patch an Keyword object + """ + + item = Keyword.objects.first() + + self.assertNotEqual(item.name, 'foobar_test_patch') + + data = {'name': 'foobar_test_patch'} + url = reverse( + 'keyword-detail', + kwargs={'pk': item.id}) + response = self.client.patch(url, data, format='json') + + # Ensure new name returned + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIsInstance(response.data, dict) + self.assertEqual( + sorted(response.data.keys()), + KEYWORD_FIELDS) + + self.assertEqual(response.data['name'], 'foobar_test_patch') + + def test_delete_an_keyword(self): + """ + Ensure we can delete an Keyword object + """ + + item = Keyword.objects.first() + + # Delete this object + url = reverse( + 'keyword-detail', + kwargs={'pk': item.id}) + response = self.client.delete(url) + + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + + # Ensure Keyword removed + url_get = reverse( + 'keyword-detail', + kwargs={'pk': item.id}) + response_get = self.client.get(url_get) + self.assertEqual(response_get.status_code, status.HTTP_404_NOT_FOUND) diff --git a/francoralite/apps/francoralite_api/urls.py b/francoralite/apps/francoralite_api/urls.py index 220f6a50..6e8a65b3 100644 --- a/francoralite/apps/francoralite_api/urls.py +++ b/francoralite/apps/francoralite_api/urls.py @@ -42,12 +42,14 @@ item_domain_tale, item_domain_vocal, item_informer, + item_keyword, item_language, item_musical_group, item_musical_organization, item_performance, item_thematic, item_usefulness, + keyword, language, legal_rights, location_gis, @@ -151,6 +153,9 @@ router.register(r'musical_group', musical_group.MusicalGroupViewSet, basename='musicalgroup') +router.register(r'keyword', + keyword.KeywordViewSet, + basename='keyword') router.register(r'thematic', thematic.ThematicViewSet, basename='thematic') @@ -249,6 +254,8 @@ r'dance', item_dance.ItemDanceViewSet) item_router.register( r'language', item_language.ItemLanguageViewSet) +item_router.register( + r'keyword', item_keyword.ItemKeywordViewSet) item_router.register( r'thematic', item_thematic.ItemThematicViewSet) item_router.register( diff --git a/francoralite/apps/francoralite_api/views/item_keyword.py b/francoralite/apps/francoralite_api/views/item_keyword.py new file mode 100644 index 00000000..fb231cc5 --- /dev/null +++ b/francoralite/apps/francoralite_api/views/item_keyword.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# Authors: Luc LEGER / Coopérative ARTEFACTS + +from django.db.models.query import QuerySet +from rest_framework import viewsets +from ..models.item_keyword import ( + ItemKeyword as ItemKeywordModel) +from ..serializers.item_keyword import ItemKeywordSerializer + + +class ItemKeywordViewSet(viewsets.ModelViewSet): + """ + ItemKeyword management + """ + + queryset = ItemKeywordModel.objects.all() + serializer_class = ItemKeywordSerializer + + keycloak_scopes = { + 'GET': 'item_keyword:view', + 'POST': 'item_keyword:add', + 'PATCH': 'item_keyword:update', + 'PUT': 'item_keyword:update', + 'DELETE': 'item_keyword:delete' + } + + def get_queryset(self): + queryset = self.queryset + if isinstance(queryset, QuerySet): + # Ensure queryset is re-evaluated on each request. + queryset = ItemKeywordModel.objects.filter( + item_id=self.kwargs['item_pk']) + return queryset diff --git a/francoralite/apps/francoralite_api/views/keyword.py b/francoralite/apps/francoralite_api/views/keyword.py new file mode 100644 index 00000000..a55fef2a --- /dev/null +++ b/francoralite/apps/francoralite_api/views/keyword.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# Authors: Luc LEGER / Coopérative ARTEFACTS + + +from django_filters.rest_framework import DjangoFilterBackend +from rest_framework import filters +from rest_framework import viewsets + +from ..models.keyword import Keyword as KeywordModel +from ..serializers.keyword import KeywordSerializer + + +class KeywordViewSet(viewsets.ModelViewSet): + """ + Keyword management + """ + + queryset = KeywordModel.objects.all() + serializer_class = KeywordSerializer + + filter_backends = (DjangoFilterBackend, filters.SearchFilter) + search_fields = ('name',) + + keycloak_scopes = { + 'GET': 'keyword:view', + 'POST': 'keyword:add', + 'PATCH': 'keyword:update', + 'PUT': 'keyword:update', + 'DELETE': 'keyword:delete', + } diff --git a/francoralite/middleware/keycloak_mw.py b/francoralite/middleware/keycloak_mw.py index 8dac6dfc..807f3213 100644 --- a/francoralite/middleware/keycloak_mw.py +++ b/francoralite/middleware/keycloak_mw.py @@ -139,6 +139,7 @@ def process_view(self, request, view_func, view_args, view_kwargs): 'item_domain_tale:view', 'item_domain_vocal:view', 'item_informer:view', + 'item_keyword:view', 'item_language:view', 'item_musical_group:view', 'item_musical_organization:view', @@ -146,6 +147,7 @@ def process_view(self, request, view_func, view_args, view_kwargs): 'item_thematic:view', 'item_usefulness:view', 'item:view', + 'keyword:view', 'language:view', 'legal_rights:view', 'location_gis:view',