def test_wrong_type_uri_references(self): coll_id = "testcoll" type_id = "test_subtype_type" entity_id = "test_subtype_entity" # Create subtype record with wrong type URI subtype_entity_values = test_subtype_entity_create_values(entity_id) entity = self.test_subtype_type_info.create_entity( entity_id, subtype_entity_values) entity[ANNAL.CURIE.type] = "test:wrong_type_uri" entity._save() # Test subtype entity created self.check_subtype_data( coll_id, type_id, entity_id, { '@type': ['test:test_subtype_type', 'annal:EntityData'], 'annal:type': "test:wrong_type_uri" }) # Update subtype definition to include supertype reference test_subtype_meta = self.test_subtype_type.get_values() test_subtype_meta[ANNAL.CURIE.supertype_uri] = [{ "@id": "test:test_supertype_type" }] self.testcoll.add_type(type_id, test_subtype_meta) # Test migration of updated type information to data migrate_coll_data(self.testcoll) self.check_subtype_data( coll_id, type_id, entity_id, { '@type': [ 'test:test_subtype_type', 'test:test_supertype_type', 'annal:EntityData' ], 'annal:type': "test:test_subtype_type" }) return
def test_migrate_list_fields(self): """ Test migration of list fields """ self.test_list = RecordList.create(self.testcoll, test_list_id, test_list_create_values) migrate_coll_data(self.testcoll) # Read field definition and check for inline field list view_data = self.check_entity_values( "_list", test_list_id, check_values=test_list_migrated_values) return
def test_field_comment_tooltip(self): """ Test migration of field without tooltip """ # Create field definition self.test_field = RecordField.create(self.testcoll, test_field_id, test_field_tooltip_create_values) # Apply migration to collection migrate_coll_data(self.testcoll) # Read field definition and check for inline field list field_data = self.check_entity_values( "_field", test_field_id, check_values=test_field_tooltip_migrated_values) return
def am_migratecollection(annroot, userhome, options): """ Apply migrations for a specified collection annalist_manager migratecollection coll Reads and writes every entity in a collection, thereby applying data migrations and saving them in the stored data. annroot is the root directory for the Annalist software installation. userhome is the home directory for the host system user issuing the command. options contains options parsed from the command line. returns 0 if all is well, or a non-zero status code. This value is intended to be used as an exit status code for the calling program. """ status, settings, site = get_settings_site(annroot, userhome, options) if status != am_errors.AM_SUCCESS: return status coll_id = getargvalue(getarg(options.args, 0), "Collection Id: ") coll = Collection.load(site, coll_id) if not (coll and coll.get_values()): print("Collection not found: %s" % (coll_id), file=sys.stderr) return am_errors.AM_NOCOLLECTION status = am_errors.AM_SUCCESS print("Apply data migrations in collection '%s'" % (coll_id, )) msgs = migrate_coll_data(coll) if msgs: for msg in msgs: print(msg) status = am_errors.AM_MIGRATECOLLFAIL return status
def am_migratecollection(annroot, userhome, options): """ Apply migrations for a specified collection annalist_manager migratecollection coll Reads and writes every entity in a collection, thereby applying data migrations and saving them in the stored data. annroot is the root directory for the Annalist software installation. userhome is the home directory for the host system user issuing the command. options contains options parsed from the command line. returns 0 if all is well, or a non-zero status code. This value is intended to be used as an exit status code for the calling program. """ status, settings, site = get_settings_site(annroot, userhome, options) if status != am_errors.AM_SUCCESS: return status coll_id = getargvalue(getarg(options.args, 0), "Collection Id: ") coll = Collection.load(site, coll_id) if not (coll and coll.get_values()): print("Collection not found: %s"%(coll_id), file=sys.stderr) return am_errors.AM_NOCOLLECTION status = am_errors.AM_SUCCESS print("Apply data migrations in collection '%s'"%(coll_id,)) msgs = migrate_coll_data(coll) if msgs: for msg in msgs: print(msg) status = am_errors.AM_MIGRATECOLLFAIL return status
def test_field_fieldgroup_references(self): """ Test migration of field group references in field definitions """ # Create field group self.test_group = RecordGroup_migration.create( self.testcoll, test_group_id, test_group_create_values) # Create field definition referencing field group self.test_field = RecordField.create(self.testcoll, test_field_id, test_field_group_create_values) # Apply migration to collection migrate_coll_data(self.testcoll) # Read field definition and check for inline field list field_data = self.check_entity_values( "_field", test_field_id, check_values=test_field_group_migrated_values) self.assertNotIn("annal:group_ref", field_data) self.check_entity_does_not_exist("_group", test_group_id) return
def test_subtype_supertype_references(self): coll_id = "testcoll" type_id = "test_subtype_type" entity_id = "test_subtype_entity" self.check_subtype_data( coll_id, type_id, entity_id, {'@type': ["test:test_subtype_type", "annal:EntityData"]}) # Update subtype definition to include supertype reference test_subtype_meta = self.test_subtype_type.get_values() test_subtype_meta[ANNAL.CURIE.supertype_uri] = [{ "@id": "test:test_supertype_type" }] self.testcoll.add_type(type_id, test_subtype_meta) # Test migration of updated type information to data migrate_coll_data(self.testcoll) self.check_subtype_data( coll_id, type_id, entity_id, { '@type': [ 'test:test_subtype_type', 'test:test_supertype_type', 'annal:EntityData' ] }) return
def am_migrateallcollections(annroot, userhome, options): """ Apply migrations to all collections annalist_manager migrateallcollections Reads and writes every entity in all collections, thereby applying data migrations and saving them in the stored data. annroot is the root directory for the Annalist software installation. userhome is the home directory for the host system user issuing the command. options contains options parsed from the command line. returns 0 if all is well, or a non-zero status code. This value is intended to be used as an exit status code for the calling program. """ status, settings, site = get_settings_site(annroot, userhome, options) if status != am_errors.AM_SUCCESS: return status print("Apply data migrations in all collections:") for coll in site.collections(): status = am_check_site_updated(coll) if status != am_errors.AM_SUCCESS: return status coll_id = coll.get_id() if coll_id != layout.SITEDATA_ID: log.debug("========== Processing '%s' =========="%(coll_id,)) print("---- Processing '%s'"%(coll_id,)) msgs = migrate_coll_data(coll) if msgs: for msg in msgs: print(msg) status = am_errors.AM_MIGRATECOLLFAIL print("Data migrations complete.") return status
def post(self, request, coll_id): """ Update some aspect of the current collection """ # Note: in many cases, this function redirects to a URI that displays a form # to gather further details of values to update. Values returned by # POST to this view are then passed as URI segments in the GET request # that renders the form. Maybe there's an easier way than all this # URI-wrangling? redirect_uri = None http_response = None viewinfo = self.collection_edit_setup(coll_id, "config", request.POST.dict()) if viewinfo.http_response: return viewinfo.http_response if "close" in request.POST: redirect_uri = viewinfo.get_continuation_next() if "migrate" in request.POST: msgs = migrate_coll_data(viewinfo.collection) msg_vals = {'id': coll_id} if msgs: for msg in msgs: log.warning(msg) err = message.MIGRATE_COLLECTION_ERROR % msg_vals msg = "\n".join([err] + msgs) log.error(msg) http_response = self.error( dict(self.error500values(), message=msg)) else: # Redisplay current page with completion message viewuri = self.get_request_path() http_response = self.redirect_info( self.get_request_path(), view_params=continuation_params(uri_param_dict(viewuri)), info_message=message.MIGRATED_COLLECTION_DATA % msg_vals) return http_response # Edit collection metadata if "metadata" in request.POST: redirect_uri = self.item_edit_uri(layout.SITEDATA_ID, "_coll", "Collection_view", coll_id, message.NO_COLLECTION_METADATA, viewinfo.get_continuation_here(), viewinfo.get_continuation_url()) # Record types type_id = request.POST.get('typelist', None) if "type_new" in request.POST: redirect_uri = self.item_new_uri(coll_id, "_type", "Type_view", viewinfo.get_continuation_here()) if "type_copy" in request.POST: redirect_uri = self.item_copy_uri(coll_id, "_type", "Type_view", type_id, message.NO_TYPE_FOR_COPY, viewinfo.get_continuation_here(), viewinfo.get_continuation_url()) if "type_edit" in request.POST: redirect_uri = self.item_edit_uri(coll_id, "_type", "Type_view", type_id, message.NO_TYPE_FOR_COPY, viewinfo.get_continuation_here(), viewinfo.get_continuation_url()) if "type_delete" in request.POST: http_response = viewinfo.confirm_delete_entity_response( layout.TYPE_TYPEID, type_id, self.view_uri("AnnalistRecordTypeDeleteView", coll_id=coll_id), form_action_field="type_delete", form_value_field="typelist", response_messages={ "no_entity": message.NO_TYPE_FOR_DELETE, "confirm_completion": message.REMOVE_RECORD_TYPE }) # List views list_id = request.POST.get('listlist', None) if "list_new" in request.POST: redirect_uri = self.item_new_uri(coll_id, "_list", "List_view", viewinfo.get_continuation_here()) if "list_copy" in request.POST: redirect_uri = self.item_copy_uri(coll_id, "_list", "List_view", list_id, message.NO_LIST_FOR_COPY, viewinfo.get_continuation_here(), viewinfo.get_continuation_url()) if "list_edit" in request.POST: redirect_uri = self.item_edit_uri(coll_id, "_list", "List_view", list_id, message.NO_LIST_FOR_COPY, viewinfo.get_continuation_here(), viewinfo.get_continuation_url()) if "list_delete" in request.POST: http_response = viewinfo.confirm_delete_entity_response( layout.LIST_TYPEID, list_id, self.view_uri("AnnalistRecordListDeleteView", coll_id=coll_id), form_action_field="list_delete", form_value_field="listlist", response_messages={ "no_entity": message.NO_LIST_FOR_DELETE, "confirm_completion": message.REMOVE_RECORD_LIST }) # Record views view_id = request.POST.get('viewlist', None) if "view_new" in request.POST: redirect_uri = self.item_new_uri(coll_id, "_view", "View_view", viewinfo.get_continuation_here()) if "view_copy" in request.POST: redirect_uri = self.item_copy_uri(coll_id, "_view", "View_view", view_id, message.NO_VIEW_FOR_COPY, viewinfo.get_continuation_here(), viewinfo.get_continuation_url()) if "view_edit" in request.POST: redirect_uri = self.item_edit_uri(coll_id, "_view", "View_view", view_id, message.NO_VIEW_FOR_COPY, viewinfo.get_continuation_here(), viewinfo.get_continuation_url()) if "view_delete" in request.POST: http_response = viewinfo.confirm_delete_entity_response( layout.VIEW_TYPEID, view_id, self.view_uri("AnnalistRecordViewDeleteView", coll_id=coll_id), form_action_field="view_delete", form_value_field="viewlist", response_messages={ "no_entity": message.NO_VIEW_FOR_DELETE, "confirm_completion": message.REMOVE_RECORD_VIEW }) # Invoke selected view and/or render status response if redirect_uri: http_response = http_response or HttpResponseRedirect(redirect_uri) if http_response: return http_response e = Annalist_Error( request.POST, "Unexpected values in POST to " + self.get_request_path()) log.exception(str(e)) return self.error( dict(self.error500values(), message=str(e) + " - see server log for details"))
def post(self, request, coll_id): """ Update some aspect of the current collection """ # Note: in many cases, this function redirects to a URI that displays a form # to gather further details of values to update. Values returned by # POST to this view are then passed as URI segments in the GET request # that renders the form. Maybe there's an easier way than all this # URI-wrangling? redirect_uri = None http_response = None viewinfo = self.collection_edit_setup(coll_id, "config", request.POST.dict()) if viewinfo.http_response: return viewinfo.http_response if "close" in request.POST: redirect_uri = viewinfo.get_continuation_next() if "migrate" in request.POST: msgs = migrate_coll_data(viewinfo.collection) msg_vals = {'id': coll_id} if msgs: for msg in msgs: log.warning(msg) err = message.MIGRATE_COLLECTION_ERROR%msg_vals msg = "\n".join([err]+msgs) log.error(msg) http_response = self.error(dict(self.error500values(),message=msg)) else: # Redisplay current page with completion message viewuri = self.get_request_path() http_response = self.redirect_info( self.get_request_path(), view_params=continuation_params(uri_param_dict(viewuri)), info_message=message.MIGRATED_COLLECTION_DATA%msg_vals ) return http_response # Edit collection metadata if "metadata" in request.POST: redirect_uri = self.item_edit_uri( layout.SITEDATA_ID, "_coll", "Collection_view", coll_id, message.NO_COLLECTION_METADATA, viewinfo.get_continuation_here(), viewinfo.get_continuation_url() ) # Record types type_id = request.POST.get('typelist', None) if "type_new" in request.POST: redirect_uri = self.item_new_uri( coll_id, "_type", "Type_view", viewinfo.get_continuation_here() ) if "type_copy" in request.POST: redirect_uri = self.item_copy_uri( coll_id, "_type", "Type_view", type_id, message.NO_TYPE_FOR_COPY, viewinfo.get_continuation_here(), viewinfo.get_continuation_url() ) if "type_edit" in request.POST: redirect_uri = self.item_edit_uri( coll_id, "_type", "Type_view", type_id, message.NO_TYPE_FOR_COPY, viewinfo.get_continuation_here(), viewinfo.get_continuation_url() ) if "type_delete" in request.POST: http_response = viewinfo.confirm_delete_entity_response( layout.TYPE_TYPEID, type_id, self.view_uri("AnnalistRecordTypeDeleteView", coll_id=coll_id), form_action_field="type_delete", form_value_field="typelist", response_messages= { "no_entity": message.NO_TYPE_FOR_DELETE , "confirm_completion": message.REMOVE_RECORD_TYPE } ) # List views list_id = request.POST.get('listlist', None) if "list_new" in request.POST: redirect_uri = self.item_new_uri( coll_id, "_list", "List_view", viewinfo.get_continuation_here() ) if "list_copy" in request.POST: redirect_uri = self.item_copy_uri( coll_id, "_list", "List_view", list_id, message.NO_LIST_FOR_COPY, viewinfo.get_continuation_here(), viewinfo.get_continuation_url() ) if "list_edit" in request.POST: redirect_uri = self.item_edit_uri( coll_id, "_list", "List_view", list_id, message.NO_LIST_FOR_COPY, viewinfo.get_continuation_here(), viewinfo.get_continuation_url() ) if "list_delete" in request.POST: http_response = viewinfo.confirm_delete_entity_response( layout.LIST_TYPEID, list_id, self.view_uri("AnnalistRecordListDeleteView", coll_id=coll_id), form_action_field="list_delete", form_value_field="listlist", response_messages= { "no_entity": message.NO_LIST_FOR_DELETE , "confirm_completion": message.REMOVE_RECORD_LIST } ) # Record views view_id = request.POST.get('viewlist', None) if "view_new" in request.POST: redirect_uri = self.item_new_uri( coll_id, "_view", "View_view", viewinfo.get_continuation_here() ) if "view_copy" in request.POST: redirect_uri = self.item_copy_uri( coll_id, "_view", "View_view", view_id, message.NO_VIEW_FOR_COPY, viewinfo.get_continuation_here(), viewinfo.get_continuation_url() ) if "view_edit" in request.POST: redirect_uri = self.item_edit_uri( coll_id, "_view", "View_view", view_id, message.NO_VIEW_FOR_COPY, viewinfo.get_continuation_here(), viewinfo.get_continuation_url() ) if "view_delete" in request.POST: http_response = viewinfo.confirm_delete_entity_response( layout.VIEW_TYPEID, view_id, self.view_uri("AnnalistRecordViewDeleteView", coll_id=coll_id), form_action_field="view_delete", form_value_field="viewlist", response_messages= { "no_entity": message.NO_VIEW_FOR_DELETE , "confirm_completion": message.REMOVE_RECORD_VIEW } ) # Invoke selected view and/or render status response if redirect_uri: http_response = http_response or HttpResponseRedirect(redirect_uri) if http_response: return http_response e = Annalist_Error(request.POST, "Unexpected values in POST to "+self.get_request_path()) log.exception(str(e)) return self.error( dict(self.error500values(), message=str(e)+" - see server log for details" ) )