PageRenderTime 279ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/robot/models.py

https://gitlab.com/epicglue/epicglue-py
Python | 710 lines | 550 code | 137 blank | 23 comment | 73 complexity | 873efb55435bf8fcbd147c49951bd52d MD5 | raw file
  1. import logging
  2. from datetime import datetime
  3. from hashlib import sha256
  4. import simplejson
  5. from django.contrib.auth.models import User
  6. from django.contrib.postgres.fields import ArrayField
  7. from django.db import models, IntegrityError
  8. from django_extensions.db.fields.json import JSONField
  9. from common.assertlib import *
  10. from common.enums import SubscriptionStatus
  11. from common.helpers import now
  12. log = logging.getLogger(__name__)
  13. class Item(models.Model):
  14. hash = models.CharField(max_length=64, primary_key=True)
  15. service = models.CharField(max_length=30)
  16. title = models.TextField(null=True, blank=True, default=None)
  17. description = models.TextField(null=True, blank=True, default=None)
  18. tags = ArrayField(models.TextField(null=True, blank=True, default=None))
  19. item_type = models.CharField(max_length=30)
  20. media_type = models.CharField(max_length=30)
  21. author = models.TextField()
  22. author_image = models.TextField(null=True, blank=True, default=None)
  23. author_image_large = models.TextField(null=True, blank=True, default=None)
  24. image = models.TextField(null=True, blank=True, default=None)
  25. image_large = models.TextField(null=True, blank=True, default=None)
  26. image_small = models.TextField(null=True, blank=True, default=None)
  27. image_width = models.IntegerField(null=True, blank=True, default=None)
  28. image_height = models.IntegerField(null=True, blank=True, default=None)
  29. image_large_width = models.IntegerField(null=True, blank=True, default=None)
  30. image_large_height = models.IntegerField(null=True, blank=True, default=None)
  31. image_small_width = models.IntegerField(null=True, blank=True, default=None)
  32. image_small_height = models.IntegerField(null=True, blank=True, default=None)
  33. video = models.TextField(null=True, blank=True, default=None)
  34. video_width = models.IntegerField(null=True, blank=True, default=None)
  35. video_height = models.IntegerField(null=True, blank=True, default=None)
  36. video_large = models.TextField(null=True, blank=True, default=None)
  37. video_large_width = models.IntegerField(null=True, blank=True, default=None)
  38. video_large_height = models.IntegerField(null=True, blank=True, default=None)
  39. location = ArrayField(models.FloatField(null=True, blank=True, default=None), size=2)
  40. location_name = models.TextField(null=True, blank=True, default=None)
  41. url = models.TextField(null=True, blank=True, default=None)
  42. url_in = models.TextField(null=True, blank=True, default=None)
  43. url_ext = models.TextField(null=True, blank=True, default=None)
  44. item_hash = models.CharField(max_length=64, null=True, blank=True, default=None)
  45. content_id = models.CharField(max_length=100)
  46. content_secondary_id = models.CharField(max_length=100, null=True, blank=True, default=None)
  47. content_created_at = models.DateTimeField()
  48. content_updated_at = models.DateTimeField(null=True, blank=True, default=None)
  49. content_order_by_date = models.DateTimeField()
  50. visibility = models.CharField(max_length=10)
  51. points = models.IntegerField(null=True, blank=True, default=None)
  52. comments = models.IntegerField(null=True, blank=True, default=None)
  53. is_duplicate = models.BooleanField(default=False)
  54. is_deleted = models.BooleanField(default=False)
  55. created_at = models.DateTimeField(auto_now_add=True)
  56. updated_at = models.DateTimeField(auto_now=True)
  57. class Meta:
  58. db_table = 'item'
  59. def __str__(self):
  60. return 'Model <%s> (%s, %s)' % (self.__class__.__name__, self.service, self.get_hash())
  61. def get_hash(self):
  62. content_id = self.content_id if self.content_secondary_id is None else self.content_secondary_id
  63. return sha256(self.service + str(content_id)).hexdigest()
  64. def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
  65. # if not self.does_hash_exist():
  66. # super(self.__class__, self).save()
  67. # log.debug('Updated -- %s' % str(self))
  68. # return
  69. self.hash = self.get_hash()
  70. if self.tags is None:
  71. self.tags = []
  72. if self.location is None:
  73. self.location = []
  74. self.validate()
  75. try:
  76. super(self.__class__, self).save()
  77. except IntegrityError, e:
  78. log.debug('Duplicate -- %s' % str(self))
  79. return
  80. log.debug('Stored -- %s' % str(self))
  81. def to_json(self, encode=True):
  82. json = {
  83. 'hash': self.get_hash(),
  84. 'content_id': self.content_id,
  85. 'content_created_at': self.content_created_at,
  86. 'content_order_by_date': self.content_order_by_date,
  87. 'item_type': self.item_type,
  88. 'media_type': self.media_type,
  89. 'service': self.service,
  90. 'author': self.author,
  91. 'visibility': str(self.visibility)
  92. }
  93. if self.title:
  94. json['title'] = self.title
  95. if self.description:
  96. json['description'] = self.description
  97. if self.image:
  98. json['image'] = self.image
  99. json['image_width'] = self.image_width
  100. json['image_height'] = self.image_height
  101. if self.image_large:
  102. json['image_large'] = self.image_large
  103. json['image_large_width'] = self.image_large_width
  104. json['image_large_height'] = self.image_large_height
  105. if self.image_small:
  106. json['image_small'] = self.image_small
  107. json['image_small_width'] = self.image_small_width
  108. json['image_small_height'] = self.image_small_height
  109. if self.video:
  110. json['video'] = self.video
  111. json['video_width'] = self.video_width
  112. json['video_height'] = self.video_height
  113. if self.video_large:
  114. json['video_large'] = self.video_large
  115. json['video_large_width'] = self.video_large_width
  116. json['video_large_height'] = self.video_large_height
  117. if self.author_image:
  118. json['author_image'] = self.author_image
  119. if self.author_image_large:
  120. json['author_image_large'] = self.author_image_large
  121. if self.tags and len(self.tags) > 0:
  122. json['tags'] = list(self.tags)
  123. if self.location and len(self.location) > 0:
  124. json['location'] = self.location
  125. if self.location_name:
  126. json['location_name'] = self.location_name
  127. if self.url:
  128. json['url'] = self.url
  129. if self.url_in:
  130. json['url_in'] = self.url_in
  131. if self.url_ext:
  132. json['url_ext'] = self.url_ext
  133. if encode:
  134. json['content_created_at'] = str(json['content_created_at'])
  135. return simplejson.dumps(json)
  136. return json
  137. @staticmethod
  138. def from_json(src):
  139. """
  140. :rtype : Item
  141. :param src: dict
  142. :return item:
  143. """
  144. m = Item()
  145. m.content_id = src['content_id']
  146. if 'content_secondary_id' in src:
  147. m.content_secondary_id = src['content_secondary_id']
  148. if 'content_order_by_date' in src:
  149. m.content_order_by_date = src['content_order_by_date']
  150. m.content_created_at = src['content_created_at']
  151. m.item_type = src['item_type']
  152. m.media_type = src['media_type']
  153. m.service = src['service']
  154. m.author = src['author']
  155. m.visibility = src['visibility']
  156. m.created_at = now()
  157. if 'content_updated_at' in src:
  158. m.content_updated_at = src['content_updated_at']
  159. if 'title' in src:
  160. m.title = src['title']
  161. if 'description' in src:
  162. m.description = src['description']
  163. if 'image' in src:
  164. m.image = src['image']
  165. m.image_width = src['image_width']
  166. m.image_height = src['image_height']
  167. if 'image_large' in src:
  168. m.image_large = src['image_large']
  169. m.image_large_width = src['image_large_width']
  170. m.image_large_height = src['image_large_height']
  171. if 'image_small' in src:
  172. m.image_small = src['image_small']
  173. m.image_small_width = src['image_small_width']
  174. m.image_small_height = src['image_small_height']
  175. if 'video' in src:
  176. m.video = src['video']
  177. m.video_width = src['video_width']
  178. m.video_height = src['video_height']
  179. if 'video_large' in src:
  180. m.video_large = src['video_large']
  181. m.video_large_width = src['video_large_width']
  182. m.video_large_height = src['video_large_height']
  183. if 'author_image' in src:
  184. m.author_image = src['author_image']
  185. if 'author_image_large' in src:
  186. m.author_image_large = src['author_image_large']
  187. if 'tags' in src:
  188. m.tags = src['tags']
  189. if 'url' in src:
  190. m.url = src['url']
  191. if 'url_in' in src:
  192. m.url_in = src['url_in']
  193. if 'url_ext' in src:
  194. m.url_ext = src['url_ext']
  195. if 'location' in src:
  196. m.location = src['location']
  197. if 'location_name' in src:
  198. m.location_name = src['location_name']
  199. if 'points' in src:
  200. m.points = src['points']
  201. if 'comments' in src:
  202. m.comments = src['comments']
  203. return m
  204. def to_es_json(self):
  205. """
  206. :rtype dict
  207. :return:
  208. """
  209. obj = self.to_json(False)
  210. if self.tags:
  211. obj['tags'] = list(self.tags)
  212. if self.points:
  213. obj['points'] = self.points
  214. if self.comments:
  215. obj['comments'] = self.comments
  216. if self.location and len(self.location) == 2:
  217. obj['location'] = {
  218. 'lat': self.location[0],
  219. 'lon': self.location[1]
  220. }
  221. if self.location_name:
  222. obj['location_name'] = self.location_name
  223. return obj
  224. def to_es_update_json(self):
  225. obj = {
  226. 'updated_at': now()
  227. }
  228. if self.points is not None:
  229. obj['points'] = self.points
  230. if self.comments is not None:
  231. obj['comments'] = self.comments
  232. return obj
  233. def validate(self):
  234. assertIsNot(self.content_id, None)
  235. assertIsInstance(self.created_at, datetime)
  236. assertIsInstance(self.content_created_at, datetime)
  237. assertIsNot(self.item_type, None)
  238. assertIsInstance(str(self.item_type), basestring)
  239. assertNotEqual(len(str(self.item_type)), 0)
  240. assertIsNot(self.media_type, None)
  241. assertIsInstance(str(self.media_type), basestring)
  242. assertNotEqual(len(str(self.media_type)), 0)
  243. assertIsInstance(self.service, basestring)
  244. assertNotEqual(len(unicode(self.service)), 0)
  245. assertIsInstance(self.author, basestring)
  246. assertNotEqual(len(unicode(self.author)), 0)
  247. assertIsNot(self.visibility, None)
  248. def is_indexable(self):
  249. # return not settings.TESTING
  250. return True
  251. def does_hash_exist(self):
  252. try:
  253. Item.objects.get(pk=self.get_hash())
  254. return True
  255. except Item.DoesNotExist:
  256. return False
  257. class ItemRawManager(models.Manager):
  258. def add_item(self, hash, json):
  259. item, created = self.get_or_create(hash=hash, defaults={'json': json})
  260. if not created:
  261. item.json = json
  262. item.updated_at = now()
  263. item.save()
  264. class ItemRaw(models.Model):
  265. hash = models.CharField(max_length=64, primary_key=True)
  266. json = JSONField()
  267. created_at = models.DateTimeField(auto_now_add=True, editable=False)
  268. updated_at = models.DateTimeField(auto_now=True, editable=False)
  269. objects = ItemRawManager()
  270. class Meta:
  271. db_table = 'item_raw'
  272. class UserItemManager(models.Manager):
  273. pass
  274. class UserItem(models.Model):
  275. user = models.ForeignKey(User, db_index=True)
  276. item = models.ForeignKey('Item')
  277. is_read = models.BooleanField(default=False)
  278. is_glued = models.BooleanField(default=False)
  279. is_deleted = models.BooleanField(default=False)
  280. created_at = models.DateTimeField(auto_now_add=True, editable=False)
  281. updated_at = models.DateTimeField(auto_now=True, editable=False)
  282. objects = UserItemManager()
  283. class Meta:
  284. db_table = 'user_item'
  285. unique_together = [['user', 'item']]
  286. @staticmethod
  287. def add_item(user, item):
  288. user_item, created = UserItem.objects.get_or_create(user=user, item=item,
  289. defaults={'is_read': False, 'is_glued': False})
  290. return user_item
  291. def mark_as_read(self):
  292. if self.is_read is False:
  293. self.is_read = True
  294. self.save()
  295. class UserItemSubscriptionManager(models.Manager):
  296. def get_items_to_index(self, batch=10):
  297. return self.filter(is_indexed=False)[:100]
  298. class UserItemSubscription(models.Model):
  299. user_item = models.ForeignKey('UserItem')
  300. user_subscription = models.ForeignKey('UserSubscription')
  301. is_indexed = models.BooleanField(default=False)
  302. created_at = models.DateTimeField(auto_now_add=True, editable=False)
  303. updated_at = models.DateTimeField(auto_now=True, editable=False)
  304. objects = UserItemSubscriptionManager()
  305. class Meta:
  306. db_table = 'user_item_subscription'
  307. @staticmethod
  308. def add_item(user_item, sub):
  309. try:
  310. user_item_sub, created = UserItemSubscription.objects.get_or_create(user_item=user_item, user_subscription=sub)
  311. except UserItemSubscription.MultipleObjectsReturned:
  312. for dup in UserItemSubscription.objects.filter(user_item=user_item, user_subscription=sub)[1:]:
  313. dup.delete()
  314. return UserItemSubscription.add_item(user_item, sub)
  315. return user_item_sub
  316. class UserServiceToken(models.Model):
  317. profile = models.ForeignKey('UserServiceProfile', null=True, blank=True, default=None, db_index=True)
  318. scope = models.CharField(max_length=255)
  319. token = models.CharField(max_length=255)
  320. token_secret = models.CharField(max_length=255, null=True, blank=True, default=None)
  321. refresh_token = models.CharField(max_length=255, null=True, blank=True, default=None)
  322. expiry = models.DateTimeField(null=True, blank=True, default=None)
  323. is_active = models.BooleanField(default=True)
  324. created_at = models.DateTimeField(auto_now_add=True, editable=False)
  325. updated_at = models.DateTimeField(auto_now=True, editable=False)
  326. class Meta:
  327. db_table = 'user_service_token'
  328. def __unicode__(self):
  329. return u'UserServiceToken (%s, %s)' % (str(self.profile), self.is_active)
  330. class Service(models.Model):
  331. category = models.ForeignKey('Category')
  332. name = models.CharField(max_length=100)
  333. short_name = models.SlugField(max_length=30, unique=True)
  334. description = models.CharField(max_length=255, null=True, blank=True, default=None)
  335. is_locked = models.BooleanField(default=False)
  336. is_visible = models.BooleanField(default=True)
  337. created_at = models.DateTimeField(auto_now_add=True, editable=False)
  338. updated_at = models.DateTimeField(auto_now=True, editable=False)
  339. class Meta:
  340. db_table = 'service'
  341. def __unicode__(self):
  342. return u'%s' % self.name
  343. class UserServiceProfileManager(models.Manager):
  344. def profile_for_username(self, user, service, username):
  345. sp, created = self.get_or_create(user=user, service=service, identifier=username, defaults={'friendly_name': username})
  346. return sp
  347. class UserServiceProfile(models.Model):
  348. user = models.ForeignKey(User, db_index=True)
  349. service = models.ForeignKey('Service')
  350. identifier = models.CharField(max_length=50)
  351. friendly_name = models.CharField(max_length=50)
  352. created_at = models.DateTimeField(auto_now_add=True, editable=False)
  353. updated_at = models.DateTimeField(auto_now=True, editable=False)
  354. objects = UserServiceProfileManager()
  355. class Meta:
  356. db_table = 'user_service_profile'
  357. unique_together = [
  358. ['user', 'service', 'identifier']
  359. ]
  360. def __unicode__(self):
  361. return '%s at %s' % (self.friendly_name, str(self.service))
  362. class Category(models.Model):
  363. upper_category = models.ForeignKey('Category', null=True, blank=True, default=None)
  364. name = models.CharField(max_length=20)
  365. description = models.CharField(max_length=255, null=True, blank=True, default=None)
  366. is_visible = models.BooleanField(default=True)
  367. created_at = models.DateTimeField(auto_now_add=True, editable=False)
  368. updated_at = models.DateTimeField(auto_now=True, editable=False)
  369. class Meta:
  370. db_table = 'category'
  371. verbose_name_plural = 'categories'
  372. def __unicode__(self):
  373. return self.name
  374. class Source(models.Model):
  375. service = models.ForeignKey('Service')
  376. script_name = models.CharField(max_length=100)
  377. name = models.CharField(max_length=30)
  378. description = models.CharField(max_length=100, null=True, blank=True, default=None)
  379. url = models.CharField(max_length=200)
  380. item_ordering_field = models.CharField(max_length=20, default='content_created_at')
  381. allow_value = models.BooleanField(default=False)
  382. value = models.CharField(max_length=100, null=True, blank=True, default=None)
  383. value_hint = models.CharField(max_length=100, null=True, blank=True, default=None)
  384. min_refresh = models.IntegerField(default=5 * 60)
  385. do_auto_subscribe = models.BooleanField(default=False)
  386. is_locked = models.BooleanField(default=False)
  387. is_per_user = models.BooleanField(default=True)
  388. is_active = models.BooleanField(default=True)
  389. is_public = models.BooleanField(default=True)
  390. created_at = models.DateTimeField(auto_now_add=True, editable=False)
  391. updated_at = models.DateTimeField(auto_now=True, editable=False)
  392. class Meta:
  393. db_table = 'source'
  394. unique_together = [
  395. ['service', 'script_name', 'value']
  396. ]
  397. def __unicode__(self):
  398. return u'%s (%s)' % (self.name, str(self.service))
  399. class SubscriptionManager(models.Manager):
  400. def find_and_update_ready_to_refresh(self):
  401. log.debug('Looking for tasks to set due to refresh')
  402. for sub in self.filter(due_refresh=False, status=str(SubscriptionStatus.READY), is_active=True):
  403. if sub.last_refresh_at is None:
  404. sub.due_refresh = True
  405. sub.save()
  406. continue
  407. since_refresh = now() - sub.last_refresh_at
  408. # check if tokens left
  409. if sub.no_tokens_left():
  410. log.debug('No tokens left')
  411. if since_refresh.total_seconds() > sub.source.min_refresh:
  412. sub.due_refresh = True
  413. sub.save()
  414. log.debug('Set %s as due to refresh' % sub.source.script_name)
  415. # else:
  416. # log.debug('%d < %d' % (since_refresh.total_seconds(), sub.source.min_refresh))
  417. def next_to_run(self):
  418. log.debug('Looking for tasks to run')
  419. tasks = []
  420. for task in self.filter(due_refresh=True, status=str(SubscriptionStatus.READY), is_active=True):
  421. if task.has_subscribers():
  422. task.set_as_enqueued()
  423. tasks.append(task)
  424. log.debug('Found %d tasks' % len(tasks))
  425. return tasks
  426. class Subscription(models.Model):
  427. user = models.ForeignKey(User, null=True, blank=True, default=None)
  428. source = models.ForeignKey('Source')
  429. profile = models.ForeignKey('UserServiceProfile', null=True, blank=True, default=None)
  430. value = models.CharField(max_length=100, null=True, blank=True, default=None)
  431. status = models.CharField(max_length=15, default=str(SubscriptionStatus.READY), choices=(
  432. (str(SubscriptionStatus.READY), 'Ready'),
  433. (str(SubscriptionStatus.ENQUEUED), 'Enqueued'),
  434. (str(SubscriptionStatus.IN_PROGRESS), 'In Progress'),
  435. (str(SubscriptionStatus.FAILED), 'Failed')))
  436. tokens_used = models.IntegerField(default=0, null=True, blank=True)
  437. tokens_left = models.IntegerField(default=0, null=True, blank=True)
  438. tokens_reset_at = models.DateTimeField(null=True, blank=True, default=None)
  439. overwritten_by_subscription_id = models.IntegerField(null=True, blank=True, default=None)
  440. due_refresh = models.BooleanField(default=True)
  441. is_active = models.BooleanField(default=True)
  442. is_hidden = models.BooleanField(default=False)
  443. last_refresh_at = models.DateTimeField(null=True, blank=True, default=None)
  444. created_at = models.DateTimeField(auto_now_add=True, editable=False)
  445. updated_at = models.DateTimeField(auto_now=True, editable=False)
  446. objects = SubscriptionManager()
  447. class Meta:
  448. db_table = 'subscription'
  449. index_together = [
  450. ['due_refresh', 'status', 'is_active']
  451. ]
  452. unique_together = [
  453. ['source', 'value']
  454. ]
  455. def __unicode__(self):
  456. if self.value:
  457. return u'Subscription of %s (%s)' % (str(self.source), self.value)
  458. return u'Subscription of %s' % str(self.source)
  459. def has_subscribers(self):
  460. return self.subscribers().count() > 0
  461. def no_tokens_left(self):
  462. if self.tokens_reset_at is None:
  463. return False
  464. return self.tokens_left < 1 and self.tokens_reset_at > now()
  465. def subscribers(self):
  466. return UserSubscription.objects.filter(subscription=self.id)
  467. def add_subscriber(self, user, profile=None):
  468. if UserSubscription.objects.filter(subscription=self, user=user, profile=profile).count() > 0:
  469. log.debug('%s was already a subscriber' % str(profile))
  470. return False
  471. log.debug('Adding subscriber %s' % str(profile))
  472. us = UserSubscription()
  473. us.subscription = self
  474. us.user = user
  475. us.profile = profile
  476. us.save()
  477. log.debug('%s added as a subscriber' % str(profile))
  478. return True
  479. def remove_subscriber(self, user, profile):
  480. return UserSubscription.objects.filter(subscription=self, user=user, profile=profile).delete()
  481. def set_as_ready(self):
  482. self.status = str(SubscriptionStatus.READY)
  483. self.save()
  484. log.debug('%s is READY' % str(self))
  485. def set_as_enqueued(self):
  486. self.status = str(SubscriptionStatus.ENQUEUED)
  487. self.due_refresh = False
  488. self.save()
  489. log.debug('%s is ENQUEUED' % str(self))
  490. def set_as_in_progress(self):
  491. self.status = str(SubscriptionStatus.IN_PROGRESS)
  492. self.due_refresh = False
  493. self.save()
  494. log.debug('%s is IN PROGRESS' % str(self))
  495. def set_as_failed(self):
  496. self.status = str(SubscriptionStatus.FAILED)
  497. self.due_refresh = True
  498. self.save()
  499. log.debug('%s has FAILED' % str(self))
  500. @staticmethod
  501. def add_subscription(source, user, profile=None, value=None):
  502. if not source.is_per_user:
  503. current_sub = Subscription.objects.filter(source=source, value=value)
  504. if current_sub.count() > 0:
  505. log.debug('Subscription found')
  506. s = current_sub[0]
  507. s.add_subscriber(user, profile)
  508. return s
  509. s = Subscription()
  510. s.user = user
  511. s.profile = profile
  512. s.source = source
  513. s.value = value
  514. s.save()
  515. s.add_subscriber(user, profile)
  516. log.debug('Subscription created')
  517. return s
  518. class SubscriptionLog(models.Model):
  519. subscription = models.ForeignKey('Subscription', db_index=True)
  520. url = models.TextField(null=True, blank=True, default=None)
  521. code = models.IntegerField()
  522. msg = models.TextField(null=True, blank=True, default=None)
  523. created_at = models.DateTimeField(auto_now_add=True, editable=False)
  524. class Meta:
  525. db_table = 'subscription_log'
  526. verbose_name = 'Subscription Log'
  527. @staticmethod
  528. def add_log(subscription, code, msg=None, url=None):
  529. return SubscriptionLog(subscription=subscription, code=code, msg=msg, url=url).save()
  530. class UserSubscription(models.Model):
  531. subscription = models.ForeignKey('Subscription', db_index=True)
  532. user = models.ForeignKey(User, db_index=True)
  533. profile = models.ForeignKey('UserServiceProfile', null=True, blank=True, default=None)
  534. created_at = models.DateTimeField(auto_now_add=True, editable=False)
  535. updated_at = models.DateTimeField(auto_now=True, editable=False)
  536. class Meta:
  537. db_table = 'user_subscription'
  538. verbose_name = 'user\'s subscription'
  539. unique_together = [
  540. ['subscription', 'user', 'profile']
  541. ]
  542. def __unicode__(self):
  543. return u'User Subscription of %s by %s' % (str(self.subscription), str(self.profile))
  544. class State(models.Model):
  545. subscription = models.ForeignKey('Subscription')
  546. value = models.CharField(max_length=255)
  547. created_at = models.DateTimeField(auto_now_add=True, editable=False)
  548. updated_at = models.DateTimeField(auto_now=True, editable=False)
  549. class Meta:
  550. db_table = 'state'
  551. def __unicode__(self):
  552. return u'State of %s' % str(self.subscription)
  553. @staticmethod
  554. def for_subscription(subscription):
  555. """
  556. :type subscription: Subscription
  557. :rtype : State
  558. """
  559. try:
  560. state = State.objects.get(subscription=subscription)
  561. log.debug('Loaded state %s' % str(state))
  562. return state
  563. except State.DoesNotExist:
  564. log.debug('Did not found stored state')
  565. return State(subscription=subscription, value=None)
  566. class Feedback(models.Model):
  567. user = models.ForeignKey(User, db_index=True)
  568. feedback = models.TextField(editable=False)
  569. created_at = models.DateTimeField(auto_now_add=True, editable=False)
  570. class Meta:
  571. db_table = 'feedback'