Source code for pyPreservica.mdformsAPI

"""
pyPreservica MDFormsAPI module definition

A client library for the Preservica Repository web services Metadata API
https://demo.preservica.com/api/metadata/documentation.html

author:     James Carr
licence:    Apache License 2.0

"""
import xml.etree.ElementTree
from typing import Callable, List, Union, Generator

from pyPreservica.common import *


[docs] class GroupFieldType(Enum): STRING = "STRING" LONG_STRING = "LONGSTRING" DATE = "DATE" NUMBER = "NUMBER"
[docs] class GroupField: field_id: str name: str field_type: GroupFieldType maxLength: int default: str visible: bool editable: bool minOccurs: int maxOccurs: int values: List[str] indexed: bool def __init__(self, field_id: str, name: str, field_type: GroupFieldType = GroupFieldType.STRING, maxLength: int = -1, default: str = "", visible: bool = True, editable: bool = True, minOccurs: int = 0, maxOccurs: int = 1, indexed: bool = True, values: List = None): self.field_id = field_id self.name = name self.field_type = field_type self.maxLength = maxLength self.default = default self.visible = visible self.editable = editable self.minOccurs = minOccurs self.maxOccurs = maxOccurs self.values = values self.indexed = indexed def __str__(self): return (f"Field ID: {self.field_id}\n" + f"Field Name: {self.name}\n" + f"Field Type: {self.field_type}\n" + f"Field Visible: {self.visible}\n" + f"Field Editable: {self.editable}\n")
[docs] class Group: group_id: str name: str description: str schemaUri: str fields: List[GroupField] def __init__(self, name: str, description: str): self.name = name self.description = description self.fields = [] def __str__(self): return (f"Group ID: {self.group_id}\n" + f"Group Name: {self.name}\n" + f"Group Description: {self.description}\n" + f"Group Schema URI: {self.schemaUri}")
def _object_from_json_(json_doc: dict) -> Group: """ Create a JSON dict object from a Group object """ group: Group = Group(name=json_doc['name'], description=json_doc['description']) group.fields = [] if 'id' in json_doc: group.group_id = json_doc['id'] if 'schemaUri' in json_doc: group.schemaUri = json_doc['schemaUri'] if 'fields' in json_doc: for field in json_doc['fields']: gf: GroupField = GroupField(field['id'], field['name'], GroupFieldType(str(field['type']))) if 'minOccurs' in field: gf.minOccurs = int(field['minOccurs']) if 'maxOccurs' in field: gf.maxOccurs = int(field['maxOccurs']) if 'visible' in field: gf.visible = bool(field['visible']) if 'editable' in field: gf.editable = bool(field['editable']) if 'values' in field: for v in field['values']: if gf.values is None: gf.values = [] gf.values.append(str(v)) if 'defaultValue' in field: gf.default = str(field['defaultValue']) if 'indexed' in field: gf.indexed = bool(field['indexed']) group.fields.append(gf) return group def _json_from_object_(group: Group) -> dict: """ Create a JSON dict object from a Group object """ fields = [] for field in group.fields: f = {"id": field.field_id, "name": field.name, "type": str(field.field_type.value)} f["minOccurs"] = str(field.minOccurs) f["maxOccurs"] = str(field.maxOccurs) f["visible"] = str(field.visible) f["editable"] = str(field.editable) if (field.values is not None) and (len(field.values) > 0): f["values"] = [item for item in field.values] f["defaultValue"] = str(field.default) f["indexed"] = str(field.indexed) fields.append(f) json_doc = {"name": group.name, "description": group.description, "fields": fields} return json_doc
[docs] class MetadataGroupsAPI(AuthenticatedAPI): def __init__(self, username: str = None, password: str = None, tenant: str = None, server: str = None, use_shared_secret: bool = False, two_fa_secret_key: str = None, protocol: str = "https", request_hook: Callable = None, credentials_path: str = 'credentials.properties'): super().__init__(username, password, tenant, server, use_shared_secret, two_fa_secret_key, protocol, request_hook, credentials_path) xml.etree.ElementTree.register_namespace("oai_dc", "http://www.openarchives.org/OAI/2.0/oai_dc/") xml.etree.ElementTree.register_namespace("ead", "urn:isbn:1-931666-22-9")
[docs] def download_template(self, form_name: str): """ Download a template csv to allow bulk input of data """ headers = {HEADER_TOKEN: self.token} url = f'{self.protocol}://{self.server}/api/metadata/csv-templates/download' for form in self.forms(): if form['title'] == form_name: form_id: str = form['id'] params = {'ids': form_id} with self.session.get(url, headers=headers, params=params) as response: if response.status_code == requests.codes.ok: with open(f"{form_name}.csv", mode="wt", encoding="utf-8") as fd: fd.write(response.content.decode("utf-8")) fd.flush() return f"{form_name}.csv" else: exception = HTTPException(None, response.status_code, response.url, "download_template", response.content.decode('utf-8')) logger.error(exception) raise exception return None
[docs] def delete_group_namespace(self, schema: str): """ Delete a new Metadata Group using its schema URI :param schema: The Group namespace schema URI :type schema: str :return: None :rtype: None """ for group in self.groups(): if group.schemaUri == schema: self.delete_group(group.group_id)
[docs] def delete_group(self, group_id: str): """ Delete a new Metadata Group using its ID :param group_id: Group ID :type group_id: str :return: :rtype: None """ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'} url = f'{self.protocol}://{self.server}/api/metadata/groups/{group_id}' with self.session.delete(url, headers=headers) as request: if request.status_code == requests.codes.no_content: return None else: exception = HTTPException(None, request.status_code, request.url, "delete_group", request.content.decode('utf-8')) logger.error(exception) raise exception
[docs] def add_fields(self, group_id: str, new_fields: List[GroupField]) -> dict: """ Add new metadata fields to an existing Group The new fields are appended to the end of the group :param group_id: The group ID of the group to update :type group_id: str :param new_fields: The list of new fields to add to the group :type new_fields: List[GroupField] :return: The updated Metadata Group as a JSON dict :rtype: dict """ this_group: Group = self.group(group_id) for field in new_fields: this_group.fields.append(field) doc = _json_from_object_(this_group) headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'} url = f'{self.protocol}://{self.server}/api/metadata/groups/{group_id}' with self.session.put(url, headers=headers, json=doc) as request: if request.status_code == requests.codes.created: return json.loads(str(request.content.decode('utf-8'))) else: exception = HTTPException(None, request.status_code, request.url, "add_fields", request.content.decode('utf-8')) logger.error(exception) raise exception
[docs] def add_group(self, group_name: str, group_description: str, fields: List[GroupField]) -> dict: """ Create a new Metadata Group GroupFields :param group_name: The name of the new Group :type group_name: str :param group_description: The description of the new Group :type group_description: str :param fields: The list of fields :type fields: List[GroupField] :return: The new metadata Group as a JSON dict :rtype: dict """ group: Group = Group(group_name, group_description) group.fields = fields json_document: dict = _json_from_object_(group) json_response: dict = self.add_group_json(json_document) return json_response
def update_form(self, form_id: str, json_form: Union[dict, str]): headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'} url = f'{self.protocol}://{self.server}/api/metadata/forms/{form_id}' if isinstance(json_form, dict): with self.session.put(url, headers=headers, json=json_form) as request: if request.status_code == requests.codes.ok: return json.loads(str(request.content.decode('utf-8'))) else: exception = HTTPException(None, request.status_code, request.url, "add_form_json", request.content.decode('utf-8')) logger.error(exception) raise exception elif isinstance(json_form, str): with self.session.put(url, headers=headers, data=json_form) as request: if request.status_code == requests.codes.ok: return json.loads(str(request.content.decode('utf-8'))) else: exception = HTTPException(None, request.status_code, request.url, "add_form_json", request.content.decode('utf-8')) logger.error(exception) raise exception else: raise RuntimeError("Argument must be a JSON dictionary or a JSON str")
[docs] def add_form(self, json_form: Union[dict, str]): """ Create a new Metadata form using a JSON dictionary object or document :param json_form: JSON dictionary or string :type json_form: dict :return: JSON document :rtype: dict """ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'} url = f'{self.protocol}://{self.server}/api/metadata/forms/' if isinstance(json_form, dict): with self.session.post(url, headers=headers, json=json_form) as request: if request.status_code == requests.codes.created: return json.loads(str(request.content.decode('utf-8'))) else: exception = HTTPException(None, request.status_code, request.url, "add_form_json", request.content.decode('utf-8')) logger.error(exception) raise exception elif isinstance(json_form, str): with self.session.post(url, headers=headers, data=json_form) as request: if request.status_code == requests.codes.created: return json.loads(str(request.content.decode('utf-8'))) else: exception = HTTPException(None, request.status_code, request.url, "add_form_json", request.content.decode('utf-8')) logger.error(exception) raise exception else: raise RuntimeError("Argument must be a JSON dictionary or a JSON str")
# def set_default_form(self, form_id: str): # """ # Set the default form # # """ # # headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'} # url = f'{self.protocol}://{self.server}/api/metadata/forms/{form_id}/default' # # payload: dict = {"default": True, "useAsDefault": True} # # with self.session.get(url, headers=headers, json=json.dumps(payload)) as request: # if request.status_code == requests.codes.ok: # return json.loads(str(request.content.decode('utf-8'))) # else: # exception = HTTPException(None, request.status_code, request.url, "set_default_form", # request.content.decode('utf-8')) # logger.error(exception) # raise exception
[docs] def add_group_json(self, json_object: Union[dict, str]) -> dict: """ Create a new Metadata Group using a JSON dictionary object or document :param json_object: JSON dictionary or string :type json_object: dict :return: JSON document :rtype: dict """ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'} url = f'{self.protocol}://{self.server}/api/metadata/groups/' if isinstance(json_object, dict): with self.session.post(url, headers=headers, json=json_object) as request: if request.status_code == requests.codes.created: return json.loads(str(request.content.decode('utf-8'))) else: exception = HTTPException(None, request.status_code, request.url, "add_group_json", request.content.decode('utf-8')) logger.error(exception) raise exception elif isinstance(json_object, str): with self.session.post(url, headers=headers, data=json_object) as request: if request.status_code == requests.codes.created: return json.loads(str(request.content.decode('utf-8'))) else: exception = HTTPException(None, request.status_code, request.url, "add_group_json", request.content.decode('utf-8')) logger.error(exception) raise exception else: raise RuntimeError("Argument must be a JSON dictionary or a JSON str")
[docs] def group_json(self, group_id: str) -> dict: """ Return a Group as a JSON object :param group_id: The Group id :type group_id: str :return: JSON document :rtype: dict """ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'} url = f'{self.protocol}://{self.server}/api/metadata/groups/{group_id}' with self.session.get(url, headers=headers) as request: if request.status_code == requests.codes.ok: return json.loads(str(request.content.decode('utf-8'))) else: exception = HTTPException(None, request.status_code, request.url, "group_json", request.content.decode('utf-8')) logger.error(exception) raise exception
[docs] def group(self, group_id: str) -> Group: """ Return a Group object by its ID :param group_id: The Group id :type group_id: str :return: The metadata Group Object :rtype: Group """ return _object_from_json_(self.group_json(group_id))
[docs] def groups_json(self) -> List[dict]: """ Return all the metadata Groups in the tenancy as a list of JSON dict objects :return: List of JSON dictionary object :rtype: List[dict] """ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'} url = f'{self.protocol}://{self.server}/api/metadata/groups' with self.session.get(url, headers=headers) as request: if request.status_code == requests.codes.ok: return json.loads(str(request.content.decode('utf-8')))['groups'] else: exception = HTTPException(None, request.status_code, request.url, "groups_json", request.content.decode('utf-8')) logger.error(exception) raise exception
[docs] def forms(self, schema_uri: Union[str, None] = None) -> dict: """ Return all the metadata Forms in the tenancy as a list of JSON dict objects :param schema_uri: The Form schema Uri :type schema_uri: str :return: List of JSON dictionary object :rtype: dict """ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'} url = f'{self.protocol}://{self.server}/api/metadata/forms' params = {} if schema_uri is not None: params = {'schemaUri': schema_uri} with self.session.get(url, headers=headers, params=params) as request: if request.status_code == requests.codes.ok: return json.loads(str(request.content.decode('utf-8')))['metadataForms'] else: exception = HTTPException(None, request.status_code, request.url, "forms_json", request.content.decode('utf-8')) logger.error(exception) raise exception
[docs] def delete_form(self, form_id: str): """ Delete a form by its ID """ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'} url = f'{self.protocol}://{self.server}/api/metadata/forms/{form_id}' with self.session.delete(url, headers=headers) as request: if request.status_code == requests.codes.no_content: return None else: exception = HTTPException(None, request.status_code, request.url, "delete_form", request.content.decode('utf-8')) logger.error(exception) raise exception
[docs] def form(self, form_id: str) -> dict: """ Return a Form as a JSON dict object :param form_id: The Form id :type form_id: str :return: JSON document :rtype: dict """ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'} url = f'{self.protocol}://{self.server}/api/metadata/forms/{form_id}' with self.session.get(url, headers=headers) as request: if request.status_code == requests.codes.ok: return json.loads(str(request.content.decode('utf-8'))) else: exception = HTTPException(None, request.status_code, request.url, "form_json", request.content.decode('utf-8')) logger.error(exception) raise exception
[docs] def groups(self) -> Generator[Group, None, None]: """ Return all the metadata Groups in the tenancy as Group Objects :return: Generator of Group Objects :rtype: Group """ for group in self.groups_json(): yield _object_from_json_(group)