mirror of
https://github.com/yweber/lodel2.git
synced 2025-12-28 03:36:55 +01:00
Merge branch 'newlodel' of git.labocleo.org:lodel2 into newlodel
This commit is contained in:
commit
0ffbe0845a
52 changed files with 735 additions and 163 deletions
|
|
@ -20,3 +20,7 @@ Instance operations :
|
|||
make dyncode # Leapi dynamic code creation ( in leapi_dyncode.py in lodel2 instance root dir)
|
||||
make init_db # Call migration handlers to tell them to init all needed databases. (note : this target has dyncode as dependencie)
|
||||
make list_hooks # List all the hooks registered
|
||||
|
||||
Instance loader uppdate :
|
||||
If the install/loader.py is updated you can update instance's loader.py using
|
||||
scripts/create_instance.sh -u INSTANCE_PATH
|
||||
|
|
|
|||
18
em_test.py
18
em_test.py
|
|
@ -87,6 +87,7 @@ person.new_field( 'firstname',
|
|||
'fre': 'Prénom',
|
||||
},
|
||||
data_handler = 'varchar',
|
||||
group = base_group,
|
||||
)
|
||||
person.new_field( 'lastname',
|
||||
display_name = {
|
||||
|
|
@ -94,6 +95,7 @@ person.new_field( 'lastname',
|
|||
'fre': 'Nom de famille',
|
||||
},
|
||||
data_handler = 'varchar',
|
||||
group = base_group,
|
||||
)
|
||||
person.new_field( 'fullname',
|
||||
display_name = {
|
||||
|
|
@ -115,6 +117,7 @@ person.new_field( 'alias',
|
|||
allowed_classes = [person],
|
||||
default = None,
|
||||
nullable = True,
|
||||
group = base_group,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -324,14 +327,16 @@ index_name = index_abstract.new_field(
|
|||
display_name = {
|
||||
'eng': 'name',
|
||||
'fre': 'nom'},
|
||||
data_handler = 'varchar')
|
||||
data_handler = 'varchar',
|
||||
group = index_group)
|
||||
|
||||
index_value = index_abstract.new_field(
|
||||
'value',
|
||||
display_name = {
|
||||
'eng': 'value',
|
||||
'fre': 'valeur'},
|
||||
data_handler = 'varchar')
|
||||
data_handler = 'varchar',
|
||||
group = index_group)
|
||||
|
||||
text.new_field( 'indexes',
|
||||
display_name = {
|
||||
|
|
@ -341,7 +346,8 @@ text.new_field( 'indexes',
|
|||
back_reference = ('Indexabs', 'texts'),
|
||||
allowed_classes = [index_abstract],
|
||||
default = None,
|
||||
nullable = True)
|
||||
nullable = True,
|
||||
group = index_group)
|
||||
|
||||
index_abstract.new_field( 'texts',
|
||||
display_name = {
|
||||
|
|
@ -349,7 +355,8 @@ index_abstract.new_field( 'texts',
|
|||
'fre': 'Texte contenant cette index'},
|
||||
data_handler = 'list',
|
||||
back_reference = ('Text', 'indexes'),
|
||||
allowed_classes = [text])
|
||||
allowed_classes = [text],
|
||||
group = index_group)
|
||||
|
||||
index_theme = em.new_class(
|
||||
'indexTheme',
|
||||
|
|
@ -364,7 +371,8 @@ index_theme_theme = index_abstract.new_field(
|
|||
'theme',
|
||||
display_name = {
|
||||
'eng': 'theme'},
|
||||
data_handler = 'varchar')
|
||||
data_handler = 'varchar',
|
||||
group = index_group)
|
||||
|
||||
#em.save('xmlfile', filename = 'examples/em_test.xml')
|
||||
pickle_file = 'examples/em_test.pickle'
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -10,7 +10,7 @@ filename = -
|
|||
context = True
|
||||
|
||||
[lodel2.editorialmodel]
|
||||
groups =
|
||||
groups = base_group, editorial_abstract, editorial_person
|
||||
emfile = editorial_model.pickle
|
||||
dyncode = leapi_dyncode.py
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ except ImportError:
|
|||
print("Unable to load lodel module. exiting...")
|
||||
exit(1)
|
||||
|
||||
|
||||
#
|
||||
# Loading settings
|
||||
#
|
||||
|
|
@ -29,16 +28,19 @@ from lodel.plugin import core_hooks
|
|||
|
||||
def start():
|
||||
#Load plugins
|
||||
from lodel import logger
|
||||
from lodel.plugin import Plugin
|
||||
logger.debug("Loader.start() called")
|
||||
Plugin.load_all()
|
||||
|
||||
LodelHook.call_hook('lodel2_bootstraped', '__main__', None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
start()
|
||||
|
||||
start()
|
||||
if Settings.runtest:
|
||||
start()
|
||||
import unittest
|
||||
import tests
|
||||
loader = unittest.TestLoader()
|
||||
|
|
@ -51,7 +53,11 @@ if __name__ == '__main__':
|
|||
runner.run(suite)
|
||||
exit()
|
||||
|
||||
import lodel
|
||||
import leapi_dyncode as dyncode
|
||||
lodel.dyncode = dyncode
|
||||
LodelHook.call_hook('lodel2_loader_main', '__main__', None)
|
||||
|
||||
#Run interative python
|
||||
import code
|
||||
print("""
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#-*- coding: utf-8 -*-
|
||||
|
||||
dyncode = None
|
||||
|
||||
##@page lodel2_start Lodel2 boot mechanism
|
||||
#
|
||||
# @par Lodel2 boot sequence
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import hashlib
|
|||
|
||||
from lodel.utils.mlstring import MlString
|
||||
|
||||
from lodel.settings import Settings
|
||||
from lodel.editorial_model.exceptions import *
|
||||
from lodel.leapi.leobject import CLASS_ID_FIELDNAME
|
||||
|
||||
|
|
@ -84,6 +85,12 @@ class EmClass(EmComponent):
|
|||
##@brief Stores EmFields instances indexed by field uid
|
||||
self.__fields = dict()
|
||||
|
||||
self.group = group
|
||||
if group is None:
|
||||
warnings.warn("NO GROUP FOR EMCLASS %s" % uid)
|
||||
else:
|
||||
group.add_components([self])
|
||||
|
||||
#Adding common field
|
||||
if not self.abstract:
|
||||
self.new_field(
|
||||
|
|
@ -95,8 +102,9 @@ class EmClass(EmComponent):
|
|||
'eng': "Allow to create instance of the good class when\
|
||||
fetching arbitrary datas from DB"},
|
||||
data_handler = 'LeobjectSubclassIdentifier',
|
||||
internal = True)
|
||||
|
||||
internal = True,
|
||||
group = group)
|
||||
|
||||
##@brief Property that represent a dict of all fields (the EmField defined in this class and all its parents)
|
||||
# @todo use Settings.editorialmodel.groups to determine wich fields should be returned
|
||||
@property
|
||||
|
|
@ -136,6 +144,16 @@ class EmClass(EmComponent):
|
|||
return list(fields.values()) if uid is None else fields[uid]
|
||||
except KeyError:
|
||||
raise EditorialModelError("No such EmField '%s'" % uid)
|
||||
|
||||
##@brief Keep in __fields only fields contained in active groups
|
||||
def _set_active_fields(self, active_groups):
|
||||
if not Settings.editorialmodel.editormode:
|
||||
active_fields = []
|
||||
for grp_name, agrp in active_groups.items():
|
||||
active_fields += [ emc for emc in agrp.components()
|
||||
if isinstance(emc, EmField)]
|
||||
self.__fields = { fname:fdh for fname, fdh in self.__fields.items()
|
||||
if fdh in active_fields }
|
||||
|
||||
##@brief Add a field to the EmClass
|
||||
# @param emfield EmField : an EmField instance
|
||||
|
|
@ -143,6 +161,7 @@ class EmClass(EmComponent):
|
|||
# @throw EditorialModelException if an EmField with same uid allready in this EmClass (overwritting allowed from parents)
|
||||
# @todo End the override checks (needs methods in data_handlers)
|
||||
def add_field(self, emfield):
|
||||
assert_edit()
|
||||
if emfield.uid in self.__fields:
|
||||
raise EditorialModelError("Duplicated uid '%s' for EmField in this class ( %s )" % (emfield.uid, self))
|
||||
# Incomplete field override check
|
||||
|
|
@ -151,7 +170,6 @@ class EmClass(EmComponent):
|
|||
if not emfield.data_handler_instance.can_override(parent_field.data_handler_instance):
|
||||
raise AttributeError("'%s' field override a parent field, but data_handles are not compatible" % emfield.uid)
|
||||
self.__fields[emfield.uid] = emfield
|
||||
emfield._emclass = self
|
||||
return emfield
|
||||
|
||||
##@brief Create a new EmField and add it to the EmClass
|
||||
|
|
@ -159,7 +177,8 @@ class EmClass(EmComponent):
|
|||
# @param uid str : the EmField uniq id
|
||||
# @param **field_kwargs : EmField constructor parameters ( see @ref EmField.__init__() )
|
||||
def new_field(self, uid, data_handler, **field_kwargs):
|
||||
return self.add_field(EmField(uid, data_handler, **field_kwargs))
|
||||
assert_edit()
|
||||
return self.add_field(EmField(uid, data_handler, self, **field_kwargs))
|
||||
|
||||
def d_hash(self):
|
||||
m = hashlib.md5()
|
||||
|
|
@ -196,7 +215,7 @@ class EmField(EmComponent):
|
|||
# @param help_text MlString|str|dict : help text
|
||||
# @param group EmGroup :
|
||||
# @param **handler_kwargs : data handler arguments
|
||||
def __init__(self, uid, data_handler, display_name = None, help_text = None, group = None, **handler_kwargs):
|
||||
def __init__(self, uid, data_handler, em_class = None, display_name = None, help_text = None, group = None, **handler_kwargs):
|
||||
from lodel.leapi.datahandlers.base_classes import DataHandler
|
||||
super().__init__(uid, display_name, help_text, group)
|
||||
##@brief The data handler name
|
||||
|
|
@ -208,7 +227,13 @@ class EmField(EmComponent):
|
|||
##@brief Stores data handler instanciation options
|
||||
self.data_handler_options = handler_kwargs
|
||||
##@brief Stores the emclass that contains this field (set by EmClass.add_field() method)
|
||||
self._emclass = None
|
||||
self._emclass = em_class
|
||||
if self._emclass is None:
|
||||
warnings.warn("No EmClass for field %s" %uid)
|
||||
if group is None:
|
||||
warnings.warn("No EmGroup for field %s" % uid)
|
||||
else:
|
||||
group.add_components([self])
|
||||
|
||||
##@brief Returns data_handler_name attribute
|
||||
def get_data_handler_name(self):
|
||||
|
|
@ -315,10 +340,13 @@ class EmGroup(object):
|
|||
##@brief Add components in a group
|
||||
# @param components list : EmComponent instances list
|
||||
def add_components(self, components):
|
||||
assert_edit()
|
||||
for component in components:
|
||||
if isinstance(component, EmField):
|
||||
if component._emclass is None:
|
||||
warnings.warn("Adding an orphan EmField to an EmGroup")
|
||||
msg = "Adding an orphan EmField '%s' to EmGroup '%s'"
|
||||
msg %= (component, self)
|
||||
warnings.warn(msg)
|
||||
elif not isinstance(component, EmClass):
|
||||
raise EditorialModelError("Expecting components to be a list of EmComponent, but %s found in the list" % type(component))
|
||||
self.__components |= set(components)
|
||||
|
|
@ -326,6 +354,7 @@ class EmGroup(object):
|
|||
##@brief Add a dependencie
|
||||
# @param em_group EmGroup|iterable : an EmGroup instance or list of instance
|
||||
def add_dependencie(self, grp):
|
||||
assert_edit()
|
||||
try:
|
||||
for group in grp:
|
||||
self.add_dependencie(group)
|
||||
|
|
@ -343,6 +372,7 @@ class EmGroup(object):
|
|||
# @param em_group EmGroup|iterable : an EmGroup instance or list of instance
|
||||
# Useless ???
|
||||
def add_applicant(self, grp):
|
||||
assert_edit()
|
||||
try:
|
||||
for group in grp:
|
||||
self.add_applicant(group)
|
||||
|
|
|
|||
|
|
@ -3,3 +3,12 @@
|
|||
class EditorialModelError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def assert_edit():
|
||||
try:
|
||||
from lodel import Settings
|
||||
except ImportError: #Very dirty, but don't know how to fix the tests
|
||||
return
|
||||
if not Settings.editorialmodel.editormode:
|
||||
raise EditorialModelError("EM is readonly : editormode is OFF")
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import importlib
|
|||
import copy
|
||||
|
||||
from lodel.utils.mlstring import MlString
|
||||
from lodel.logger import logger
|
||||
from lodel import logger
|
||||
from lodel.settings import Settings
|
||||
from lodel.settings.utils import SettingsError
|
||||
|
||||
|
|
@ -128,19 +128,28 @@ class EditorialModel(object):
|
|||
#EditorialModel.__active_classes attibutes
|
||||
def __set_actives(self):
|
||||
if Settings.editorialmodel.editormode:
|
||||
logger.warning("All EM groups active because editormode in ON")
|
||||
# all groups & classes actives because we are in editor mode
|
||||
self.__active_groups = self.__groups
|
||||
self.__active_classes = self.__classes
|
||||
else:
|
||||
#determine groups first
|
||||
self.__active_groups = dict()
|
||||
self.__active_classes = dict()
|
||||
for agrp in Settings.editorialmodel.groups:
|
||||
if agrp not in self.__groups:
|
||||
raise SettingsError('Invalid group found in settings : %s' % agrp)
|
||||
logger.debug("Set group '%s' as active" % agrp)
|
||||
grp = self.__groups[agrp]
|
||||
self.__active_groups[grp.uid] = grp
|
||||
for acls in grp.components():
|
||||
for acls in [cls for cls in grp.components() if isinstance(cls, EmClass)]:
|
||||
self.__active_classes[acls.uid] = acls
|
||||
if len(self.__active_groups) == 0:
|
||||
raise RuntimeError("No groups activated, abording...")
|
||||
if len(self.__active_classes) == 0:
|
||||
raise RuntimeError("No active class found. Abording")
|
||||
for clsname, acls in self.__active_classes.items():
|
||||
acls._set_active_fields(self.__active_groups)
|
||||
|
||||
##@brief EmField getter
|
||||
# @param uid str : An EmField uid represented by "CLASSUID.FIELDUID"
|
||||
|
|
@ -167,7 +176,7 @@ class EditorialModel(object):
|
|||
# @param emclass EmClass : the EmClass instance to add
|
||||
# @return emclass
|
||||
def add_class(self, emclass):
|
||||
self.raise_if_ro()
|
||||
assert_edit()
|
||||
if not isinstance(emclass, EmClass):
|
||||
raise ValueError("<class EmClass> expected but got %s " % type(emclass))
|
||||
if emclass.uid in self.classes():
|
||||
|
|
@ -179,7 +188,7 @@ class EditorialModel(object):
|
|||
# @param emgroup EmGroup : the EmGroup instance to add
|
||||
# @return emgroup
|
||||
def add_group(self, emgroup):
|
||||
self.raise_if_ro()
|
||||
assert_edit()
|
||||
if not isinstance(emgroup, EmGroup):
|
||||
raise ValueError("<class EmGroup> expected but got %s" % type(emgroup))
|
||||
if emgroup.uid in self.groups():
|
||||
|
|
@ -192,7 +201,7 @@ class EditorialModel(object):
|
|||
#@param **kwargs : EmClass constructor options (
|
||||
# see @ref lodel.editorial_model.component.EmClass.__init__() )
|
||||
def new_class(self, uid, **kwargs):
|
||||
self.raise_if_ro()
|
||||
assert_edit()
|
||||
return self.add_class(EmClass(uid, **kwargs))
|
||||
|
||||
##@brief Add a new EmGroup to the editorial model
|
||||
|
|
@ -200,14 +209,14 @@ class EditorialModel(object):
|
|||
#@param *kwargs : EmGroup constructor keywords arguments (
|
||||
# see @ref lodel.editorial_model.component.EmGroup.__init__() )
|
||||
def new_group(self, uid, **kwargs):
|
||||
self.raise_if_ro()
|
||||
assert_edit()
|
||||
return self.add_group(EmGroup(uid, **kwargs))
|
||||
|
||||
##@brief Save a model
|
||||
# @param translator module : The translator module to use
|
||||
# @param **translator_args
|
||||
def save(self, translator, **translator_kwargs):
|
||||
self.raise_if_ro()
|
||||
assert_edit()
|
||||
if isinstance(translator, str):
|
||||
translator = self.translator_from_name(translator)
|
||||
return translator.save(self, **translator_kwargs)
|
||||
|
|
|
|||
|
|
@ -316,7 +316,7 @@ def load_class_xml(model, elem):
|
|||
|
||||
fields = elem.find('fields')
|
||||
for field in fields:
|
||||
emfield = load_field_xml(model, field)
|
||||
emfield = load_field_xml(model, field, emclass)
|
||||
l_emfields = emclass.fields()
|
||||
incls = False
|
||||
for emf in l_emfields:
|
||||
|
|
@ -329,10 +329,11 @@ def load_class_xml(model, elem):
|
|||
return emclass
|
||||
|
||||
##@brief Creates a EmField from a xml description
|
||||
# @param elem : the element which represents the EmField
|
||||
# @param model : the model which will contain the new field
|
||||
# @return a new EmField object
|
||||
def load_field_xml(model, elem):
|
||||
#@param elem : the element which represents the EmField
|
||||
#@param model : the model which will contain the new field
|
||||
#@param emclass EmClass : the EmClass of the field
|
||||
#@return a new EmField object
|
||||
def load_field_xml(model, elem, emclass):
|
||||
uid = elem.find('uid').text
|
||||
if elem.find('display_name').text is None:
|
||||
name = None
|
||||
|
|
@ -354,16 +355,13 @@ def load_field_xml(model, elem):
|
|||
group = None
|
||||
|
||||
dhdl = elem.find('datahandler_name')
|
||||
dhdl_opts = {}
|
||||
if dhdl.text is not None:
|
||||
dhdl_opts = elem.find('datahandler_options')
|
||||
|
||||
if dhdl_opts is not None:
|
||||
dhdl_options = load_dhdl_options_xml(model, dhdl_opts)
|
||||
emfield = EmField(uid, dhdl.text, name, help_text, group, **dhdl_options)
|
||||
else:
|
||||
emfield = EmField(uid, dhdl.text, name, help_text, group)
|
||||
else:
|
||||
emfield = EmField(uid, dhdl.text, name, help_text, group)
|
||||
emfield = EmField(
|
||||
uid, dhdl.text, emclass, name, help_text, group, **dhdl_options)
|
||||
|
||||
return emfield
|
||||
|
||||
|
|
|
|||
|
|
@ -110,8 +110,7 @@ class DataHandler(object):
|
|||
return data_handler.default
|
||||
elif data_handler is not None and data_handler.nullable:
|
||||
return None
|
||||
|
||||
return RuntimeError("Unable to construct data for field %s", fname)
|
||||
return cur_value
|
||||
|
||||
##@brief Check datas consistency
|
||||
# @param emcomponent EmComponent : An EmComponent child class instance
|
||||
|
|
@ -184,6 +183,7 @@ class DataField(DataHandler):
|
|||
# References are fields that stores a reference to another
|
||||
# editorial object
|
||||
class Reference(DataHandler):
|
||||
base_type="ref"
|
||||
|
||||
##@brief Instanciation
|
||||
# @param allowed_classes list | None : list of allowed em classes if None no restriction
|
||||
|
|
@ -249,19 +249,22 @@ class SingleRef(Reference):
|
|||
##@brief This class represent a data_handler for multiple references to another object
|
||||
#
|
||||
# The fields using this data handlers are like SingleRef but can store multiple references in one field
|
||||
# @note SQL implementation could be tricky
|
||||
# @note for the moment split on ',' chars
|
||||
class MultipleRef(Reference):
|
||||
|
||||
##
|
||||
# @param max_item int | None : indicate the maximum number of item referenced by this field, None mean no limit
|
||||
def __init__(self, max_item = None, **kwargs):
|
||||
self.max_item = max_item
|
||||
super().__init__(**kwargs)
|
||||
|
||||
|
||||
def _check_data_value(self, value):
|
||||
value = value.split(',')
|
||||
if self.max_item is not None:
|
||||
if self.max_item < len(value):
|
||||
return None, FieldValidationError("To many items")
|
||||
return value, None
|
||||
|
||||
## @brief Class designed to handle datas access will fieldtypes are constructing datas
|
||||
#
|
||||
|
|
|
|||
|
|
@ -72,9 +72,6 @@ class UniqID(Integer):
|
|||
kwargs['internal'] = 'automatic'
|
||||
super(self.__class__, self).__init__(primary_key = True, **kwargs)
|
||||
|
||||
def _check_data_value(self, value):
|
||||
return value, None
|
||||
|
||||
def construct_data(self, emcomponent, fname, datas, cur_value):
|
||||
if cur_value is None:
|
||||
#Ask datasource to provide a new uniqID
|
||||
|
|
@ -114,3 +111,6 @@ class Concat(FormatString):
|
|||
super().__init__(
|
||||
format_string = format_string, field_list = field_list, **kwargs)
|
||||
|
||||
class Password(Varchar):
|
||||
help = 'Handle passwords'
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -20,10 +20,9 @@ class List(MultipleRef):
|
|||
# @param value *
|
||||
# @return tuple(value, exception)
|
||||
def _check_data_value(self, value):
|
||||
val, expt = super()._check_data_value()
|
||||
val, expt = super()._check_data_value(value)
|
||||
if not isinstance(expt, Exception):
|
||||
val = list(val)
|
||||
val, expt = super()._check_data_value(value.values())
|
||||
return val, expt
|
||||
|
||||
|
||||
|
|
@ -41,10 +40,9 @@ class Set(MultipleRef):
|
|||
# @param value *
|
||||
# @return tuple(value, exception)
|
||||
def _check_data_value(self, value):
|
||||
val, expt = super()._check_data_value()
|
||||
val, expt = super()._check_data_value(value)
|
||||
if not isinstance(expt, Exception):
|
||||
val = set(val)
|
||||
val, expt = super()._check_data_value(value.values())
|
||||
val = tuple(set(val))
|
||||
return val, expt
|
||||
|
||||
|
||||
|
|
@ -62,9 +60,9 @@ class Map(MultipleRef):
|
|||
# @param value *
|
||||
# @return tuple(value, exception)
|
||||
def _check_data_value(self, value):
|
||||
val, expt = super()._check_data_value(value)
|
||||
if not isinstance(value, dict):
|
||||
return None, FieldValidationError("Values for dict fields should be dict")
|
||||
val, expt = super()._check_data_value(value.values())
|
||||
return (
|
||||
None if isinstance(expt, Exception) else value,
|
||||
expt)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import functools
|
|||
#from lodel.editorial_model.components import *
|
||||
from lodel.leapi.leobject import LeObject
|
||||
from lodel.leapi.datahandlers.base_classes import DataHandler
|
||||
from lodel import logger
|
||||
|
||||
##@brief Generate python module code from a given model
|
||||
# @param model lodel.editorial_model.model.EditorialModel
|
||||
|
|
@ -112,6 +113,7 @@ def generate_classes(model):
|
|||
bootstrap = ""
|
||||
# Generating field list for LeObjects generated from EmClass
|
||||
for em_class in get_classes(model):
|
||||
logger.info("Generating a dynamic class for %s" % em_class.uid)
|
||||
uid = list() # List of fieldnames that are part of the EmClass primary key
|
||||
parents = list() # List of parents EmClass
|
||||
# Determine pk
|
||||
|
|
|
|||
|
|
@ -180,8 +180,11 @@ class LeObject(object):
|
|||
cls.__name__))
|
||||
##@return A dict with fieldname as key and datahandler as instance
|
||||
@classmethod
|
||||
def fields(cls):
|
||||
return copy.copy(cls._fields)
|
||||
def fields(cls, include_ro = False):
|
||||
if include_ro:
|
||||
return copy.copy(cls._fields)
|
||||
else:
|
||||
return {fname:cls._fields[fname] for fname in cls._fields if not cls._fields[fname].is_internal()}
|
||||
|
||||
##@brief Return the list of parents classes
|
||||
#
|
||||
|
|
@ -223,7 +226,7 @@ class LeObject(object):
|
|||
uid_handlers = set( cls._fields[name] for name in cls._uid )
|
||||
for pcls in cls.hierarch()[1:]:
|
||||
puid_handlers = set(cls._fields[name] for name in pcls._uid)
|
||||
if set(pcls._uid) != set(pcls._uid) \
|
||||
if set(pcls._uid) != set(prev._uid) \
|
||||
or puid_handlers != uid_handlers:
|
||||
break
|
||||
prev = pcls
|
||||
|
|
@ -242,14 +245,24 @@ class LeObject(object):
|
|||
ro_ds, rw_ds = cls._datasource_name
|
||||
#Read only datasource initialisation
|
||||
cls._ro_datasource = cls._init_datasource(ro_ds, True)
|
||||
log_msg = "Read only datasource %s initialized for LeObject %s"
|
||||
log_msg %= (ro_ds, cls.__name__)
|
||||
logger.debug(log_msg)
|
||||
if cls._ro_datasource is None:
|
||||
log_msg = "No read only datasource set for LeObject %s"
|
||||
log_msg %= cls.__name__
|
||||
logger.debug(log_msg)
|
||||
else:
|
||||
log_msg = "Read only datasource '%s' initialized for LeObject %s"
|
||||
log_msg %= (ro_ds, cls.__name__)
|
||||
logger.debug(log_msg)
|
||||
#Read write datasource initialisation
|
||||
cls._rw_datasource = cls._init_datasource(rw_ds, False)
|
||||
log_msg = "Read&write only datasource %s initialized for LeObject %s"
|
||||
log_msg %= (rw_ds, cls.__name__)
|
||||
logger.debug(log_msg)
|
||||
if cls._ro_datasource is None:
|
||||
log_msg = "No read/write datasource set for LeObject %s"
|
||||
log_msg %= cls.__name__
|
||||
logger.debug(log_msg)
|
||||
else:
|
||||
log_msg = "Read/write datasource '%s' initialized for LeObject %s"
|
||||
log_msg %= (ro_ds, cls.__name__)
|
||||
logger.debug(log_msg)
|
||||
|
||||
|
||||
##@brief Replace the _datasource attribute value by a datasource instance
|
||||
|
|
@ -586,7 +599,6 @@ construction and consitency when datas are not complete\n")
|
|||
query_filter = list()
|
||||
for uid in uids:
|
||||
query_filter.append((uid, '=', self.data(uid)))
|
||||
|
||||
try:
|
||||
query = LeUpdateQuery(self.__class__, query_filter)
|
||||
except Exception as err:
|
||||
|
|
@ -648,9 +660,7 @@ construction and consitency when datas are not complete\n")
|
|||
#@return a list of items (lists of (fieldname, fieldvalue))
|
||||
@classmethod
|
||||
def get(cls, query_filters, field_list=None, order=None, group=None, limit=None, offset=0):
|
||||
if field_list is None:
|
||||
field_list = cls.fieldnames(True)
|
||||
else:
|
||||
if field_list is not None:
|
||||
for uid in [ uidname
|
||||
for uidname in cls.uid_fieldname()
|
||||
if uidname not in field_list ]:
|
||||
|
|
|
|||
|
|
@ -188,7 +188,9 @@ class LeFilteredQuery(LeQuery):
|
|||
other_ds_filters[cur_ds].append(
|
||||
((rfield, ref_dict), op, value))
|
||||
#deduplication of std filters
|
||||
filters_orig = list(set(filters_orig))
|
||||
if not isinstance(filters_orig, set):
|
||||
filters_orig = set(filters_orig)
|
||||
filters_orig = list(filters_orig)
|
||||
# Sets _query_filter attribute of self query
|
||||
self._query_filter = (filters_orig, result_rel_filters)
|
||||
|
||||
|
|
@ -290,6 +292,9 @@ field name" % fieldname)
|
|||
err_l[field] = ret
|
||||
continue
|
||||
field_datahandler = self._target_class.field(field)
|
||||
if isinstance(field_datahandler, Exception):
|
||||
err_l[field] = error
|
||||
continue
|
||||
if ref_field is not None and not field_datahandler.is_reference():
|
||||
# inconsistency
|
||||
err_l[field] = NameError( "The field '%s' in %s is not \
|
||||
|
|
@ -330,10 +335,12 @@ field to use for the relational filter"
|
|||
else:
|
||||
rel_filters.append((ret, operator, value))
|
||||
else:
|
||||
# Casting value given datahandler
|
||||
value, error = field_datahandler._check_data_value(value)
|
||||
res_filters.append((field,operator, value))
|
||||
|
||||
if len(err_l) > 0:
|
||||
raise LeApiDataCheckError(
|
||||
raise LeApiDataCheckErrors(
|
||||
"Error while preparing filters : ",
|
||||
err_l)
|
||||
return (res_filters, rel_filters)
|
||||
|
|
@ -517,7 +524,7 @@ target to LeUpdateQuery constructor"
|
|||
if target_class.initialized:
|
||||
self.__leobject_instance_datas = target.datas(True)
|
||||
else:
|
||||
query_filters = [(target._uid[0], '=', str(target.uid()))]
|
||||
query_filters = [(target._uid[0], '=', target.uid())]
|
||||
|
||||
super().__init__(target_class, query_filters)
|
||||
|
||||
|
|
@ -552,7 +559,7 @@ target to LeUpdateQuery constructor"
|
|||
res_data.update(datas)
|
||||
res_datas = self._target_class.prepare_datas(
|
||||
res_data, True, True)
|
||||
filters = [(uid_name, '=', str(res_data[uid_name]))]
|
||||
filters = [(uid_name, '=', res_data[uid_name])]
|
||||
res = self._rw_datasource.update(
|
||||
self._target_class, filters, [],
|
||||
res_datas)
|
||||
|
|
@ -658,17 +665,18 @@ class LeGetQuery(LeFilteredQuery):
|
|||
# @throw LeApiQueryError if unknown field given
|
||||
def set_field_list(self, field_list):
|
||||
err_l = dict()
|
||||
for fieldname in field_list:
|
||||
ret = self._check_field(self._target_class, fieldname)
|
||||
if isinstance(ret, Exception):
|
||||
msg = "No field named '%s' in %s"
|
||||
msg %= (fieldname, self._target_class.__name__)
|
||||
expt = NameError(msg)
|
||||
err_l[fieldname] = expt
|
||||
if len(err_l) > 0:
|
||||
msg = "Error while setting field_list in a get query"
|
||||
raise LeApiQueryErrors(msg = msg, exceptions = err_l)
|
||||
self._field_list = list(set(field_list))
|
||||
if field_list is not None:
|
||||
for fieldname in field_list:
|
||||
ret = self._check_field(self._target_class, fieldname)
|
||||
if isinstance(ret, Exception):
|
||||
msg = "No field named '%s' in %s"
|
||||
msg %= (fieldname, self._target_class.__name__)
|
||||
expt = NameError(msg)
|
||||
err_l[fieldname] = expt
|
||||
if len(err_l) > 0:
|
||||
msg = "Error while setting field_list in a get query"
|
||||
raise LeApiQueryErrors(msg = msg, exceptions = err_l)
|
||||
self._field_list = list(set(field_list))
|
||||
|
||||
##@brief Execute the get query
|
||||
def execute(self, datas = None):
|
||||
|
|
@ -678,9 +686,10 @@ class LeGetQuery(LeFilteredQuery):
|
|||
# @returns a list containing the item(s)
|
||||
def _query(self, datas = None):
|
||||
# select datas corresponding to query_filter
|
||||
fl = list(self._field_list) if self._field_list is not None else None
|
||||
l_datas=self._ro_datasource.select(
|
||||
target = self._target_class,
|
||||
field_list = list(self._field_list),
|
||||
field_list = fl,
|
||||
filters = self._query_filter[0],
|
||||
relational_filters = self._query_filter[1],
|
||||
order = self._order,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import copy
|
|||
from importlib.machinery import SourceFileLoader, SourcelessFileLoader
|
||||
|
||||
import plugins
|
||||
from .exceptions import *
|
||||
|
||||
## @package lodel.plugins Lodel2 plugins management
|
||||
#
|
||||
|
|
@ -25,8 +26,6 @@ LOADER_FILENAME_VARNAME = '__loader__'
|
|||
PLUGIN_DEPS_VARNAME = '__plugin_deps__'
|
||||
ACTIVATE_METHOD_NAME = '_activate'
|
||||
|
||||
class PluginError(Exception):
|
||||
pass
|
||||
|
||||
##@brief Handle plugins
|
||||
#
|
||||
|
|
@ -119,7 +118,14 @@ class Plugin(object):
|
|||
#@throw PluginError if the filename was not valid
|
||||
def _import_from_init_var(self, varname):
|
||||
# Read varname
|
||||
filename = getattr(self.module, varname)
|
||||
try:
|
||||
filename = getattr(self.module, varname)
|
||||
except AttributeError:
|
||||
msg = "Malformed plugin {plugin}. No {varname} found in __init__.py"
|
||||
msg = msg.format(
|
||||
plugin = self.name,
|
||||
varname = LOADER_FILENAME_VARNAME)
|
||||
raise PluginError(msg)
|
||||
#Path are not allowed
|
||||
if filename != os.path.basename(filename):
|
||||
msg = "Invalid {varname} content : '{fname}' for plugin {name}"
|
||||
|
|
@ -213,12 +219,8 @@ class Plugin(object):
|
|||
#Loading the plugin
|
||||
try:
|
||||
self.__loader_module = self._import_from_init_var(LOADER_FILENAME_VARNAME)
|
||||
except AttributeError:
|
||||
msg = "Malformed plugin {plugin}. No {varname} found in __init__.py"
|
||||
msg = msg.format(
|
||||
plugin = self.name,
|
||||
varname = LOADER_FILENAME_VARNAME)
|
||||
raise PluginError(msg)
|
||||
except PluginError as e:
|
||||
raise e
|
||||
except ImportError as e:
|
||||
msg = "Broken plugin {plugin} : {expt}"
|
||||
msg = msg.format(
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from lodel.settings.validator import SettingValidator
|
|||
|
||||
CONFSPEC = {
|
||||
'lodel2.datasource.mongodb_datasource.*':{
|
||||
'read_only': (True, SettingValidator('bool')),
|
||||
'read_only': (False, SettingValidator('bool')),
|
||||
'host': ('localhost', SettingValidator('host')),
|
||||
'port': (None, SettingValidator('string')),
|
||||
'db_name':('lodel', SettingValidator('string')),
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ from lodel import logger
|
|||
from lodel.leapi.leobject import CLASS_ID_FIELDNAME
|
||||
|
||||
from . import utils
|
||||
from .utils import object_collection_name,\
|
||||
MONGODB_SORT_OPERATORS_MAP, connection_string
|
||||
from .utils import object_collection_name, collection_name, \
|
||||
MONGODB_SORT_OPERATORS_MAP, connection_string, mongo_fieldname
|
||||
|
||||
class MongoDbDataSourceError(Exception):
|
||||
pass
|
||||
|
|
@ -75,7 +75,7 @@ class MongoDbDatasource(object):
|
|||
#@warning multiple UID broken by this method
|
||||
#@return an integer
|
||||
def new_numeric_id(self, emcomp):
|
||||
target = emcomp.uid_source()
|
||||
target = emcomp #.uid_source()
|
||||
tuid = target._uid[0] # Multiple UID broken here
|
||||
results = self.select(
|
||||
target, field_list = [tuid], filters = [],
|
||||
|
|
@ -130,16 +130,26 @@ class MongoDbDatasource(object):
|
|||
|
||||
query_filters = self.__process_filters(
|
||||
target, filters, relational_filters)
|
||||
|
||||
query_result_ordering = None
|
||||
if order is not None:
|
||||
query_result_ordering = utils.parse_query_order(order)
|
||||
results_field_list = None if len(field_list) == 0 else field_list
|
||||
limit = limit if limit is not None else 0
|
||||
|
||||
|
||||
if group is None:
|
||||
if field_list is None:
|
||||
field_list = dict()
|
||||
else:
|
||||
f_list=dict()
|
||||
for fl in field_list:
|
||||
f_list[fl] = 1
|
||||
field_list = f_list
|
||||
field_list['_id'] = 0
|
||||
cursor = collection.find(
|
||||
filter=query_filters, projection=results_field_list,
|
||||
skip=offset, limit=limit, sort=query_result_ordering)
|
||||
spec = query_filters,
|
||||
fields=field_list,
|
||||
skip=offset,
|
||||
limit=limit if limit != None else 0,
|
||||
sort=query_result_ordering)
|
||||
else:
|
||||
pipeline = list()
|
||||
unwinding_list = list()
|
||||
|
|
@ -156,7 +166,7 @@ class MongoDbDatasource(object):
|
|||
sorting_list.extends(query_result_ordering)
|
||||
|
||||
pipeline.append({'$match': query_filters})
|
||||
if results_field_list is not None:
|
||||
if field_list is not None:
|
||||
pipeline.append({
|
||||
'$project': SON([{field_name: 1}
|
||||
for field_name in field_list])})
|
||||
|
|
@ -180,7 +190,7 @@ class MongoDbDatasource(object):
|
|||
#@param relational_filters list : List of relational filters
|
||||
#@return int : number of deleted records
|
||||
def delete(self, target, filters, relational_filters):
|
||||
if target.is_asbtract():
|
||||
if target.is_abstract():
|
||||
#Deletion with abstract LeObject as target (reccursiv calls)
|
||||
return self.__act_on_abstract(target, filters,
|
||||
relational_filters, self.delete)
|
||||
|
|
@ -197,15 +207,15 @@ class MongoDbDatasource(object):
|
|||
#@param upd_datas dict : datas to update (new values)
|
||||
#@return int : Number of updated records
|
||||
def update(self, target, filters, relational_filters, upd_datas):
|
||||
if target.is_asbtract():
|
||||
if target.is_abstract():
|
||||
#Update using abstract LeObject as target (reccursiv calls)
|
||||
return self.__act_on_abstract(target, filters,
|
||||
relational_filters, self.update, upd_datas = upd_datas)
|
||||
#Non abstract beahavior
|
||||
mongo_filters = self.__process_filters(
|
||||
target, filters, relational_filters)
|
||||
res = self.__collection(target).update_many(mongo_filters, upd_datas)
|
||||
return res.modified_count()
|
||||
res = self.__collection(target).update(mongo_filters, upd_datas)
|
||||
return res['n']
|
||||
|
||||
## @brief Inserts a record in a given collection
|
||||
# @param target Emclass : class of the object to insert
|
||||
|
|
@ -252,7 +262,8 @@ class MongoDbDatasource(object):
|
|||
fname, op, val))
|
||||
del(new_filters[i])
|
||||
new_filters.append(
|
||||
(CLASS_ID_FIELDNAME, '=', target_child.__name__))
|
||||
(CLASS_ID_FIELDNAME, '=',
|
||||
collection_name(target_child.__name__)))
|
||||
result += act(
|
||||
target = target_child,
|
||||
filters = new_filters,
|
||||
|
|
@ -348,7 +359,7 @@ class MongoDbDatasource(object):
|
|||
if '$in' in res[fname]:
|
||||
#WARNING we allready have a IN on this field, doing dedup
|
||||
#from result
|
||||
deduped = set(res[fname]['$in']) & subq
|
||||
deduped = set(res[fname]['$in']) & subq_results
|
||||
if len(deduped) == 0:
|
||||
del(res[fname]['$in'])
|
||||
else:
|
||||
|
|
@ -419,8 +430,8 @@ class MongoDbDatasource(object):
|
|||
#here we are filling a dict with leobject as index but
|
||||
#we are doing a UNIQ on collection name
|
||||
cur_collname = object_collection_name(leobject)
|
||||
if cur_collname not in collnames:
|
||||
leo_collname[cur_collame] = leobject
|
||||
if cur_collname not in leo_collname:
|
||||
leo_collname[cur_collname] = leobject
|
||||
rfilters[fname][leobject] = dict()
|
||||
#Fecthing the collection's representative leobject
|
||||
repr_leo = leo_collname[cur_collname]
|
||||
|
|
@ -436,17 +447,33 @@ class MongoDbDatasource(object):
|
|||
@classmethod
|
||||
def __filters2mongo(cls, filters):
|
||||
res = dict()
|
||||
eq_fieldname = [] #Stores field with equal comparison OP
|
||||
for fieldname, op, value in filters:
|
||||
oop = op
|
||||
ovalue = value
|
||||
op, value = cls.__op_value_conv(op, value)
|
||||
if op == '=':
|
||||
eq_fieldname.append(fieldname)
|
||||
if fieldname in res:
|
||||
logger.warning("Dropping previous condition. Overwritten \
|
||||
by an equality filter")
|
||||
res[fieldname] = value
|
||||
continue
|
||||
if fieldname in eq_fieldname:
|
||||
logger.warning("Dropping condition : '%s %s %s'" % (
|
||||
fieldname, op, value))
|
||||
continue
|
||||
|
||||
if fieldname not in res:
|
||||
res[fieldname] = dict()
|
||||
if op in res[fieldname]:
|
||||
logger.warning("Dropping condition : '%s %s %s'" % (
|
||||
fieldname, op, value))
|
||||
else:
|
||||
res[fieldname][op] = value
|
||||
if op not in cls.lodel2mongo_op_map:
|
||||
raise ValueError("Invalid operator : '%s'" % op)
|
||||
new_op = cls.lodel2mongo_op_map[op]
|
||||
res[fieldname][new_op] = value
|
||||
return res
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -69,6 +69,9 @@ def connect(host, port, db_name, username, password):
|
|||
def object_collection_name(class_object):
|
||||
return class_object.__name__
|
||||
|
||||
def collection_name(class_name):
|
||||
return class_name
|
||||
|
||||
## @brief Determine a collection field name given a lodel2 fieldname
|
||||
# @note For the moment this method only return the argument but EVERYWHERE
|
||||
# in the datasource we should use this method to gather proper fieldnames
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ CONFSPEC = {
|
|||
SettingValidator('dummy')),
|
||||
'listen_port': ( '9090',
|
||||
SettingValidator('int')),
|
||||
'virtualenv': ('',
|
||||
SettingValidator('path')),
|
||||
'uwsgicmd': ('uwsgi_python3', SettingValidator('dummy')),
|
||||
},
|
||||
'lodel2.webui.sessions': {
|
||||
'directory': ( '/tmp/lodel2_session',
|
||||
|
|
|
|||
51
plugins/webui/exceptions.py
Normal file
51
plugins/webui/exceptions.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#-*- coding: utf-8 -*-
|
||||
|
||||
from werkzeug.wrappers import Response
|
||||
|
||||
class HttpException(Exception):
|
||||
|
||||
STATUS_STR = {
|
||||
4:{
|
||||
400: 'Bad request',
|
||||
401: 'Unauthorized',
|
||||
402: 'Payment required',
|
||||
403: 'Forbidden',
|
||||
404: 'Not found',
|
||||
418: 'I\'m a teapot', #RFC 2324
|
||||
},
|
||||
5:{
|
||||
500: 'Internal server error',
|
||||
501: 'Not implemented',
|
||||
},
|
||||
}
|
||||
|
||||
def __init__(self, status_code = 500, tpl = 'error.html', custom = None):
|
||||
self.status_code = status_code
|
||||
self.tpl = tpl
|
||||
self.custom = custom
|
||||
|
||||
def render(self, request):
|
||||
from .interface.template.loader import TemplateLoader
|
||||
loader = TemplateLoader()
|
||||
tpl_vars = {
|
||||
'status_code': self.status_code,
|
||||
'status_str': self.status_str(self.status_code),
|
||||
'custom': self.custom }
|
||||
response = Response(
|
||||
loader.render_to_response(self.tpl, template_vars = tpl_vars),
|
||||
mimetype = 'text/html')
|
||||
response.status_code = self.status_code
|
||||
return response
|
||||
|
||||
@staticmethod
|
||||
def status_str(status_code):
|
||||
status_fam = status_code / 100
|
||||
if status_fam not in HttpException.STATUS_STR or \
|
||||
status_code not in HttpException.STATUS_STR[status_fam]:
|
||||
return 'Unknown'
|
||||
else:
|
||||
return HttpException.STATUS_STR[status_fam][status_code]
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,2 +1,5 @@
|
|||
from .base import *
|
||||
from .admin import *
|
||||
from .document import *
|
||||
from .listing import *
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,135 @@
|
|||
from ...exceptions import *
|
||||
from .base import get_response
|
||||
|
||||
from lodel.leapi.exceptions import *
|
||||
from lodel import logger
|
||||
|
||||
import leapi_dyncode as dyncode
|
||||
import warnings
|
||||
from lodel import logger
|
||||
|
||||
def index_admin(request):
|
||||
return get_response('admin/admin.html')
|
||||
|
||||
def admin_update(request):
|
||||
msg=''
|
||||
if request.method == 'POST':
|
||||
|
||||
error = None
|
||||
datas = list()
|
||||
classname = request.form['classname']
|
||||
uid = request.form['uid']
|
||||
try:
|
||||
target_leo = dyncode.Object.name2class(classname)
|
||||
except LeApiError:
|
||||
classname = None
|
||||
if classname is None or target_leo.is_abstract():
|
||||
raise HttpException(400)
|
||||
fieldnames = target_leo.fieldnames()
|
||||
fields = dict()
|
||||
|
||||
for in_put, in_value in request.form.items():
|
||||
if in_put != 'classname' and in_put != 'uid':
|
||||
fields[in_put[12:]] = in_value
|
||||
obj = (target_leo.get(('lodel_id = %s' % (uid))))[0]
|
||||
inserted = obj.update(fields)
|
||||
|
||||
if inserted==1:
|
||||
msg = 'Successfully updated';
|
||||
else:
|
||||
msg = 'Oops something wrong happened...object not saved'
|
||||
return get_response('admin/admin_edit.html', target=target_leo, lodel_id = uid, msg = msg)
|
||||
|
||||
test_valid = 'lodel_id' in request.GET \
|
||||
and len(request.GET['lodel_id']) == 1
|
||||
|
||||
if test_valid:
|
||||
try:
|
||||
lodel_id = int(request.GET['lodel_id'][0])
|
||||
except (ValueError, TypeError):
|
||||
test_valid = False
|
||||
|
||||
if not test_valid:
|
||||
raise HttpException(400)
|
||||
else:
|
||||
obj = dyncode.Object.get(['lodel_id = %d' % lodel_id])
|
||||
if len(obj) == 0:
|
||||
raise HttpException(404)
|
||||
if 'classname' in request.GET:
|
||||
classname = request.GET['classname']
|
||||
if len(classname) > 1:
|
||||
raise HttpException(400)
|
||||
classname = classname[0]
|
||||
try:
|
||||
target_leo = dyncode.Object.name2class(classname)
|
||||
except LeApiError:
|
||||
classname = None
|
||||
|
||||
return get_response('admin/admin_edit.html', target=target_leo, lodel_id =lodel_id)
|
||||
|
||||
def admin_create(request):
|
||||
classname = None
|
||||
|
||||
if request.method == 'POST':
|
||||
error = None
|
||||
datas = list()
|
||||
classname = request.form['classname']
|
||||
try:
|
||||
target_leo = dyncode.Object.name2class(classname)
|
||||
except LeApiError:
|
||||
classname = None
|
||||
if classname is None or target_leo.is_abstract():
|
||||
raise HttpException(400)
|
||||
fieldnames = target_leo.fieldnames()
|
||||
fields = dict()
|
||||
|
||||
for in_put, in_value in request.form.items():
|
||||
if in_put != 'classname':
|
||||
fields[in_put[12:]] = in_value
|
||||
new_uid = target_leo.insert(fields)
|
||||
|
||||
if not new_uid is None:
|
||||
msg = 'Successfull creation';
|
||||
else:
|
||||
msg = 'Oops something wrong happened...object not saved'
|
||||
return get_response('admin/admin_create.html', target=target_leo, msg = msg)
|
||||
|
||||
if 'classname' in request.GET:
|
||||
classname = request.GET['classname']
|
||||
if len(classname) > 1:
|
||||
raise HttpException(400)
|
||||
classname = classname[0]
|
||||
try:
|
||||
target_leo = dyncode.Object.name2class(classname)
|
||||
except LeApiError:
|
||||
classname = None
|
||||
msg = None
|
||||
if 'msg' in request.GET:
|
||||
msg = request.GET['msg']
|
||||
if classname is None or target_leo.is_abstract():
|
||||
raise HttpException(400)
|
||||
return get_response('admin/admin_create.html', target=target_leo)
|
||||
|
||||
def admin_classes(request):
|
||||
return get_response('admin/list_classes_admin.html', my_classes = dyncode.dynclasses)
|
||||
|
||||
def admin_class(request):
|
||||
if 'classname' in request.GET:
|
||||
classname = request.GET['classname']
|
||||
if len(classname) > 1:
|
||||
raise HttpException(400)
|
||||
classname = classname[0]
|
||||
try:
|
||||
target_leo = dyncode.Object.name2class(classname)
|
||||
except LeApiError:
|
||||
classname = None
|
||||
if classname is None or target_leo.is_abstract():
|
||||
raise HttpException(400)
|
||||
return get_response('admin/show_class_admin.html', target=target_leo)
|
||||
|
||||
def admin(request):
|
||||
return get_response('admin/admin.html')
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,13 +5,27 @@ from ..template.loader import TemplateLoader
|
|||
|
||||
# This module contains the web UI controllers that will be called from the web ui class
|
||||
|
||||
|
||||
def get_response(tpl, mimetype='text/html', status_code=200):
|
||||
##@brief Render a template and return a respone
|
||||
#@param tpl str : template relativ path
|
||||
#@param tpl_vars : templates variables (obsolete)
|
||||
#@param mimetype
|
||||
#@param status_code
|
||||
#@param **kwargs : new version of tpl_vars
|
||||
#@return a response...
|
||||
def get_response(tpl='empty.html', tpl_vars={}, mimetype='text/html', status_code=200, **kwargs):
|
||||
tpl_vars.update(kwargs)
|
||||
loader = TemplateLoader()
|
||||
response = Response(loader.render_to_response(tpl), mimetype=mimetype)
|
||||
response = Response(loader.render_to_response(tpl, template_vars=tpl_vars), mimetype=mimetype)
|
||||
response.status_code = status_code
|
||||
return response
|
||||
|
||||
## @brief gets the html template corresponding to a given component type
|
||||
# @param type str : name of the component type
|
||||
# @param params dict : extra parameters to customize the template
|
||||
def get_component_html(type='text', params={}):
|
||||
params['type'] = type
|
||||
template_loader = TemplateLoader()
|
||||
return template_loader.render_to_html(template_file='components/components.html', template_vars=params)
|
||||
|
||||
def index(request):
|
||||
return get_response('index/index.html')
|
||||
|
|
@ -22,10 +36,14 @@ def not_found(request):
|
|||
|
||||
|
||||
def test(request):
|
||||
return get_response('test.html')
|
||||
if 'id' not in request.url_args:
|
||||
id = None
|
||||
else:
|
||||
id = request.url_args['id']
|
||||
|
||||
|
||||
def list_classes(request):
|
||||
# TODO Add the method to get the classes list
|
||||
|
||||
return get_response('list_classes.html')
|
||||
template_vars = {
|
||||
'id': id,
|
||||
'params': request.GET
|
||||
}
|
||||
return get_response('test.html', tpl_vars=template_vars)
|
||||
|
||||
|
|
|
|||
6
plugins/webui/interface/controllers/document.py
Normal file
6
plugins/webui/interface/controllers/document.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
from .base import get_response
|
||||
|
||||
|
||||
def show_document(request):
|
||||
template_vars = {'id': request.url_args['id']}
|
||||
return get_response('documents/show.html', tpl_vars=template_vars)
|
||||
19
plugins/webui/interface/controllers/listing.py
Normal file
19
plugins/webui/interface/controllers/listing.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from .base import get_response
|
||||
import leapi_dyncode as dyncode
|
||||
|
||||
def list_classes(request):
|
||||
template_vars = {'my_classes': dyncode.dynclasses}
|
||||
return get_response('listing/list_classes.html', tpl_vars=template_vars)
|
||||
|
||||
def show_class(request):
|
||||
template_vars = {
|
||||
'params': request.GET
|
||||
}
|
||||
return get_response('listing/show_class.html', tpl_vars=template_vars)
|
||||
|
||||
def show_object(request):
|
||||
template_vars = {
|
||||
'params': request.GET
|
||||
}
|
||||
return get_response('listing/show_object.html', tpl_vars=template_vars)
|
||||
|
|
@ -3,19 +3,15 @@ import re
|
|||
|
||||
from .controllers import *
|
||||
from .urls import urls
|
||||
from ..main import root_url
|
||||
from lodel.settings import Settings
|
||||
|
||||
|
||||
def format_url_rule(url_rule):
|
||||
if url_rule == '^$':
|
||||
return "^%s$" % Settings.sitename
|
||||
|
||||
formatted_rule = ''
|
||||
if url_rule.startswith('^'):
|
||||
formatted_rule += "^"
|
||||
|
||||
formatted_rule += "%s/%s" % (Settings.sitename, url_rule)
|
||||
return formatted_rule
|
||||
res = url_rule.replace('^', '^'+root_url())
|
||||
else:
|
||||
res = root_url()+'.*'+url_rule
|
||||
return res
|
||||
|
||||
|
||||
def get_controller(request):
|
||||
|
|
@ -26,9 +22,10 @@ def get_controller(request):
|
|||
|
||||
# Returning the right controller to call
|
||||
for regex, callback in url_rules:
|
||||
match = re.search(regex, request.PATH)
|
||||
if match is not None:
|
||||
request.url_args = match.groups()
|
||||
p = re.compile(regex)
|
||||
m = p.search(request.PATH)
|
||||
if m is not None:
|
||||
request.url_args = m.groupdict()
|
||||
return callback
|
||||
|
||||
return not_found
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Lodel 2 templates API : loaded by default
|
||||
|
||||
class Test(object):
|
||||
|
||||
def ok(self):
|
||||
return 'ok'
|
||||
|
|
@ -2,9 +2,12 @@
|
|||
import jinja2
|
||||
import os
|
||||
|
||||
import settings
|
||||
from lodel.settings import Settings
|
||||
import leapi_dyncode
|
||||
|
||||
from .api import api_lodel_templates
|
||||
from .exceptions.not_allowed_custom_api_key_error import NotAllowedCustomAPIKeyError
|
||||
from ...main import root_url
|
||||
|
||||
from ...main import PLUGIN_PATH
|
||||
TEMPLATE_PATH = os.path.realpath(os.path.join(PLUGIN_PATH, 'templates/'))
|
||||
|
|
@ -39,6 +42,11 @@ class TemplateLoader(object):
|
|||
# lodel2 default api is loaded
|
||||
# TODO change this if needed
|
||||
template.globals['lodel'] = api_lodel_templates
|
||||
template.globals['leapi'] = leapi_dyncode
|
||||
template.globals['settings'] = Settings
|
||||
template.globals['url'] = lambda sufix='': root_url()\
|
||||
+ ('' if sufix.startswith('/') else '/')\
|
||||
+ sufix
|
||||
|
||||
# Extra modules are loaded
|
||||
if template_extra is not None:
|
||||
|
|
|
|||
|
|
@ -1,9 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from .controllers import *
|
||||
|
||||
urls = (
|
||||
(r'^$', index),
|
||||
(r'admin/?$', admin),
|
||||
(r'admin/(.+)$', admin),
|
||||
(r'test/(.+)$', test),
|
||||
(r'test/?$', test)
|
||||
(r'^/?$', index),
|
||||
(r'^/admin/?$', admin),
|
||||
(r'^/admin/create$', admin_create),
|
||||
(r'^/admin/update$', admin_update),
|
||||
(r'^/admin/classes_admin$', admin_classes),
|
||||
(r'^/admin/class_admin$', admin_class),
|
||||
(r'/test/(?P<id>.*)$', test),
|
||||
(r'^/test/?$', test),
|
||||
#(r'/show/(?P<id>.*)$', show_document),
|
||||
(r'^/list_classes', list_classes),
|
||||
#(r'^/show_object/(.+)$', show_object),
|
||||
(r'^/show_object?$', show_object),
|
||||
#(r'^/show_class/(.+)$', show_class),
|
||||
(r'^/show_class?$', show_class)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,12 +6,25 @@ from lodel.settings import Settings
|
|||
|
||||
PLUGIN_PATH = os.path.dirname(__file__)
|
||||
|
||||
##@brief Return the root url of the instance
|
||||
#@warning no trailing slash
|
||||
def root_url():
|
||||
return Settings.sitename
|
||||
|
||||
|
||||
##@brief uwsgi startup demo
|
||||
@LodelHook('lodel2_loader_main')
|
||||
def uwsgi_fork(hook_name, caller, payload):
|
||||
from lodel.plugin.plugins import Plugin
|
||||
Plugin.from_name('users')
|
||||
|
||||
if Settings.webui.standalone:
|
||||
cmd='uwsgi_python3 --http-socket {addr}:{port} --module plugins.webui.run'
|
||||
cmd='{uwsgi} --http-socket {addr}:{port} --module plugins.webui.run'
|
||||
cmd = cmd.format(
|
||||
addr = Settings.webui.listen_address,
|
||||
port = Settings.webui.listen_port)
|
||||
port = Settings.webui.listen_port,
|
||||
uwsgi= Settings.webui.uwsgicmd)
|
||||
if Settings.webui.virtualenv != '':
|
||||
cmd += " --virtualenv %s" % Settings.webui.virtualenv
|
||||
|
||||
exit(os.system(cmd))
|
||||
|
|
|
|||
|
|
@ -3,11 +3,14 @@ import loader # Lodel2 loader
|
|||
|
||||
import os
|
||||
from werkzeug.contrib.sessions import FilesystemSessionStore
|
||||
from werkzeug.wrappers import Response
|
||||
|
||||
from lodel.settings import Settings
|
||||
from .interface.router import get_controller
|
||||
from .interface.lodelrequest import LodelRequest
|
||||
from .exceptions import *
|
||||
from lodel.utils.datetime import get_utc_timestamp
|
||||
from lodel.plugin.hooks import LodelHook
|
||||
|
||||
SESSION_FILES_BASE_DIR = Settings.webui.sessions.directory
|
||||
SESSION_FILES_TEMPLATE = Settings.webui.sessions.file_template
|
||||
|
|
@ -15,6 +18,8 @@ SESSION_EXPIRATION_LIMIT = Settings.webui.sessions.expiration
|
|||
|
||||
session_store = FilesystemSessionStore(path=SESSION_FILES_BASE_DIR, filename_template=SESSION_FILES_TEMPLATE)
|
||||
|
||||
#Starting instance
|
||||
loader.start()
|
||||
|
||||
# TODO déplacer dans un module "sessions.py"
|
||||
def delete_old_session_files(timestamp_now):
|
||||
|
|
@ -53,11 +58,23 @@ def application(env, start_response):
|
|||
request.session = session_store.new()
|
||||
request.session['user_context'] = None
|
||||
request.session['last_accessed'] = current_timestamp
|
||||
|
||||
controller = get_controller(request)
|
||||
response = controller(request)
|
||||
|
||||
try:
|
||||
controller = get_controller(request)
|
||||
response = controller(request)
|
||||
except HttpException as e:
|
||||
try:
|
||||
response = e.render(request)
|
||||
except Exception as eb:
|
||||
res = Response()
|
||||
res.status_code = 500
|
||||
return res
|
||||
|
||||
|
||||
if request.session.should_save:
|
||||
session_store.save(request.session)
|
||||
response.set_cookie('sid', request.session.sid)
|
||||
|
||||
return response(env, start_response)
|
||||
|
||||
res = response(env, start_response)
|
||||
LodelHook.call_hook('lodel2_session_end', __file__, None)
|
||||
return res
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
{% extends "base_backend.html" %}
|
||||
{% block title %}Lodel 2 - ADMIN{% endblock %}
|
||||
{% block content %}ADMIN{% endblock %}
|
||||
{% block title %}- Index{% endblock %}
|
||||
{% block body %}
|
||||
<h1>{{settings.sitename}} administration</h1>
|
||||
{{url('admin')}}
|
||||
<ul>
|
||||
<li><a href="classes_admin">List of Classes</a></li>
|
||||
</ul>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
|||
20
plugins/webui/templates/admin/admin_create.html
Normal file
20
plugins/webui/templates/admin/admin_create.html
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{% extends "base_backend.html" %}
|
||||
{% import "admin/editable_component.html" as edit %}
|
||||
|
||||
{% block title %}- Creating a new {{target.__name__}}{% endblock %}
|
||||
{% block body %}
|
||||
{% if msg is not none %}
|
||||
{% block msg %} <p style="color:red; font-size:20pt; font-weight:bold">{{ msg }}</p> {% endblock %}
|
||||
{% endif %}
|
||||
<h1>Creating a new {{target.__name__}}</h1>
|
||||
<form action="" method ="post">
|
||||
<input type="hidden" name="classname" id="classname" value="{{target.__name__}}" />
|
||||
{% for fieldname, field in target.fields().items() %}
|
||||
<div style="padding-bottom:15px;"> {{edit.input(fieldname, field) }}</div>
|
||||
{% endfor %}
|
||||
<p> </p>
|
||||
<input type="submit" value="Save">
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
19
plugins/webui/templates/admin/admin_edit.html
Normal file
19
plugins/webui/templates/admin/admin_edit.html
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{% extends "base_backend.html" %}
|
||||
{% import "admin/editable_component.html" as edit %}
|
||||
{% set objects = target.get(('lodel_id = %s') % (lodel_id)) %}
|
||||
{% set obj = objects.pop() %}
|
||||
{% block title %}- Edit Object{% endblock %}
|
||||
{% block body %}
|
||||
{% if msg is not none %}
|
||||
{% block msg %} <p style="color:red; font-size:20pt; font-weight:bold">{{ msg }}</p> {% endblock %}
|
||||
{% endif %}
|
||||
<h1>Lodel 2 - Edit Object {{ lodel_id }} of {{ target.__name__ }}</h1>
|
||||
<form action="" method ="post">
|
||||
<input type="hidden" name="uid" value="{{ lodel_id}}" >
|
||||
<input type="hidden" name="classname" value={{ target.__name__ }} />
|
||||
{% for fieldname, fieldvalue in obj.fields().items() %}
|
||||
<div style="padding-bottom:15px;"> {{edit.input(fieldname, fieldvalue, obj.data(fieldname)) }} </div>
|
||||
{% endfor %}
|
||||
<input type="submit" value="Save">
|
||||
</form>
|
||||
{% endblock %}
|
||||
10
plugins/webui/templates/admin/editable_component.html
Normal file
10
plugins/webui/templates/admin/editable_component.html
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{% macro input(fieldname, field, value='') -%}
|
||||
<label for="field_input_{{fieldname}}">{{fieldname}}</label>
|
||||
{% if field.base_type == 'bool' %}
|
||||
<input id="field_input_{{fieldname}}" name="field_input_{{fieldname}}" type="checkbox" checked="{% if value %}checked{% endif %}" />
|
||||
{% elif field.base_type == 'char' or field.base_type == 'int' %}
|
||||
<input id="{{fieldname}}" name="field_input_{{fieldname}}" type="text" value="{{value}}" />
|
||||
{% else %}
|
||||
Unsupported base type "{{field.base_type}}" </br>
|
||||
{% endif %}
|
||||
{%- endmacro %}
|
||||
|
|
@ -2,14 +2,12 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>{% block title %}{% endblock %}</title>
|
||||
<title>{{ settings.sitename }} Admin{% block title %}{% endblock %}</title>
|
||||
{% block style %}{% endblock %}
|
||||
{% block scripts %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
{% block body %}{% endblock %}
|
||||
<script type="text/javascript">{% block javascript %}{% endblock %}</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
|||
9
plugins/webui/templates/components/components.html
Normal file
9
plugins/webui/templates/components/components.html
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{% macro input(name, value='', type='text') -%}
|
||||
<input type="{{ type }}" value="{{ value }}" name="{{ name }}" id= "{{ name }}"/>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro textarea(name, value='', rows=10, cols=40) -%}
|
||||
<textarea name="{{ name }}" rows="{{ rows }}" cols="{{ cols }}">
|
||||
{{ value|e }}
|
||||
</textarea>
|
||||
{%- endmacro %}
|
||||
5
plugins/webui/templates/documents/show.html
Normal file
5
plugins/webui/templates/documents/show.html
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{% extends "base_backend.html" %}
|
||||
{% block title %}Lodel 2 - Document {{ id }}{% endblock %}
|
||||
{% block content %}
|
||||
{{ leapi.Section.get(['lodel_id = %s' % id]) }}
|
||||
{% endblock %}
|
||||
0
plugins/webui/templates/empty.html
Normal file
0
plugins/webui/templates/empty.html
Normal file
9
plugins/webui/templates/error.html
Normal file
9
plugins/webui/templates/error.html
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{status_code}} {{status_str}}</title>
|
||||
</head>
|
||||
<body>
|
||||
{{status_code}} {{status_str}}
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,3 +1,9 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Lodel 2 - DASHBOARD{% endblock %}
|
||||
{% block content %}DASHBOARD{% endblock %}
|
||||
{% block content %}
|
||||
DASHBOARD <br />
|
||||
{{ lodel.Test().ok() }}
|
||||
<ul>
|
||||
<li><a href="list_classes">Tous les types</a></li>
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
|
|
|||
16
plugins/webui/templates/listing/list_classes.html
Normal file
16
plugins/webui/templates/listing/list_classes.html
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Lodel 2 - List of Classes{% endblock %}
|
||||
{% block content %}
|
||||
<h1>Lodel 2 - List of Classes</h1>
|
||||
<ul>
|
||||
{% for classe in my_classes %}
|
||||
{% set abst = '' %}
|
||||
{% if classe.is_abstract() %}
|
||||
{% set abst = ' - Abstract class ' %}
|
||||
{% else %}
|
||||
{% set abst = ' - ' ~ classe.get(None)|length %}
|
||||
{% endif %}
|
||||
<li> <a href="show_class?name={{ classe.__name__ }}" target="_blank">{{ classe.__name__ }} </a>{{ abst }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
30
plugins/webui/templates/listing/show_class.html
Normal file
30
plugins/webui/templates/listing/show_class.html
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
{% extends "base.html" %}
|
||||
{% set my_classname = params['name'].pop() %}
|
||||
{% block title %}Lodel 2 - Class {{ my_classname }} {% endblock %}
|
||||
{% block content %}
|
||||
<h1>Lodel 2 - Class {{ my_classname }} </h1>
|
||||
{% set my_class = leapi.Object.name2class(my_classname) %}
|
||||
{% if my_class.child_classes()|length >0 %}
|
||||
<h2> Childs classes</h2>
|
||||
<ul>
|
||||
{% for child in my_class.child_classes() %}
|
||||
{% if child.is_abstract() %}
|
||||
{% set abst = ' - Abstract class ' %}
|
||||
{% else %}
|
||||
{% set abst = ' - ' ~ child.get(None)|length %}
|
||||
{% endif %}
|
||||
<li><a href="show_class?name={{ child.__name__ }}" target="_blank">{{ child.__name__ }}</a>{{ abst }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% if not my_class.is_abstract() %}
|
||||
{% set uid_f = my_class.uid_fieldname() %}
|
||||
{% set objects = my_class.get(None) %}
|
||||
<ul>
|
||||
{% for obj in objects %}
|
||||
<li><a href="show_object?classe={{ my_classname }}&id={{ obj.uid() }}" target="_blank">{{ obj.uid() }} </a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
21
plugins/webui/templates/listing/show_object.html
Normal file
21
plugins/webui/templates/listing/show_object.html
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{% extends "base.html" %}
|
||||
{% import 'components/components.html' as components %}
|
||||
{% set my_classname = params['classe'].pop() %}
|
||||
{% set my_id = params['id'].pop() %}
|
||||
{% set my_class = leapi.Object.name2class(my_classname) %}
|
||||
{% set objects = my_class.get(('%s = %s') % ('lodel_id', my_id)) %}
|
||||
{% set obj = objects.pop() %}
|
||||
{% block title %}Lodel 2 - Object {{ my_id }} {% endblock %}
|
||||
{% import "components/components.html" as components %}
|
||||
{% block content %}
|
||||
<h1>Lodel 2 - Object {{ my_id }} of the class {{ my_classname }}</h1>
|
||||
|
||||
<ul>
|
||||
<!-- To get a component HTML code, it is necessary to call : components.<macro_name>(args) -->
|
||||
{% for fieldname, fieldvalue in obj.datas().items() %}
|
||||
{% if fieldvalue is not none %}
|
||||
<li> {{ fieldname }} : {{ fieldvalue }} </li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
|
@ -1,6 +1,16 @@
|
|||
{% import "components/components.html" as components %}
|
||||
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
{{ components.textarea('test', value='ceci est un test', rows=10, cols=20) }}<br/>
|
||||
URL arg : id = {{ id }}<br />
|
||||
GET values :<br />
|
||||
<ul>
|
||||
{% for argument_name, argument_value in params.items() %}
|
||||
<li>{{argument_name}} = {{ argument_value }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<form action="http://localhost:9090/admin?r=1&rand[]=7&rand[]=5" method="POST" enctype="multipart/form-data">
|
||||
<input type="text" name="re[]" value="3"><br />
|
||||
<input type="text" name="re[]" value="1"><br />
|
||||
|
|
|
|||
|
|
@ -1,10 +1,18 @@
|
|||
#!/bin/bash
|
||||
|
||||
usage() {
|
||||
echo "Usage : $0 instance_name instance_dir [lodel_libdir]" 1>&2
|
||||
echo -e "Usage : $0 instance_name (instance_dir|-u) [lodel_libdir]" 1>&2
|
||||
echo -e "\n\tIf -u given as first argument update instance's loader.py" 1>&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cp_loader() {
|
||||
cp -Rv $libdir/install/loader.py $instdir/
|
||||
# Adding lib path to loader
|
||||
sed -i -E "s#^(LODEL2_LIB_ABS_PATH = )None#\1'$libdir'#" "$loader"
|
||||
}
|
||||
|
||||
|
||||
if [ $# -lt 2 ]
|
||||
then
|
||||
echo "Not enough arguments" 1>&2
|
||||
|
|
@ -12,6 +20,7 @@ then
|
|||
fi
|
||||
|
||||
|
||||
|
||||
name="$1"
|
||||
instdir="$2"
|
||||
|
||||
|
|
@ -21,6 +30,13 @@ libdir="${libdir:=$(realpath $(dirname $0)/..)}/"
|
|||
loader="$instdir/loader.py"
|
||||
conf="$instdir/conf.d/lodel2.ini"
|
||||
|
||||
if [ $1 = '-u' ]
|
||||
then
|
||||
#Update instance
|
||||
cp_loader
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -e "$instdir" ]
|
||||
then
|
||||
echo "Abording... "$instdir" exists" 1>&2
|
||||
|
|
@ -34,19 +50,15 @@ chmod 700 "$instdir/sessions"
|
|||
|
||||
#cp -Rv $libdir/install/* $instdir
|
||||
cp -Rv $libdir/install/conf.d $instdir/
|
||||
cp -Rv $libdir/install/loader.py $instdir/
|
||||
cp -Rv $libdir/examples/em_test.pickle $instdir/editorial_model.pickle
|
||||
ln -sv $libdir/install/Makefile $instdir/Makefile
|
||||
ln -sv $libdir/install/lodel_admin.py $instdir/lodel_admin.py
|
||||
ln -sv $libdir/plugins $instdir/plugins
|
||||
|
||||
|
||||
|
||||
# Adding lib path to loader
|
||||
sed -i -E "s#^(LODEL2_LIB_ABS_PATH = )None#\1'$libdir'#" "$loader"
|
||||
cp_loader
|
||||
# Adding instance name to conf
|
||||
sed -i -E "s#^sitename = noname#sitename = $name#" "$conf"
|
||||
|
||||
|
||||
echo -e "\nInstance successfully created in $instdir"
|
||||
echo -e "============================\n"
|
||||
echo "Now you should edit files in '${instdir}/conf.d/' and then run : cd $instdir && make dyncode"
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -71,7 +71,7 @@ class LeQueryDatasourceTestCase(unittest.TestCase):
|
|||
self.assertEqual(call_args[0], cls)
|
||||
self.assertEqual(
|
||||
sorted(call_args[1]),
|
||||
sorted([('lodel_id', '=', '1'), ('alias', '=', '2')]))
|
||||
sorted([('lodel_id', '=', 1), ('alias', '=', '2')]))
|
||||
self.assertEqual(call_args[2], [])
|
||||
self.check_nocall(read = False, exclude = ['delete'])
|
||||
self.check_nocall(read = True)
|
||||
|
|
@ -87,7 +87,7 @@ class LeQueryDatasourceTestCase(unittest.TestCase):
|
|||
query.execute()
|
||||
self.mockwrite.delete.assert_called_once_with(
|
||||
cls,
|
||||
[('lodel_id', '=', '1')],
|
||||
[('lodel_id', '=', 1)],
|
||||
[(('alias', {cls: 'firstname'}), '=', 'foo')])
|
||||
self.check_nocall(read = False, exclude = ['delete'])
|
||||
self.check_nocall(read = True)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import unittest
|
|||
import tests.loader_utils
|
||||
from tests.leapi.query.utils import dyncode_module as dyncode
|
||||
|
||||
from lodel.leapi.exceptions import LeApiDataCheckError
|
||||
from lodel.leapi.exceptions import *
|
||||
from lodel.leapi.query import LeDeleteQuery, LeUpdateQuery, LeGetQuery
|
||||
|
||||
class LeFilteredQueryTestCase(unittest.TestCase):
|
||||
|
|
@ -13,20 +13,20 @@ class LeFilteredQueryTestCase(unittest.TestCase):
|
|||
def test_filters(self):
|
||||
""" Testing FilteredQuery filters handling """
|
||||
test_datas = [ ( 'lodel_id = 42',
|
||||
( [('lodel_id','=','42')],
|
||||
( [('lodel_id','=',42)],
|
||||
[])),
|
||||
( 'lodel_id <= 42',
|
||||
( [('lodel_id','<=','42')],
|
||||
( [('lodel_id','<=',42)],
|
||||
[])),
|
||||
( ['lodel_id <= 42'],
|
||||
( [('lodel_id','<=','42')],
|
||||
( [('lodel_id','<=',42)],
|
||||
[])),
|
||||
( ('lodel_id <= 42',),
|
||||
( [('lodel_id','<=','42')],
|
||||
( [('lodel_id','<=',42)],
|
||||
[])),
|
||||
( ['lodel_id <= 42','lodel_id >= 33'],
|
||||
( [ ('lodel_id','<=','42'),
|
||||
('lodel_id', '>=','33')],
|
||||
( [ ('lodel_id','<=',42),
|
||||
('lodel_id', '>=',33)],
|
||||
[])),
|
||||
]
|
||||
for q_class in self.q_classes:
|
||||
|
|
@ -53,7 +53,7 @@ class LeFilteredQueryTestCase(unittest.TestCase):
|
|||
)
|
||||
for invalid_filter in invalid_filters:
|
||||
for q_class in self.q_classes:
|
||||
with self.assertRaises( LeApiDataCheckError,
|
||||
with self.assertRaises( LeApiDataCheckErrors,
|
||||
msg="for filter '%s'" % (invalid_filter,)):
|
||||
q_class(dyncode.Publication, invalid_filter)
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ class LeFilteredQueryTestCase(unittest.TestCase):
|
|||
'not in',
|
||||
'like',
|
||||
'not like']
|
||||
values = ( '42',
|
||||
values = ( 42,
|
||||
'not in',
|
||||
'in',
|
||||
'like',
|
||||
|
|
|
|||
|
|
@ -259,7 +259,7 @@ class LeObjectQueryMockTestCase(unittest.TestCase):
|
|||
mock_init.assert_called_once_with(
|
||||
dyncode.Person,
|
||||
query_filters = ['lodel_id = 1'],
|
||||
field_list = dyncode.Person.fieldnames(True),
|
||||
field_list = None,
|
||||
order = None, group = None, limit = None, offset = 0)
|
||||
|
||||
with patch.object(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue