PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/site/remote/models.py

https://bitbucket.org/davka003/automagically
Python | 326 lines | 315 code | 10 blank | 1 comment | 10 complexity | 6956378814d31ae0ae9b86a1e6b7a91b MD5 | raw file
  1. from django.db import models
  2. from django.http import Http404, HttpResponse, HttpResponseNotFound
  3. from django.core.exceptions import ValidationError
  4. import traceback
  5. import sys
  6. import renderbasic
  7. import renderjquery
  8. import renderxml
  9. debug = False
  10. preRenderedCache = {}
  11. class Theme(models.Model):
  12. name = models.CharField(max_length=15)
  13. maxX = models.PositiveIntegerField(default = 5)
  14. maxY = models.PositiveIntegerField(default = 0)
  15. def __unicode__(self):
  16. return self.name
  17. def renderpage(self, remote, baseUrl, staticBaseUrl):
  18. """ Should return a dictionary with keys as integer page numbers and values should be objects of type HttpResponse."""
  19. if self.name == 'Basic':
  20. return renderbasic.render(remote, baseUrl, staticBaseUrl)
  21. elif self.name == 'jQuery':
  22. return renderjquery.render(remote, baseUrl, staticBaseUrl)
  23. elif self.name == 'xml':
  24. return renderxml.render(remote, baseUrl, staticBaseUrl)
  25. else:
  26. return {0: HttpResponse('<html><head><title>A page</title></head><body>A page body</body></html>')}
  27. class Remote(models.Model):
  28. name = models.CharField(max_length=50)
  29. theme = models.ForeignKey(Theme)
  30. theme_config = models.CharField(max_length=255, default = '', blank = True)
  31. style = models.TextField(default = '', blank = True, help_text = "Custom styleing for the theme, in jQuery theme this should be a chunk of CSS without any style-tag.")
  32. offlineApp = models.BooleanField(default = True, help_text = "If the remote should work as an HTML5 offline application, reduces page load time especially on mobile devices.")
  33. def __unicode__(self):
  34. return self.name
  35. def render(self, baseUrl, staticBaseUrl):
  36. return self.theme.renderpage(self, baseUrl, staticBaseUrl)
  37. def save(self):
  38. self.style = self.style.replace('url(images', 'url(/static/automagically/jquery/images')
  39. super(Remote, self).save()
  40. render(self)
  41. class Page(models.Model):
  42. name = models.CharField(max_length=25)
  43. number = models.PositiveIntegerField(default = 0)
  44. config = models.CharField(max_length=255, default = '', blank = True, help_text = "Additional configuration information here.")
  45. remote = models.ForeignKey(Remote)
  46. def save(self):
  47. super(Page, self).save()
  48. if debug:
  49. print 'Render, page changed'
  50. render(self.remote)
  51. def remoteText(self):
  52. return self.remote.name
  53. def __unicode__(self):
  54. return self.name
  55. class Widget(models.Model):
  56. displayText = models.CharField(max_length = 25, default = '', blank = True)
  57. page = models.ForeignKey(Page)
  58. x = models.PositiveIntegerField(default = 0)
  59. y = models.PositiveIntegerField(default = 0)
  60. xSize = models.PositiveIntegerField(default = 0)
  61. ySize = models.PositiveIntegerField(default = 0)
  62. def save(self):
  63. super(Widget, self).save()
  64. print 'Render, widget changed'
  65. try:
  66. render(self.page.remote)
  67. except:
  68. pass
  69. def delete(self):
  70. r = self.page.remote
  71. super(Widget, self).delete()
  72. try:
  73. render(r)
  74. except:
  75. pass
  76. def remote(self):
  77. return self.page.remote
  78. def clean(self):
  79. for w in Widget.objects.filter(page = self.page, y = self.y, x = self.x):
  80. if w != self:
  81. try:
  82. wi, t = w.getSubTypeInstance()
  83. except:
  84. wi = None
  85. if wi != self:
  86. raise ValidationError('There is alredy another widget at this page and position. That is this widget: ' + str(w) + '. Please set another value for Page/X/Y.')
  87. if self.page.remote.theme.maxX != 0 and ((len(Widget.objects.filter(page = self.page, y = self.y)) > self.page.remote.theme.maxX) or (self.x > self.page.remote.theme.maxX)):
  88. raise ValidationError(u'To high X-value or too many widgets at this Y-value, max for this theme is %d.' %(self.page.remote.theme.maxX))
  89. if self.page.remote.theme.maxY != 0 and ((len(Widget.objects.filter(page = self.page, x = self.x)) > self.page.remote.theme.maxY) or (self.y > self.page.remote.theme.maxY)):
  90. raise ValidationError(u'To high Y-value or too many widget at this X-value, max for this theme is %d.' %(self.page.remote.theme.maxY))
  91. def getSubTypeInstance(self):
  92. t = self.getSubType()
  93. if t == 'SingleDevCmd':
  94. return self.singledevcmd, t
  95. elif t == 'OnOffDev':
  96. return self.onoffdev, t
  97. elif t == 'DimDev':
  98. return self.dimdev, t
  99. elif t == 'VariableValue':
  100. return self.variablevalue, t
  101. elif t == 'GenericContent':
  102. return self.genericcontent, t
  103. elif t == 'Heading':
  104. return self.heading, t
  105. elif t == 'Link':
  106. return self.link, t
  107. else:
  108. print 'Unknown type in getSubTypeInstance'
  109. return None, 'Unknown'
  110. def getSubType(self):
  111. if hasattr(self, 'singledevcmd'):
  112. return 'SingleDevCmd'
  113. elif hasattr(self, 'onoffdev'):
  114. return 'OnOffDev'
  115. elif hasattr(self, 'dimdev'):
  116. return 'DimDev'
  117. elif hasattr(self, 'variablevalue'):
  118. return 'VariableValue'
  119. elif hasattr(self, 'genericcontent'):
  120. return 'GenericContent'
  121. elif hasattr(self, 'heading'):
  122. return 'Heading'
  123. elif hasattr(self, 'link'):
  124. return 'Link'
  125. else:
  126. print 'Unknown type in getSubType'
  127. return None
  128. def __unicode__(self):
  129. if self.displayText == '':
  130. subType, t = self.getSubTypeInstance()
  131. if subType:
  132. return subType.getDisplayText()
  133. else:
  134. return 'Unable to call subType.getDisplayText'
  135. return self.displayText
  136. class SingleDevCmd(Widget):
  137. dev = models.ForeignKey('core.Device')
  138. cmd = models.ForeignKey('core.Command')
  139. def getDisplayText(self):
  140. return self.dev.name
  141. def clean(self):
  142. super(SingleDevCmd, self).clean()
  143. if self.cmd.cmd == 'DIM':
  144. if not self.dev.dim:
  145. raise ValidationError('Dimming not supported by selected device')
  146. if self.cmd.cmd == 'ACTIVATE':
  147. if not self.dev.activate:
  148. raise ValidationError('Activate not supported by selected device')
  149. if self.cmd.cmd in ['ON', 'OFF']:
  150. if not self.dev.onOff:
  151. raise ValidationError('On/Off not supported by selected device')
  152. if self.cmd.cmd not in ['ON', 'OFF', 'ACTIVATE', 'DIM']:
  153. raise ValidationError('Selected command is not supported by selected device')
  154. class OnOffDev(Widget):
  155. dev = models.ForeignKey('core.Device')
  156. def getDisplayText(self):
  157. return self.dev.name
  158. def clean(self):
  159. super(OnOffDev, self).clean()
  160. if not self.dev.onOff:
  161. raise ValidationError('On/Off not supported by selected device')
  162. class DimDev(Widget):
  163. dev = models.ForeignKey('core.Device')
  164. def getDisplayText(self):
  165. return self.dev.name
  166. def clean(self):
  167. super(DimDev, self).clean()
  168. if not self.dev.dim:
  169. raise ValidationError('Dimming not supported by selected device')
  170. class VariableValue(Widget):
  171. var = models.ForeignKey('core.GlobalVariable')
  172. def getDisplayText(self):
  173. return self.var.name
  174. class Heading(Widget):
  175. divider = models.BooleanField(default = False, blank = True)
  176. def getDisplayText(self):
  177. return 'Divider'
  178. class Link(Widget):
  179. targetpage = models.ForeignKey(Page, null = True, blank = True, default = None, help_text="Link to another page of this or another remote")
  180. url = models.CharField(max_length = 255, default = '', blank = True, help_text = "Entar a URL")
  181. def clean(self):
  182. super(Link, self).clean()
  183. if self.page == None:
  184. if self.url == '':
  185. raise ValidationError("Cant have empty page and no URL")
  186. if self.url != '':
  187. if (not self.url.startswith('http://')) or (not self.url.startswith('https://')):
  188. self.url = 'http://' + self.url
  189. def getDisplayText(self):
  190. return self.linktext
  191. class GenericContent(Widget):
  192. content = models.TextField(default = '')
  193. def getDisplayText(self):
  194. return 'Generic Content'
  195. def render(remote):
  196. baseUrl = '/remote/' + str(remote.id) + '/'
  197. staticBaseUrl = '/static/automagically/'
  198. preRenderedCache[int(remote.id)] = remote.render(baseUrl, staticBaseUrl)
  199. print 'rendered into preRenderedCache', remote.id
  200. def getPreRendered(remote_id, page):
  201. if int(remote_id) not in preRenderedCache:
  202. print 'Remote missing in cache, need to render it'
  203. try:
  204. r = Remote.objects.get(id = remote_id)
  205. render(r)
  206. except:
  207. if debug:
  208. raise
  209. else:
  210. strings = traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)
  211. print 'Error rendering page'
  212. for s in strings:
  213. print s
  214. raise Http404
  215. return preRenderedCache[int(remote_id)].get(int(page), preRenderedCache.get(0))
  216. def getPreRenderedCacheManifest(remote_id):
  217. if int(remote_id) not in preRenderedCache:
  218. print 'Remote missing in cache, need to render it'
  219. try:
  220. r = Remote.objects.get(id = remote_id)
  221. render(r)
  222. except:
  223. if debug:
  224. raise
  225. else:
  226. strings = traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)
  227. print 'Error rendering page in getPreRenderedCacheManifest'
  228. for s in strings:
  229. print s
  230. raise Http404
  231. return preRenderedCache[int(remote_id)].get('CACHE_MANIFEST', HttpResponseNotFound())
  232. def getConfig(remote, page = None):
  233. """ Merge the two configs from remote and optionaly page
  234. and return that as a dictionary. Expect a , separated
  235. list of key/value pairs sepparated by :. Config in
  236. page take precedance over the one in remote."""
  237. res = {}
  238. cfg = remote.theme_config
  239. if page:
  240. cfg += ',' + page.config
  241. for s in cfg.split(','):
  242. if s != '':
  243. kv = s.split(':', 1)
  244. res[kv[0]] = kv[1]
  245. return res