Skip to content

Commit

Permalink
chore(xmlupload): refactor xmlupload, add unittest (DEV-1043) (#203)
Browse files Browse the repository at this point in the history
  • Loading branch information
jnussbaum committed Jun 23, 2022
1 parent 4c6ed19 commit fcf8384
Show file tree
Hide file tree
Showing 12 changed files with 735 additions and 603 deletions.
50 changes: 50 additions & 0 deletions knora/dsplib/models/projectContext.py
@@ -0,0 +1,50 @@
from typing import Optional

from knora.dsplib.models.connection import Connection
from knora.dsplib.models.group import Group
from knora.dsplib.models.helpers import BaseError
from knora.dsplib.models.project import Project


class ProjectContext:
"""Represents the project context"""

_projects: list[Project]
_project_map: dict[str, str] # dictionary of (project name:project IRI) pairs
_inv_project_map: dict[str, str] # dictionary of (project IRI:project name) pairs
_groups: Optional[list[Group]]
_group_map: Optional[dict[str, str]]
_shortcode: Optional[str]
_project_name: Optional[str]

def __init__(self, con: Connection, shortcode: Optional[str] = None):
self._shortcode = shortcode
self._projects = Project.getAllProjects(con=con)
self._project_map: dict[str, str] = {x.shortname: x.id for x in self._projects}
self._inv_project_map: dict[str, str] = {x.id: x.shortname for x in self._projects}
try:
self._groups = Group.getAllGroups(con=con)
except BaseError:
self._groups = None
if self._groups:
self._group_map: dict[str, str] = {self._inv_project_map[x.project] + ':' + x.name: x.id for x in
self._groups}
else:
self._group_map = None
self._project_name = None
# get the project name from the shortcode
if self._shortcode:
for p in self._projects:
if p.shortcode == self._shortcode:
self._project_name = p.shortname
break

@property
def group_map(self) -> dict[str, str]:
"""Dictionary of (project:group name) and (group id) pairs of all groups in project"""
return self._group_map

@property
def project_name(self) -> Optional[str]:
"""Name of the project"""
return self._project_name
57 changes: 57 additions & 0 deletions knora/dsplib/models/xmlallow.py
@@ -0,0 +1,57 @@
from lxml import etree

from knora.dsplib.models.projectContext import ProjectContext
from knora.dsplib.models.xmlerror import XmlError


class XmlAllow:
"""Represents the allow element of the XML used for data import"""

_group: str
_permission: str

def __init__(self, node: etree.Element, project_context: ProjectContext) -> None:
"""
Constructor which parses the XML DOM allow element
Args:
node: The DOM node to be processed (represents a single right in a permission set)
project_context: Context for DOM node traversal
Returns:
None
"""
tmp = node.attrib['group'].split(':')
sysgroups = ['UnknownUser', 'KnownUser', 'ProjectMember', 'Creator', 'ProjectAdmin', 'SystemAdmin']
if len(tmp) > 1:
if tmp[0]:
if tmp[0] == 'knora-admin' and tmp[1] in sysgroups:
self._group = node.attrib['group']
else:
self._group = project_context.group_map.get(node.attrib['group'])
if self._group is None:
raise XmlError("Group \"{}\" is not known: Cannot find project!".format(node.attrib['group']))
else:
if project_context.project_name is None:
raise XmlError("Project shortcode has not been set in ProjectContext")
self._group = project_context.project_name + ':' + tmp[1]
else:
if tmp[0] in sysgroups:
self._group = 'knora-admin:' + node.attrib['group']
else:
raise XmlError("Group \"{}\" is not known: ".format(node.attrib['group']))
self._permission = node.text

@property
def group(self) -> str:
"""The group specified in the allow element"""
return self._group

@property
def permission(self) -> str:
"""The reference to a set of permissions"""
return self._permission

def print(self) -> None:
"""Prints the attributes of the XmlAllow instance"""
print(" group=", self._group, " permission=", self._permission)
26 changes: 26 additions & 0 deletions knora/dsplib/models/xmlbitstream.py
@@ -0,0 +1,26 @@
from lxml import etree


class XMLBitstream:
"""Represents a bitstream object (file) of a resource in the XML used for data import"""

_value: str
_permissions: str

def __init__(self, node: etree.Element) -> None:
self._value = node.text
self._permissions = node.get('permissions')

@property
def value(self) -> str:
"""The file path of the bitstream object"""
return self._value

@property
def permissions(self) -> str:
"""Reference to the set of permissions for the bitstream object"""
return self._permissions

def print(self) -> None:
"""Prints the bitstream object and its attributes."""
print(' Bitstream file path: ' + str(self._value))
9 changes: 9 additions & 0 deletions knora/dsplib/models/xmlerror.py
@@ -0,0 +1,9 @@
class XmlError(Exception):
"""Represents an error raised in the context of the XML import"""
_message: str

def __init__(self, msg: str):
self._message = msg

def __str__(self) -> str:
return 'XML-ERROR: ' + self._message
54 changes: 54 additions & 0 deletions knora/dsplib/models/xmlpermission.py
@@ -0,0 +1,54 @@
from lxml import etree

from knora.dsplib.models.permission import Permissions
from knora.dsplib.models.projectContext import ProjectContext
from knora.dsplib.models.xmlallow import XmlAllow


class XmlPermission:
"""Represents the permission set containing several XmlAllow elements in the XML used for data import"""

_id: str
_allows: list[XmlAllow]

def __init__(self, node: etree.Element, project_context: ProjectContext) -> None:
"""
Constructor which parses a XML DOM permissions element representing an named permission set
Args:
node: The DOM node to be processed (representing an a permission set)
project_context: Context for DOM node traversal
"""
self._allows = []
self._id = node.attrib['id']
for allow_node in node:
self._allows.append(XmlAllow(allow_node, project_context))

@property
def id(self) -> str:
"""The id of the permission set, p.ex. res-default"""
return self._id

@property
def allows(self) -> list[XmlAllow]:
"""List of XmlAllow elements defining permissions for specific groups"""
return self._allows

def get_permission_instance(self) -> Permissions:
"""Returns a list of allow elements of this permission instance"""
permissions = Permissions()
for allow in self._allows:
permissions.add(allow.permission, allow.group)
return permissions

def __str__(self) -> str:
allow_str: list[str] = []
for allow in self._allows:
allow_str.append("{} {}".format(allow.permission, allow.group))
return '|'.join(allow_str)

def print(self) -> None:
"""Prints the permission set"""
print('Permission: ', self._id)
for a in self._allows:
a.print()
65 changes: 65 additions & 0 deletions knora/dsplib/models/xmlproperty.py
@@ -0,0 +1,65 @@
from typing import Optional

from lxml import etree

from knora.dsplib.models.xmlvalue import XMLValue
from knora.dsplib.models.xmlerror import XmlError


class XMLProperty:
"""Represents a property of a resource in the XML used for data import"""

_name: str
_valtype: str
_values: list[XMLValue]

def __init__(self, node: etree.Element, valtype: str, default_ontology: Optional[str] = None):
"""
The constructor for the DSP property
Args:
node: the property node, p.ex. <decimal-prop></decimal-prop>
valtype: the type of value given by the name of the property node, p.ex. decimal in <decimal-prop>
default_ontology: the name of the ontology
"""
# get the property name which is in format namespace:propertyname, p.ex. rosetta:hasName
tmp_prop_name = node.attrib['name'].split(':')
if len(tmp_prop_name) > 1:
if tmp_prop_name[0]:
self._name = node.attrib['name']
else:
# replace an empty namespace with the default ontology name
self._name = default_ontology + ':' + tmp_prop_name[1]
else:
self._name = 'knora-admin:' + tmp_prop_name[0]
listname = node.attrib.get('list') # safe the list name if given (only for lists)
self._valtype = valtype
self._values = []

# parse the subnodes of the property nodes which contain the actual values of the property
for subnode in node:
if subnode.tag == valtype: # the subnode must correspond to the expected value type
self._values.append(XMLValue(subnode, valtype, listname))
else:
raise XmlError(f"ERROR Unexpected tag: '{subnode.tag}'. Property may contain only value tags!")

@property
def name(self) -> str:
"""The name of the property"""
return self._name

@property
def valtype(self) -> str:
"""The value type of the property"""
return self._valtype

@property
def values(self) -> list[XMLValue]:
"""List of values of this property"""
return self._values

def print(self) -> None:
"""Prints the property."""
print(' Property: {} Type: {}'.format(self._name, self._valtype))
for value in self._values:
value.print()

0 comments on commit fcf8384

Please sign in to comment.