No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

lefactory.py 8.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. #-*- coding: utf-8 -*-
  2. import importlib
  3. import EditorialModel
  4. from EditorialModel.model import Model
  5. from EditorialModel.fieldtypes.generic import GenericFieldType
  6. ## @brief This class is designed to generated the leobject API given an EditorialModel.model
  7. # @note Contains only static methods
  8. #
  9. # The name is not good but i've no other ideas for the moment
  10. class LeFactory(object):
  11. output_file = 'dyn.py'
  12. modname = None
  13. def __init__(self):
  14. raise NotImplementedError("Not designed (yet?) to be implemented")
  15. ## @brief Return a LeObject child class given its name
  16. # @return a python class or False
  17. @staticmethod
  18. def leobj_from_name(name):
  19. if LeFactory.modname is None:
  20. modname = 'leobject.' + LeFactory.output_file.split('.')[1]
  21. else:
  22. modname = LeFactory.modname
  23. mod = importlib.import_module(modname)
  24. try:
  25. res = getattr(mod, name)
  26. except AttributeError:
  27. return False
  28. return res
  29. @classmethod
  30. def leobject(cls):
  31. return cls.leobj_from_name('LeObject')
  32. ## @brief Convert an EmType or EmClass name in a python class name
  33. # @param name str : The name
  34. # @return name.title()
  35. @staticmethod
  36. def name2classname(name):
  37. if not isinstance(name, str):
  38. raise AttributeError("Argument name should be a str and not a %s" % type(name))
  39. return name.title()
  40. ## @brief Return a call to a FieldType constructor given an EmField
  41. # @param emfield EmField : An EmField
  42. # @return a string representing the python code to instanciate a EmFieldType
  43. @staticmethod
  44. def fieldtype_construct_from_field(emfield):
  45. return '%s.EmFieldType(**%s)' % (
  46. GenericFieldType.module_name(emfield.fieldtype),
  47. repr(emfield._fieldtype_args),
  48. )
  49. ## @brief Given a Model and an EmClass instances generate python code for corresponding LeClass
  50. # @param model Model : A Model instance
  51. # @param emclass EmClass : An EmClass instance from model
  52. # @return A string representing the python code for the corresponding LeClass child class
  53. @staticmethod
  54. def emclass_pycode(model, emclass):
  55. cls_fields = dict()
  56. cls_linked_types = dict() #keys are LeType classnames and values are tuples (attr_fieldname, attr_fieldtype)
  57. #Populating linked_type attr
  58. for rfield in [ f for f in emclass.fields() if f.fieldtype == 'rel2type']:
  59. fti = rfield.fieldtype_instance()
  60. cls_linked_types[LeFactory.name2classname(model.component(fti.rel_to_type_id).name)] = [
  61. (f.name, LeFactory.fieldtype_construct_from_field(f)) for f in model.components('EmField') if f.rel_field_id == rfield.uid
  62. ]
  63. # Populating fieldtype attr
  64. for field in emclass.fields(relational = False):
  65. cls_fields[field.name] = LeFactory.fieldtype_construct_from_field(field)
  66. fti = field.fieldtype_instance()
  67. # Populating fieldgroup attr
  68. cls_fieldgroup = dict()
  69. for fieldgroup in emclass.fieldgroups():
  70. cls_fieldgroup[fieldgroup.name] = list()
  71. for field in fieldgroup.fields(relational = False):
  72. cls_fieldgroup[fieldgroup.name].append(field.name)
  73. return """
  74. #Initialisation of {name} class attributes
  75. {name}._fieldtypes = {ftypes}
  76. {name}._linked_types = {ltypes}
  77. {name}._fieldgroups = {fgroups}
  78. {name}._classtype = {classtype}
  79. """.format(
  80. name = LeFactory.name2classname(emclass.name),
  81. ftypes = "{" + (','.join(['\n\t%s:%s' % (repr(f), v) for f, v in cls_fields.items()])) + "\n}",
  82. ltypes = '{'+ (','.join(
  83. [
  84. '\n\t{ltname}:{ltattr_list}'.format(
  85. ltname = lt,
  86. ltattr_list = '['+(','.join([
  87. '(%s,%s)'%(repr(ltname), ltftype) for ltname, ltftype in ltattr
  88. ]))+']'
  89. ) for lt, ltattr in cls_linked_types.items()
  90. ]))+'}',
  91. fgroups = repr(cls_fieldgroup),
  92. classtype = repr(emclass.classtype)
  93. )
  94. ## @brief Given a Model and an EmType instances generate python code for corresponding LeType
  95. # @param model Model : A Model instance
  96. # @param emtype EmType : An EmType instance from model
  97. # @return A string representing the python code for the corresponding LeType child class
  98. @staticmethod
  99. def emtype_pycode(model, emtype):
  100. type_fields = list()
  101. type_superiors = list()
  102. for field in emtype.fields(relational=False):
  103. type_fields.append(field.name)
  104. for nat, sup_l in emtype.superiors().items():
  105. type_superiors.append('%s:[%s]' % (
  106. repr(nat),
  107. ','.join([LeFactory.name2classname(sup.name) for sup in sup_l])
  108. ))
  109. return """
  110. #Initialisation of {name} class attributes
  111. {name}._fields = {fields}
  112. {name}._superiors = {dsups}
  113. {name}._leclass = {leclass}
  114. """.format(
  115. name=LeFactory.name2classname(emtype.name),
  116. fields=repr(type_fields),
  117. dsups='{' + (','.join(type_superiors)) + '}',
  118. leclass=LeFactory.name2classname(emtype.em_class.name)
  119. )
  120. ## @brief Generate python code containing the LeObject API
  121. # @param backend_cls Backend : A model backend class
  122. # @param backend_args dict : A dict representing arguments for backend_cls instanciation
  123. # @param datasource_cls Datasource : A datasource class
  124. # @param datasource_args dict : A dict representing arguments for datasource_cls instanciation
  125. # @return A string representing python code
  126. @staticmethod
  127. def generate_python(backend_cls, backend_args, datasource_cls, datasource_args):
  128. model = Model(backend=backend_cls(**backend_args))
  129. result = ""
  130. #result += "#-*- coding: utf-8 -*-\n"
  131. #Putting import directives in result
  132. result += """
  133. from EditorialModel.model import Model
  134. from leobject.leobject import _LeObject
  135. from leobject.leclass import LeClass
  136. from leobject.letype import LeType
  137. import EditorialModel.fieldtypes
  138. """
  139. result += """
  140. import %s
  141. import %s
  142. """ % (backend_cls.__module__, datasource_cls.__module__)
  143. #Generating the code for LeObject class
  144. backend_constructor = '%s.%s(**%s)' % (backend_cls.__module__, backend_cls.__name__, repr(backend_args))
  145. leobj_me_uid = dict()
  146. for comp in model.components('EmType') + model.components('EmClass'):
  147. leobj_me_uid[comp.uid] = LeFactory.name2classname(comp.name)
  148. result += """
  149. ## @brief _LeObject concret clas
  150. # @see leobject::leobject::_LeObject
  151. class LeObject(_LeObject):
  152. _model = Model(backend=%s)
  153. _datasource = %s(**%s)
  154. _me_uid = %s
  155. """ % (backend_constructor, datasource_cls.__module__ + '.' + datasource_cls.__name__, repr(datasource_args), repr(leobj_me_uid))
  156. emclass_l = model.components(EditorialModel.classes.EmClass)
  157. emtype_l = model.components(EditorialModel.types.EmType)
  158. #LeClass child classes definition
  159. for emclass in emclass_l:
  160. result += """
  161. ## @brief EmClass {name} LeClass child class
  162. # @see leobject::leclass::LeClass
  163. class {name}(LeObject,LeClass):
  164. _class_id = {uid}
  165. """.format(
  166. name=LeFactory.name2classname(emclass.name),
  167. uid=emclass.uid
  168. )
  169. #LeType child classes definition
  170. for emtype in emtype_l:
  171. result += """
  172. ## @brief EmType {name} LeType child class
  173. # @see leobject::letype::LeType
  174. class {name}(LeType, {leclass}):
  175. _type_id = {uid}
  176. """.format(
  177. name=LeFactory.name2classname(emtype.name),
  178. leclass=LeFactory.name2classname(emtype.em_class.name),
  179. uid=emtype.uid
  180. )
  181. #Set attributes of created LeClass and LeType child classes
  182. for emclass in emclass_l:
  183. result += LeFactory.emclass_pycode(model, emclass)
  184. for emtype in emtype_l:
  185. result += LeFactory.emtype_pycode(model, emtype)
  186. #Populating LeObject._me_uid dict for a rapid fetch of LeType and LeClass given an EM uid
  187. me_uid = {comp.uid: LeFactory.name2classname(comp.name) for comp in emclass_l + emtype_l}
  188. result += """
  189. ## @brief Dict for getting LeClass and LeType child classes given an EM uid
  190. LeObject._me_uid = %s
  191. """ % "{" + (','.join(['%s:%s' % (k, v) for k, v in me_uid.items()])) + "}"
  192. return result