/
models.py
118 lines (86 loc) · 3.55 KB
/
models.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
from django.db import models
from django.utils.functional import cached_property
import jsonpickle
from devmo import SECTIONS_TWITTER, SECTIONS_UPDATES
class BundleManager(models.Manager):
"""Custom manager for bundles."""
def recent_entries(self, bundles):
"""Most recent entries."""
if isinstance(bundles, basestring):
# Single bundle.
return Entry.objects.filter(feed__bundles__shortname=bundles)
else:
# Sequence of bundles.
return Entry.objects.filter(
feed__bundles__shortname__in=bundles)
class Bundle(models.Model):
"""A bundle of several feeds. A feed can be in several (or no) bundles."""
shortname = models.SlugField(
help_text='Short name to find this bundle by.', unique=True)
feeds = models.ManyToManyField('feeder.Feed', related_name='bundles',
blank=True)
objects = BundleManager()
def __unicode__(self):
return self.shortname
class Feed(models.Model):
"""A feed holds the metadata of an RSS feed."""
shortname = models.SlugField(
help_text='Short name to find this feed by.', unique=True)
title = models.CharField(max_length=140)
url = models.CharField(max_length=2048)
# HTTP Headers
etag = models.CharField(max_length=140)
last_modified = models.DateTimeField()
# If a feed has (severe) issues, it will be disabled
enabled = models.BooleanField(default=True)
disabled_reason = models.CharField(max_length=2048, blank=True)
keep = models.PositiveIntegerField(
default=0, help_text=('Discard all but this amount of entries. 0 == '
'do not discard.'))
created = models.DateTimeField(
auto_now_add=True, verbose_name='Created On')
updated = models.DateTimeField(
auto_now=True, verbose_name='Last Modified')
def __unicode__(self):
return self.shortname
def delete_old_entries(self):
"""Delete entries that exceed the amount we want to keep."""
if not self.keep > 0:
return
to_delete = self.entries.order_by('-last_published')[self.keep:]
for item in to_delete:
# This doesn't perform extremely well, but it's what we have to do
# to keep exactly `n` entries around, as LIMIT is invalid in a
# DELETE statement.
item.delete()
class Entry(models.Model):
"""An entry is an item representing feed content."""
feed = models.ForeignKey(Feed, related_name='entries')
guid = models.CharField(max_length=255)
raw = models.TextField()
visible = models.BooleanField(default=True)
# Feed entry updated field
last_published = models.DateTimeField()
created = models.DateTimeField(
auto_now_add=True, verbose_name='Created On')
updated = models.DateTimeField(
auto_now=True, verbose_name='Last Modified')
class Meta:
ordering = ['-last_published']
unique_together = ('feed', 'guid')
verbose_name_plural = 'Entries'
def __unicode__(self):
return '%s: %s' % (self.feed.shortname, self.guid)
@cached_property
def parsed(self):
"""Unpickled feed data."""
return jsonpickle.decode(self.raw)
@cached_property
def section(self):
"""The section this entry is associated with."""
try:
bundle = self.feed.bundles.all()[0].shortname
except IndexError:
return None
return SECTIONS_TWITTER.get(bundle, SECTIONS_UPDATES.get(
bundle, None))