Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(xmlupload): refactor xmlupload, add unittests (DEV-1043) #203

Merged
merged 6 commits into from Jun 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docstring is missing, see the other classes

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()