From 00614d90e97473e7e2e6085d2733698bfbc823cd Mon Sep 17 00:00:00 2001 From: Yann Date: Fri, 19 Jun 2015 12:26:46 +0200 Subject: [PATCH] New way to specify _fields and table in EmComponent's child classes Now _fields and table MUST be class attribute. Its important to note that _fields now consist in a list of (name, ftype) tuples and that ftype is a EmFieldType class and NOT an EmFieldType instance --- Doxyfile | 2 +- EditorialModel/classes.py | 64 ++++++++++++++------------- EditorialModel/components.py | 45 ++++++++++++++++--- EditorialModel/fieldgroups.py | 6 +-- EditorialModel/test/test_component.py | 10 ++--- 5 files changed, 80 insertions(+), 47 deletions(-) diff --git a/Doxyfile b/Doxyfile index 138cbc7..82a9457 100644 --- a/Doxyfile +++ b/Doxyfile @@ -425,7 +425,7 @@ EXTRACT_ALL = YES # be included in the documentation. # The default value is: NO. -EXTRACT_PRIVATE = NO +EXTRACT_PRIVATE = YES # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. diff --git a/EditorialModel/classes.py b/EditorialModel/classes.py index 0495ea2..1ff4edc 100644 --- a/EditorialModel/classes.py +++ b/EditorialModel/classes.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- -""" Manipulate Classes of the Editorial Model - Create classes of object - @see EmClass, EmType, EmFieldGroup, EmField -""" +## @file classes.py +# @see EditorialModel::classes::EmClass import logging as logger @@ -14,18 +12,24 @@ import sqlalchemy as sql import EditorialModel.fieldtypes as ftypes import EditorialModel +## @brief Manipulate Classes of the Editorial Model +# Create classes of object. +#@see EmClass, EmType, EmFieldGroup, EmField class EmClass(EmComponent): table = 'em_class' + + ## @brief Specific EmClass fields + # @see EditorialModel::components::EmComponent::_fields + _fields = [ + ('classtype', ftypes.EmField_char), + ('icon', ftypes.EmField_integer), + ('sortcolumn', ftypes.EmField_char) + ] - def __init__(self, id_or_name): - self.table = EmClass.table - self._fields = [('classtype', ftypes.EmField_char()), ('icon', ftypes.EmField_integer()), ('sortcolumn', ftypes.EmField_char())] - super(EmClass, self).__init__(id_or_name) - - """ create a new class - @param name str: name of the new class - @param class_type EmClasstype: type of the class - """ + ## Create a new class + # @param name str: name of the new class + # @param class_type EmClasstype: type of the class + # @return An EmClass instance @classmethod def create(c, name, class_type): try: @@ -38,8 +42,9 @@ class EmClass(EmComponent): return res @classmethod - ## Isolate SQL for create + ## Isolate SQL for EmClass::create # @todo Remove hardcoded default value for icon + # @return An instance of EmClass def _createDb(c, name, class_type): """ Do the db querys for EmClass::create() """ @@ -62,15 +67,16 @@ class EmClass(EmComponent): return resclass - """ retrieve list of the field_groups of this class - @return field_groups [EmFieldGroup]: - """ + ## Retrieve list of the field_groups of this class + # @return field_groups [EmFieldGroup]: def fieldgroups(self): records = self._fieldgroupsDb() fieldgroups = [ EditorialModel.fieldgroups.EmFieldGroup(int(record.uid)) for record in records ] return fieldgroups + ## Isolate SQL for EmClass::fieldgroups + # @return An array of dict (sqlalchemy fetchall) def _fieldgroupsDb(self): dbe = self.__class__.getDbE() emfg = sql.Table(EditorialModel.fieldgroups.EmFieldGroup.table, sqlutils.meta(dbe)) @@ -81,21 +87,21 @@ class EmClass(EmComponent): return res.fetchall() - """ retrieve list of fields - @return fields [EmField]: - """ + ## Retrieve list of fields + # @return fields [EmField]: def fields(self): pass - """ retrieve list of type of this class - @return types [EmType]: - """ + ## Retrieve list of type of this class + # @return types [EmType]: def types(self): records = self._typesDb() types = [ EditorialModel.types.EmType(int(record.uid)) for record in records ] return types + ## Isolate SQL for EmCLass::types + # @return An array of dict (sqlalchemy fetchall) def _typesDb(self): dbe = self.__class__.getDbE() emtype = sql.Table(EditorialModel.types.EmType.table, sqlutils.meta(dbe)) @@ -104,15 +110,13 @@ class EmClass(EmComponent): res = conn.execute(req) return res.fetchall() - """ add a new EmType that can ben linked to this class - @param t EmType: type to link - @return success bool: done or not - """ + ## Add a new EmType that can ben linked to this class + # @param t EmType: type to link + # @return success bool: done or not def link_type(self, t): pass - """ retrieve list of EmType that are linked to this class - @return types [EmType]: - """ + ## Retrieve list of EmType that are linked to this class + # @return types [EmType]: def linked_types(self): pass diff --git a/EditorialModel/components.py b/EditorialModel/components.py index bedd4b4..a85830c 100644 --- a/EditorialModel/components.py +++ b/EditorialModel/components.py @@ -30,8 +30,24 @@ class EmComponent(object): ## Used by EmComponent::modify_rank ranked_in = None - ##Read only properties - _ro_properties = [ 'date_update', 'date_create', 'uid', 'rank'] + ## Read only properties + _ro_properties = [ 'date_update', 'date_create', 'uid', 'rank', 'deleted'] + + ## @brief List fields name and fieldtype + # + # This is a list that describe database fields common for each EmComponent child classes. + # A database field is defined here by a tuple(name, type) with name a string and type an EditorialModel.fieldtypes.EmFieldType + # @warning The EmFieldType in second position in the tuples must be a class type and not a class instance !!! + # @see EditorialModel::classes::EmClass::_fields EditorialModel::fieldgroups::EmFieldGroup::_fields EditorialModel::types::EmType::_fields EditorialModel::fields::EmField::_fields + _fields = [ + ('uid', ftypes.EmField_integer), + ('name', ftypes.EmField_char), + ('rank', ftypes.EmField_integer), + ('date_update', ftypes.EmField_date), + ('date_create', ftypes.EmField_date), + ('string', ftypes.EmField_mlstring), + ('help', ftypes.EmField_mlstring) + ] ## Instaciate an EmComponent # @param id_or_name int|str: name or id of the object @@ -42,8 +58,11 @@ class EmComponent(object): if type(self) is EmComponent: raise NotImplementedError('Abstract class') - # data fields of the object - self._fields = OrderedDict([('uid', ftypes.EmField_integer()), ('name', ftypes.EmField_char()), ('rank', ftypes.EmField_integer()), ('date_update', ftypes.EmField_date()), ('date_create', ftypes.EmField_date()), ('string', ftypes.EmField_mlstring()), ('help', ftypes.EmField_mlstring())] + self._fields) + ## @brief An OrderedDict storing fields name and values + # Values are handled by EditorialModel::fieldtypes::EmFieldType + # @warning \ref _fields instance property is not the same than EmComponent::_fields class property. In the instance property the EditorialModel::fieldtypes::EmFieldType are instanciated to be able to handle datas + # @see EmComponent::_fields EditorialModel::fieldtypes::EmFieldType + self._fields = OrderedDict([ (name, ftype()) for (name,ftype) in (EmComponent._fields + self.__class__._fields) ] ) # populate if isinstance(id_or_name, int): @@ -52,6 +71,7 @@ class EmComponent(object): self.name = id_or_name else: raise TypeError('Bad argument: expecting or but got : '+str(type(id_or_name))) + self.table = self.__class__.table self.populate() ## Access values of data fields from the object properties @@ -60,8 +80,17 @@ class EmComponent(object): def __getattr__(self, name): if name != '_fields' and name in self._fields: return self._fields[name].value + else: + return super(EmComponent, self).__getattribute__(name) - raise AttributeError('Error unknown attribute : '+name) + #raise AttributeError('Error unknown attribute : '+name) + + def __getattribute__(self, name): + if super(EmComponent, self).__getattribute__('deleted'): + #raise EmComponentNotExistError("This component has been deleted") + raise EmComponentNotExistError("This "+super(EmComponent, self).__getattribute__('__class__').__name__+" has been deleted") + res = super(EmComponent, self).__getattribute(name) + return res ## Set values of data fields from the object properties # @param name str: The propertie name @@ -85,6 +114,8 @@ class EmComponent(object): if keys in record: self._fields[keys].from_string(record[keys]) + super(EmComponent, self).__setattr__('deleted', False) + @classmethod ## Shortcut that return the sqlAlchemy engine def getDbE(c): @@ -108,7 +139,7 @@ class EmComponent(object): c.close() if not res or len(res) == 0: - raise EmComponentNotExistError("No component found with "+('name ' + self.name if self.uid == None else 'uid ' + str(self.uid) )) + raise EmComponentNotExistError("No "+self.__class__.__name__+" found with "+('name ' + self.name if self.uid == None else 'uid ' + str(self.uid) )) return res @@ -198,6 +229,8 @@ class EmComponent(object): c.close if not res: raise RuntimeError("Unable to delete the component in the database") + + super(EmComponent, self).__setattr__('deleted', True) # pass diff --git a/EditorialModel/fieldgroups.py b/EditorialModel/fieldgroups.py index 63ff622..52ca25c 100644 --- a/EditorialModel/fieldgroups.py +++ b/EditorialModel/fieldgroups.py @@ -19,17 +19,13 @@ class EmFieldGroup(EmComponent): table = 'em_fieldgroup' ## List of fields # @todo Bad storage, here we want an ordereddict not a tuple list - _fields = [('class_id', ftypes.EmField_integer())] + _fields = [('class_id', ftypes.EmField_integer)] ## Instanciate an EmFieldGroup with data fetched from db # @param id_or_name str|int: Identify the EmFieldGroup by name or by global_id # @throw TypeError # @see EditorialModel::components::EmComponent::__init__() # @throw EditorialModel::components::EmComponentNotExistError - def __init__(self, id_or_name): - self.table = EmFieldGroup.table - self._fields = self.__class__._fields - super(EmFieldGroup, self).__init__(id_or_name) @classmethod ## Create a new EmFieldGroup diff --git a/EditorialModel/test/test_component.py b/EditorialModel/test/test_component.py index d9ca0ed..0e782dd 100644 --- a/EditorialModel/test/test_component.py +++ b/EditorialModel/test/test_component.py @@ -115,10 +115,7 @@ def tearDownModule(): class EmTestComp(EmComponent): table = 'ttest' ranked_in = 'rank_fam' - def __init__(self, ion): - self._fields = [('rank_fam', ftypes.EmField_char())] - super(EmTestComp, self).__init__(ion) - pass + _fields = [('rank_fam', ftypes.EmField_char)] # The parent class of all other test cases for component # It defines a SetUp function and some utility functions for EmComponent tests @@ -431,6 +428,9 @@ class TestCreate(ComponentTestCase): pass +#====================# +# EmComponent.delete # +#====================# class TestDelete(ComponentTestCase): def test_delete(self): @@ -466,7 +466,7 @@ class TestDelete(ComponentTestCase): for j in range(i+1,len(vals)): try: - tc = EmTestComp(tcv['name']) + tc = EmTestComp(vals[j]['name']) except EmComponentNotExistError: self.fail('EmComponent should not be deleted') pass