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.

components.py 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. # -*- coding: utf-8 -*-
  2. ## Main object to manipulate Editorial Model
  3. # parent of all other EM editing classes
  4. # @see EmClass, EmType, EmFieldGroup, EmField
  5. import datetime
  6. from Lodel.utils.mlstring import MlString
  7. import logging
  8. import sqlalchemy as sql
  9. from Database import sqlutils
  10. import EditorialModel.fieldtypes as ftypes
  11. from collections import OrderedDict
  12. logger = logging.getLogger('Lodel2.EditorialModel')
  13. class EmComponent(object):
  14. dbconf = 'default' #the name of the engine configuration
  15. table = None
  16. ranked_in = None
  17. """ instaciate an EmComponent
  18. @param id_or_name int|str: name or id of the object
  19. @exception TypeError
  20. """
  21. def __init__(self, id_or_name):
  22. if type(self) is EmComponent:
  23. raise EnvironmentError('Abstract class')
  24. # data fields of the object
  25. 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)
  26. # populate
  27. if isinstance(id_or_name, int):
  28. self.uid = id_or_name
  29. self.name = None
  30. elif isinstance(id_or_name, str):
  31. self.uid = None
  32. self.name = id_or_name
  33. else:
  34. raise TypeError('Bad argument: expecting <int> or <str> but got : '+str(type(id_or_name)))
  35. self.populate()
  36. # access values of data fields from the object properties
  37. def __getattr__(self, name):
  38. if name != '_fields' and name in self._fields:
  39. return self._fields[name].value
  40. raise AttributeError('Error unknown attribute : '+name)
  41. # set values of data fields from the object properties
  42. def __setattr__(self, name, value):
  43. if name != '_fields' and hasattr(self, '_fields') and name in object.__getattribute__(self, '_fields'):
  44. self._fields[name].from_python(value)
  45. else:
  46. object.__setattr__(self, name, value)
  47. """ Lookup in the database properties of the object to populate the properties
  48. """
  49. def populate(self):
  50. records = self._populateDb() #Db query
  51. for record in records:
  52. for keys in self._fields.keys():
  53. if keys in record:
  54. self._fields[keys].from_string(record[keys])
  55. @classmethod
  56. def getDbE(c):
  57. """ Shortcut that return the sqlAlchemy engine """
  58. return sqlutils.getEngine(c.dbconf)
  59. def _populateDb(self):
  60. """ Do the query on the db """
  61. dbe = self.__class__.getDbE()
  62. component = sql.Table(self.table, sqlutils.meta(dbe))
  63. req = sql.sql.select([component])
  64. if self.uid == None:
  65. req = req.where(component.c.name == self.name)
  66. else:
  67. req = req.where(component.c.uid == self.uid)
  68. c = dbe.connect()
  69. res = c.execute(req)
  70. res = res.fetchall()
  71. c.close()
  72. if not res or len(res) == 0:
  73. raise EmComponentNotExistError("No component found with "+('name ' + self.name if self.uid == None else 'uid ' + str(self.uid) ))
  74. return res
  75. ## Insert a new component in the database
  76. # This function create and assign a new UID and handle the date_create value
  77. # @param values The values of the new component
  78. # @return An instance of the created component
  79. #
  80. # @todo Check that the query didn't failed
  81. # @todo Check that every mandatory _fields are given in args
  82. # @todo Put a real rank at creation
  83. @classmethod
  84. def create(cl, **kwargs):
  85. for argname in kwargs:
  86. if argname in ['date_update', 'date_create', 'rank', 'uid']: #Automatic properties
  87. raise TypeError("Invalid argument : "+argname)
  88. #TODO check that every mandatory _fields are here like below for example
  89. #for name in cl._fields:
  90. # if cl._fields[name].notNull and cl._fields[name].default == None:
  91. # raise TypeError("Missing argument : "+name)
  92. kwargs['uid'] = cl.newUid()
  93. kwargs['date_update'] = kwargs['date_create'] = datetime.datetime.utcnow()
  94. dbe = cl.getDbE()
  95. conn = dbe.connect()
  96. kwargs['rank'] = -1 #Warning !!!
  97. table = sql.Table(cl.table, sqlutils.meta(dbe))
  98. req = table.insert(kwargs)
  99. res = conn.execute(req) #Check res?
  100. conn.close()
  101. return cl(kwargs['name']) #Maybe no need to check res because this would fail if the query failed
  102. """ write the representation of the component in the database
  103. @return bool
  104. """
  105. def save(self):
  106. values = {}
  107. for name, field in self._fields.items():
  108. values[name] = field.to_sql()
  109. #Don't allow creation date overwritting
  110. if 'date_create' in values:
  111. del values['date_create']
  112. logger.warning("date_create supplied for save, but overwritting of date_create not allowed, the date will not be changed")
  113. self._saveDb(values)
  114. def _saveDb(self, values):
  115. """ Do the query on the db """
  116. dbe = self.__class__.getDbE()
  117. component = sql.Table(self.table, sqlutils.meta(dbe))
  118. req = sql.update(component, values = values).where(component.c.uid == self.uid)
  119. c = dbe.connect()
  120. res = c.execute(req)
  121. c.close()
  122. if not res:
  123. raise RuntimeError("Unable to save the component in the database")
  124. """ delete this component data in the database
  125. @return bool
  126. """
  127. def delete(self):
  128. #<SQL>
  129. dbe = self.__class__.getDbE()
  130. component = sql.Table(self.table, sqlutils.meta(dbe))
  131. req= component.delete().where(component.c.uid == self.uid)
  132. c = dbe.connect()
  133. res = c.execute(req)
  134. c.close
  135. if not res:
  136. raise RuntimeError("Unable to delete the component in the database")
  137. #</SQL>
  138. pass
  139. ## modify_rank
  140. #
  141. # Permet de changer le rank d'un component, soit en lui donnant un rank précis, soit en augmentant ou reduisant sont rank actuelle d'une valleur donné.
  142. #
  143. # @param new_rank int: le rank ou modificateur de rank
  144. # @param sign str: Un charactère qui peut être : '=' pour afecter un rank, '+' pour ajouter le modificateur de rank ou '-' pour soustraire le modificateur de rank.
  145. #
  146. # @return bool: True en cas de réussite False en cas d'echec.
  147. def modify_rank(self, new_rank, sign = '='):
  148. if(type(new_rank) is int):
  149. if(new_rank >= 0):
  150. dbe = self.__class__.getDbE()
  151. component = sql.Table(self.table, sqlutils.meta(dbe))
  152. req = sql.sql.select([component.c.uid, component.c.rank])
  153. if(type(sign) is not str):
  154. logger.error("Bad argument")
  155. raise TypeError('Excepted a string (\'=\' or \'+\' or \'-\') not a '+str(type(sign)))
  156. if(sign == '='):
  157. req = sql.sql.select([component.c.uid, component.c.rank])
  158. req = req.where((getattr(component.c, self.ranked_in) == getattr(self, self.ranked_in)) & (component.c.rank == new_rank))
  159. c = dbe.connect()
  160. res = c.execute(req)
  161. res = res.fetchone()
  162. c.close()
  163. if(res != None):
  164. if(new_rank < self.rank):
  165. req = req.where((getattr(component.c, self.ranked_in) == getattr(self, self.ranked_in)) & ( component.c.rank >= new_rank) & (component.c.rank < self.rank))
  166. else:
  167. req = req.where((getattr(component.c, self.ranked_in) == getattr(self, self.ranked_in)) & (component.c.rank <= new_rank) & (component.c.rank > self.rank))
  168. c = dbe.connect()
  169. res = c.execute(req)
  170. res = res.fetchall()
  171. vals = list()
  172. vals.append({'id' : self.uid, 'rank' : new_rank})
  173. for row in res:
  174. if(new_rank < self.rank):
  175. vals.append({'id' : row.uid, 'rank' : row.rank+1})
  176. else:
  177. vals.append({'id' : row.uid, 'rank' : row.rank-1})
  178. req = component.update().where(component.c.uid == sql.bindparam('id')).values(rank = sql.bindparam('rank'))
  179. c.execute(req, vals)
  180. c.close()
  181. self.rank = new_rank
  182. else:
  183. logger.error("Bad argument")
  184. raise ValueError('new_rank to big, new_rank - 1 doesn\'t exist. new_rank = '+str((new_rank)))
  185. elif(sign == '+'):
  186. req = sql.sql.select([component.c.uid, component.c.rank])
  187. req = req.where((getattr(component.c, self.ranked_in) == getattr(self, self.ranked_in)) & (component.c.rank == self.rank + new_rank))
  188. c = dbe.connect()
  189. res = c.execute(req)
  190. res = res.fetchone()
  191. c.close()
  192. if(res != None):
  193. if(new_rank != 0):
  194. req = req.where((getattr(component.c, self.ranked_in) == getattr(self, self.ranked_in)) & (component.c.rank <= self.rank + new_rank) & (component.c.rank > self.rank))
  195. c = dbe.connect()
  196. res = c.execute(req)
  197. res = res.fetchall()
  198. vals = list()
  199. vals.append({'id' : self.uid, 'rank' : self.rank + new_rank})
  200. for row in res:
  201. vals.append({'id' : row.uid, 'rank' : row.rank - 1})
  202. req = component.update().where(component.c.uid == sql.bindparam('id')).values(rank = sql.bindparam('rank'))
  203. c.execute(req, vals)
  204. c.close()
  205. self.rank += new_rank
  206. else:
  207. logger.error("Bad argument")
  208. raise ValueError('Excepted a positive int not a null. new_rank = '+str((new_rank)))
  209. else:
  210. logger.error("Bad argument")
  211. raise ValueError('new_rank to big, rank + new rank doesn\'t exist. new_rank = '+str((new_rank)))
  212. elif(sign == '-'):
  213. if((self.rank + new_rank) > 0):
  214. if(new_rank != 0):
  215. req = req.where((getattr(component.c, self.ranked_in) == getattr(self, self.ranked_in)) & (component.c.rank >= self.rank - new_rank) & (component.c.rank < self.rank))
  216. c = dbe.connect()
  217. res = c.execute(req)
  218. res = res.fetchall()
  219. vals = list()
  220. vals.append({'id' : self.uid, 'rank' : self.rank - new_rank})
  221. for row in res:
  222. vals.append({'id' : row.uid, 'rank' : row.rank + 1})
  223. req = component.update().where(component.c.uid == sql.bindparam('id')).values(rank = sql.bindparam('rank'))
  224. c.execute(req, vals)
  225. c.close()
  226. self.rank -= new_rank
  227. else:
  228. logger.error("Bad argument")
  229. raise ValueError('Excepted a positive int not a null. new_rank = '+str((new_rank)))
  230. else:
  231. logger.error("Bad argument")
  232. raise ValueError('new_rank to big, rank - new rank is negative. new_rank = '+str((new_rank)))
  233. else:
  234. logger.error("Bad argument")
  235. raise ValueError('Excepted a string (\'=\' or \'+\' or \'-\') not a '+str((sign)))
  236. else:
  237. logger.error("Bad argument")
  238. raise ValueError('Excepted a positive int not a negative. new_rank = '+str((new_rank)))
  239. else:
  240. logger.error("Bad argument")
  241. raise TypeError('Excepted a int not a '+str(type(new_rank)))
  242. def __repr__(self):
  243. if self.name is None:
  244. return "<%s #%s, 'non populated'>" % (type(self).__name__, self.uid)
  245. else:
  246. return "<%s #%s, '%s'>" % (type(self).__name__, self.uid, self.name)
  247. @classmethod
  248. def newUid(c):
  249. """ This function register a new component in uids table
  250. @return The new uid
  251. """
  252. dbe = c.getDbE()
  253. uidtable = sql.Table('uids', sqlutils.meta(dbe))
  254. conn = dbe.connect()
  255. req = uidtable.insert(values={'table':c.table})
  256. res = conn.execute(req)
  257. uid = res.inserted_primary_key[0]
  258. logger.debug("Registering a new UID '"+str(uid)+"' for '"+c.table+"' component")
  259. conn.close()
  260. return uid
  261. class EmComponentNotExistError(Exception):
  262. pass