]> git.ipfire.org Git - ipfire.org.git/blame - webapp/backend/planet.py
Massive web site update
[ipfire.org.git] / webapp / backend / planet.py
CommitLineData
940227cb
MT
1#!/usr/bin/python
2
67ab72b8 3import datetime
27066195 4import re
940227cb 5import textile
27066195 6import unicodedata
940227cb 7
a6dc0bad 8from misc import Object
940227cb 9
a6dc0bad
MT
10class PlanetEntry(Object):
11 def __init__(self, backend, data):
12 Object.__init__(self, backend)
8876f3df 13
a6dc0bad 14 self.data = data
940227cb 15
60b0917c
MT
16 def __cmp__(self, other):
17 return cmp(self.published, other.published)
18
940227cb
MT
19 @property
20 def id(self):
a6dc0bad 21 return self.data.id
940227cb
MT
22
23 @property
24 def slug(self):
a6dc0bad 25 return self.data.slug
940227cb 26
67ab72b8
MT
27 def set_title(self, title):
28 if self.title == title:
29 return
30
31 self.db.execute("UPDATE planet SET title = %s WHERE id = %s", title, self.id)
32 self.data["title"] = title
33
34 title = property(lambda s: s.data.title, set_title)
a6dc0bad
MT
35
36 @property
37 def url(self):
38 return "http://planet.ipfire.org/post/%s" % self.slug
940227cb 39
67ab72b8
MT
40 def set_published(self, published):
41 if self.published == published:
42 return
43
44 self.db.execute("UPDATE planet SET published = %s WHERE id = %s",
45 published, self.id)
46 self.data["published"] = published
47
48 published = property(lambda s: s.data.published, set_published)
940227cb 49
cc3b928d
MT
50 @property
51 def year(self):
52 return self.published.year
53
54 @property
55 def month(self):
56 return self.published.month
57
940227cb
MT
58 @property
59 def updated(self):
a6dc0bad 60 return self.data.updated
940227cb 61
67ab72b8 62 def get_markdown(self):
a6dc0bad
MT
63 return self.data.markdown
64
67ab72b8
MT
65 def set_markdown(self, markdown):
66 if self.markdown == markdown:
67 return
68
69 markup = self.render(markdown)
70 self.db.execute("UPDATE planet SET markdown = %s, markup = %s WHERE id = %s",
71 markdown, markup, self.id)
72
73 self.data.update({
74 "markdown" : markdown,
75 "markup" : markup,
76 })
77
78 markdown = property(get_markdown, set_markdown)
79
a6dc0bad
MT
80 @property
81 def markup(self):
82 if self.data.markup:
83 return self.data.markup
84
85 return self.render(self.markdown)
940227cb
MT
86
87 @property
88 def abstract(self):
89 return self.render(self.markdown, 400)
90
91 def render(self, text, limit=0):
27066195 92 return self.planet.render(text, limit)
940227cb
MT
93
94 @property
95 def text(self):
a6dc0bad
MT
96 # Compat for markup
97 return self.markup
940227cb
MT
98
99 @property
100 def author(self):
66862195
MT
101 if not hasattr(self, "_author"):
102 self._author = self.accounts.get_by_uid(self.data.author_id)
a6dc0bad 103
66862195 104 return self._author
940227cb 105
67ab72b8
MT
106 def set_status(self, status):
107 if self.status == status:
108 return
109
110 self.db.execute("UPDATE planet SET status = %s WHERE id = %s", status, self.id)
111 self.data["status"] = status
112
113 status = property(lambda s: s.data.status, set_status)
114
115 def is_draft(self):
116 return self.status == "draft"
117
118 def is_published(self):
119 return self.status == "published"
120
d88b8f41
MT
121 def count_view(self, referer=None, location=None):
122 self.db.execute("INSERT INTO planet_views(post_id, referer, location) \
123 VALUES(%s, %s, %s)", self.id, referer, location)
c0c55656 124
c6ba6949
MT
125 if hasattr(self, "_views"):
126 self._views += 1
127
128 @property
129 def views(self):
130 if not hasattr(self, "_views"):
131 res = self.db.get("SELECT COUNT(*) AS views FROM planet_views \
132 WHERE post_id = %s", self.id)
133
134 self._views = res.views
135
136 return self._views
137
940227cb 138
a6dc0bad 139class Planet(Object):
940227cb 140 def get_authors(self):
9068dba1
MT
141 query = self.db.query("SELECT DISTINCT author_id FROM planet WHERE status = %s \
142 AND published IS NOT NULL AND published <= NOW()", "published")
143
940227cb 144 authors = []
9068dba1 145 for author in query:
a6dc0bad 146 author = self.accounts.search(author.author_id)
940227cb
MT
147 if author:
148 authors.append(author)
149
27066195 150 return sorted(authors)
940227cb 151
cc3b928d 152 def get_years(self):
9068dba1 153 res = self.db.query("SELECT DISTINCT EXTRACT(YEAR FROM published)::integer AS year \
a6dc0bad 154 FROM planet WHERE status = %s ORDER BY year DESC", "published")
cc3b928d
MT
155
156 return [row.year for row in res]
157
940227cb
MT
158 def get_entry_by_slug(self, slug):
159 entry = self.db.get("SELECT * FROM planet WHERE slug = %s", slug)
9068dba1 160
940227cb 161 if entry:
a6dc0bad 162 return PlanetEntry(self.backend, entry)
940227cb 163
27066195
MT
164 def get_entry_by_id(self, id):
165 entry = self.db.get("SELECT * FROM planet WHERE id = %s", id)
9068dba1 166
27066195 167 if entry:
a6dc0bad 168 return PlanetEntry(self.backend, entry)
27066195 169
a6dc0bad
MT
170 def get_entries(self, limit=3, offset=None, status="published", author_id=None):
171 query = "SELECT * FROM planet"
172 args, clauses = [], []
940227cb 173
a6dc0bad
MT
174 if status:
175 clauses.append("status = %s")
176 args.append(status)
940227cb 177
8570acc8
MT
178 if status == "published":
179 clauses.append("published <= NOW()")
180
a6dc0bad
MT
181 if author_id:
182 clauses.append("author_id = %s")
183 args.append(author_id)
940227cb 184
a6dc0bad
MT
185 if clauses:
186 query += " WHERE %s" % " AND ".join(clauses)
940227cb 187
a6dc0bad
MT
188 query += " ORDER BY published DESC"
189
190 # Respect limit and offset
191 if limit:
9068dba1
MT
192 query += " LIMIT %s"
193 args.append(limit)
194
a6dc0bad 195 if offset:
9068dba1
MT
196 query += " OFFSET %s"
197 args.append(offset)
940227cb 198
a6dc0bad
MT
199 entries = []
200 for entry in self.db.query(query, *args):
201 entry = PlanetEntry(self.backend, entry)
202 entries.append(entry)
940227cb 203
a6dc0bad 204 return entries
940227cb 205
a6dc0bad
MT
206 def get_entries_by_author(self, author_id, limit=None, offset=None):
207 return self.get_entries(limit=limit, offset=offset, author_id=author_id)
27066195 208
cc3b928d
MT
209 def get_entries_by_year(self, year):
210 entries = self.db.query("SELECT * FROM planet \
9068dba1
MT
211 WHERE status = %s AND EXTRACT(YEAR FROM published) = %s \
212 ORDER BY published DESC", "published", year)
cc3b928d 213
a6dc0bad 214 return [PlanetEntry(self.backend, e) for e in entries]
d88b8f41
MT
215
216 def get_hot_entries(self, days=30, limit=8):
217 entries = self.db.query("WITH hottest AS (SELECT post_id, COUNT(post_id) AS count \
218 FROM planet_views WHERE \"when\" >= NOW() - INTERVAL '%s days' \
219 GROUP BY post_id ORDER BY count DESC) SELECT * FROM planet \
220 LEFT JOIN hottest ON planet.id = hottest.post_id \
221 WHERE hottest.count IS NOT NULL \
222 ORDER BY hottest.count DESC LIMIT %s",
223 days, limit)
224
225 return [PlanetEntry(self.backend, e) for e in entries]
cc3b928d 226
27066195
MT
227 def render(self, text, limit=0):
228 if limit and len(text) >= limit:
229 text = text[:limit] + "..."
a6dc0bad 230
27066195
MT
231 return textile.textile(text)
232
233 def _generate_slug(self, title):
234 slug = unicodedata.normalize("NFKD", title).encode("ascii", "ignore")
235 slug = re.sub(r"[^\w]+", " ", slug)
236 slug = "-".join(slug.lower().strip().split())
237
238 if not slug:
239 slug = "entry"
240
241 while True:
242 e = self.db.get("SELECT * FROM planet WHERE slug = %s", slug)
243 if not e:
244 break
245 slug += "-"
246
247 return slug
248
3066ac8d 249 def create(self, title, markdown, author, status="published", published=None):
67ab72b8
MT
250 slug = self._generate_slug(title)
251 markup = self.render(markdown)
252
253 if published is None:
254 published = datetime.datetime.utcnow()
255
256 id = self.db.execute("INSERT INTO planet(author_id, slug, title, status, \
3066ac8d 257 markdown, markup, published) VALUES(%s, %s, %s, %s, %s, %s, %s) RETURNING id",
67ab72b8
MT
258 author.uid, slug, title, status, markdown, markup, published)
259
3066ac8d
MT
260 if id:
261 return self.get_entry_by_id(id)
67ab72b8 262
27066195
MT
263 def update_entry(self, entry):
264 self.db.execute("UPDATE planet SET title = %s, markdown = %s WHERE id = %s",
265 entry.title, entry.markdown, entry.id)
266
267 def save_entry(self, entry):
268 slug = self._generate_slug(entry.title)
269
9068dba1
MT
270 id = self.db.execute("INSERT INTO planet(author_id, title, slug, markdown, published) \
271 VALUES(%s, %s, %s, %s, NOW())", entry.author.uid, entry.title, slug, entry.markdown)
27066195 272
00af5cf1
MT
273 return id
274
2bdd073f 275 def search(self, what):
fa7e1a0a
MT
276 res = self.db.query("WITH \
277 q AS (SELECT plainto_tsquery(%s, %s) AS query), \
278 ranked AS (SELECT id, query, ts_rank_cd(to_tsvector(%s, markdown), query) AS rank \
279 FROM planet, q WHERE markdown @@ query ORDER BY rank DESC) \
280 SELECT *, ts_headline(markup, ranked.query, 'MinWords=100, MaxWords=110') AS markup FROM planet \
281 JOIN ranked ON planet.id = ranked.id \
282 WHERE status = %s AND published IS NOT NULL AND published <= NOW() \
60b0917c 283 ORDER BY ranked DESC LIMIT 25",
fa7e1a0a
MT
284 "english", what, "english", "published")
285
286 return [PlanetEntry(self.backend, e) for e in res]