1
0
Fork 0
mirror of https://github.com/yweber/lodel2.git synced 2026-07-08 00:20:48 +02:00
lodel2_mirror/leapi/lerelation.py

173 lines
6.4 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#-*- coding: utf-8 -*-
import copy
import re
import EditorialModel.fieldtypes.leo as ft_leo
from . import lecrud
from . import leobject
from . import lefactory
## @brief Main class for relations
class _LeRelation(lecrud._LeCrud):
## @brief Handles the superior
_lesup_fieldtype = {'lesup': ft_leo.EmFieldType(True)}
## @brief Handles the subordinate
_lesub_fieldtype = {'lesub': ft_leo.EmFieldType(False) }
## @brief Stores the list of fieldtypes that are common to all relations
_rel_fieldtypes = dict()
def __init__(self, rel_id, **kwargs):
self.id_relation = rel_id
## @brief Forge a filter to match the superior
@classmethod
def sup_filter(self, leo):
if isinstance(leo, leobject._LeObject):
return ('lesup', '=', leo)
## @brief Forge a filter to match the superior
@classmethod
def sub_filter(self, leo):
if isinstance(leo, leobject._LeObject):
return ('lesub', '=', leo)
## @return a dict with field name as key and fieldtype instance as value
@classmethod
def fieldtypes(cls):
rel_ft = dict()
rel_ft.update(cls._uid_fieldtype)
rel_ft.update(cls._lesup_fieldtype)
rel_ft.update(cls._lesub_fieldtype)
rel_ft.update(cls._rel_fieldtypes)
if cls.implements_lerel2type():
rel_ft.update(cls._rel_attr_fieldtypes)
return rel_ft
@classmethod
def _prepare_relational_fields(cls, field):
return lecrud.LeApiQueryError("Relational field '%s' given but %s doesn't is not a LeObject" % (field,
cls.__name__))
## @brief Prepare filters before sending them to the datasource
# @param cls : Concerned class
# @param filters_l list : List of filters
# @return prepared and checked filters
@classmethod
def _prepare_filters(cls, filters_l):
filters, rel_filters = super()._prepare_filters(filters_l)
res_filters = list()
for field, op, value in filters:
if field in ['lesup', 'lesub']:
if isinstance(value, str):
try:
value = int(value)
except ValueError as e:
raise LeApiDataCheckError("Wrong value given for '%s'"%field)
if isinstance(value, int):
value = cls.name2class('LeObject')(value)
res_filters.append( (field, op, value) )
return res_filters, rel_filters
@classmethod
## @brief deletes a relation between two objects
# @param filters_list list
# @param target_class str
def delete(cls, filters_list, target_class):
filters, rel_filters = cls._prepare_filters(filters_list)
if isinstance(target_class, str):
target_class = cls.name2class(target_class)
ret = cls._datasource.delete(target_class, filters)
return True if ret == 1 else False
## @brief move to the first rank
# @return True in case of success, False in case of failure
def move_first(self):
return self.set_rank('first')
## @brief move to the last rank
# @return True in case of success, False in case of failure
def move_last(self):
return self.set_rank('last')
## @brief move to the given rank defined by a shift step
# @param step str|int Step of the rank shift. Can be a string containing an operator and the value (i.e. "+6"
# or "-6"), or can be an integer (the operator will then be "+")
# @return True in case of success, False in case of failure
def shift_rank(self, step):
return self.set_rank(step)
## @brief sets a new rank
# @return True in case of success, False in case of failure
def set_rank(self, rank):
parsed_rank = self.__class__._parse_rank(rank)
return self._datasource.update_rank(self.id_relation, rank)
@classmethod
## @brief checks if a rank value is valid
# @param rank str|int
# @return tuple|str|int The tuple is for the case using an operator and a step (i.e. '+x' => ('+','x'))
# @throw ValueError if the rank value or type is not valid
def _parse_rank(cls, rank):
if isinstance(rank, str):
if rank == 'first' or rank == 'last':
return rank
else:
compiled_regular_expression = re.compile('([+]?)([0-9]+)')
matching_groups = compiled_regular_expression.search(rank)
if matching_groups:
return (matching_groups.group(1), matching_groups.group(2))
else:
raise ValueError("Invalid rank value : %s" % rank)
elif isinstance(rank, int):
if rank < 1:
raise ValueError("Invalid rank value : %d" % rank)
else:
return rank
else:
raise ValueError("Invalid rank type : %s" % type(rank))
## @brief Abstract class to handle hierarchy relations
class _LeHierarch(_LeRelation):
## @brief Delete current instance from DB
def delete(self):
lecrud._LeCrud._delete(self)
## @brief Abstract class to handle rel2type relations
class _LeRel2Type(_LeRelation):
## @brief Stores the list of fieldtypes handling relations attributes
_rel_attr_fieldtypes = dict()
## @brief Delete current instance from DB
def delete(self):
lecrud._LeCrud._delete(self)
## @brief Implements insert for rel2type
# @todo checks when autodetecing the rel2type class
@classmethod
def insert(cls, datas, classname = None):
#Set the nature
if 'nature' not in datas:
datas['nature'] = None
if cls == cls.name2class('LeRel2Type') and classname is None:
# autodetect the rel2type child class
classname = relname(datas['lesup'], datas['lesub'])
super().insert(datas, classname)
## @brief Given a superior and a subordinate, returns the classname of the give rel2type
# @param lesupclass LeClass : LeClass child class (can be a LeType or a LeClass child)
# @param lesubclass LeType : A LeType child class
# @return a name as string
@staticmethod
def relname(lesupclass, lesubclass):
supname = lesupclass._leclass.__name__ if lesupclass.implements_letype() else lesupclass.__name__
subname = lesubclass.__name__
return "Rel_%s2%s" % (supname, subname)