]> git.ipfire.org Git - people/shoehn/ipfire.org.git/blob - webapp/backend/planet.py
c34898f0fe097191217cafd4b3ff09a2ab5c03a7
[people/shoehn/ipfire.org.git] / webapp / backend / planet.py
1 #!/usr/bin/python
2
3 import datetime
4 import re
5 import textile
6 import tornado.database
7 import unicodedata
8
9 from accounts import Accounts
10 from databases import Databases
11
12 from misc import Object
13
14 class PlanetEntry(Object):
15 def __init__(self, backend, data):
16 Object.__init__(self, backend)
17
18 self.data = data
19
20 @property
21 def id(self):
22 return self.data.id
23
24 @property
25 def slug(self):
26 return self.data.slug
27
28 def set_title(self, title):
29 if self.title == title:
30 return
31
32 self.db.execute("UPDATE planet SET title = %s WHERE id = %s", title, self.id)
33 self.data["title"] = title
34
35 title = property(lambda s: s.data.title, set_title)
36
37 @property
38 def url(self):
39 return "http://planet.ipfire.org/post/%s" % self.slug
40
41 def set_published(self, published):
42 if self.published == published:
43 return
44
45 self.db.execute("UPDATE planet SET published = %s WHERE id = %s",
46 published, self.id)
47 self.data["published"] = published
48
49 published = property(lambda s: s.data.published, set_published)
50
51 @property
52 def year(self):
53 return self.published.year
54
55 @property
56 def month(self):
57 return self.published.month
58
59 @property
60 def updated(self):
61 return self.data.updated
62
63 def get_markdown(self):
64 return self.data.markdown
65
66 def set_markdown(self, markdown):
67 if self.markdown == markdown:
68 return
69
70 markup = self.render(markdown)
71 self.db.execute("UPDATE planet SET markdown = %s, markup = %s WHERE id = %s",
72 markdown, markup, self.id)
73
74 self.data.update({
75 "markdown" : markdown,
76 "markup" : markup,
77 })
78
79 markdown = property(get_markdown, set_markdown)
80
81 @property
82 def markup(self):
83 if self.data.markup:
84 return self.data.markup
85
86 return self.render(self.markdown)
87
88 @property
89 def abstract(self):
90 return self.render(self.markdown, 400)
91
92 def render(self, text, limit=0):
93 return self.planet.render(text, limit)
94
95 @property
96 def text(self):
97 # Compat for markup
98 return self.markup
99
100 @property
101 def author(self):
102 if not hasattr(self, "__author"):
103 self.__author = self.accounts.search(self.data.author_id)
104
105 return self.__author
106
107 def set_status(self, status):
108 if self.status == status:
109 return
110
111 self.db.execute("UPDATE planet SET status = %s WHERE id = %s", status, self.id)
112 self.data["status"] = status
113
114 status = property(lambda s: s.data.status, set_status)
115
116 def is_draft(self):
117 return self.status == "draft"
118
119 def is_published(self):
120 return self.status == "published"
121
122 # Tags
123
124 def get_tags(self):
125 if not hasattr(self, "__tags"):
126 res = self.db.query("SELECT tag FROM planet_tags \
127 WHERE post_id = %s ORDER BY tag", self.id)
128 self.__tags = []
129 for row in res:
130 self.__tags.append(row.tag)
131
132 return self.__tags
133
134 def set_tags(self, tags):
135 # Delete all existing tags.
136 self.db.execute("DELETE FROM planet_tags WHERE post_id = %s", self.id)
137
138 self.db.executemany("INSERT INTO planet_tags(post_id, tag) VALUES(%s, %s)",
139 ((self.id, tag) for tag in tags))
140
141 # Update cache.
142 self.__tags = tags
143 self.__tags.sort()
144
145 tags = property(get_tags, set_tags)
146
147
148 class Planet(Object):
149 def get_authors(self):
150 authors = []
151 for author in self.db.query("SELECT DISTINCT author_id FROM planet WHERE status = %s", "published"):
152 author = self.accounts.search(author.author_id)
153 if author:
154 authors.append(author)
155
156 return sorted(authors)
157
158 def get_years(self):
159 res = self.db.query("SELECT DISTINCT YEAR(published) AS year \
160 FROM planet WHERE status = %s ORDER BY year DESC", "published")
161
162 return [row.year for row in res]
163
164 def get_entry_by_slug(self, slug):
165 entry = self.db.get("SELECT * FROM planet WHERE slug = %s", slug)
166 if entry:
167 return PlanetEntry(self.backend, entry)
168
169 def get_entry_by_id(self, id):
170 entry = self.db.get("SELECT * FROM planet WHERE id = %s", id)
171 if entry:
172 return PlanetEntry(self.backend, entry)
173
174 def _limit_and_offset_query(self, limit=None, offset=None):
175 query = " "
176
177 if limit:
178 if offset:
179 query += "LIMIT %d,%d" % (offset, limit)
180 else:
181 query += "LIMIT %d" % limit
182
183 return query
184
185 def get_entries(self, limit=3, offset=None, status="published", author_id=None):
186 query = "SELECT * FROM planet"
187 args, clauses = [], []
188
189 if status:
190 clauses.append("status = %s")
191 args.append(status)
192
193 if author_id:
194 clauses.append("author_id = %s")
195 args.append(author_id)
196
197 if clauses:
198 query += " WHERE %s" % " AND ".join(clauses)
199
200 query += " ORDER BY published DESC"
201
202 # Respect limit and offset
203 if limit:
204 if offset:
205 query += " LIMIT %s,%s"
206 args += [offset, limit,]
207 else:
208 query += " LIMIT %s"
209 args.append(limit)
210
211 entries = []
212 for entry in self.db.query(query, *args):
213 entry = PlanetEntry(self.backend, entry)
214 entries.append(entry)
215
216 return entries
217
218 def get_entries_by_author(self, author_id, limit=None, offset=None):
219 return self.get_entries(limit=limit, offset=offset, author_id=author_id)
220
221 def get_entries_by_year(self, year):
222 entries = self.db.query("SELECT * FROM planet \
223 WHERE status = %s AND YEAR(published) = %s ORDER BY published DESC",
224 "published", year)
225
226 return [PlanetEntry(self.backend, e) for e in entries]
227
228 def render(self, text, limit=0):
229 if limit and len(text) >= limit:
230 text = text[:limit] + "..."
231
232 return textile.textile(text)
233
234 def _generate_slug(self, title):
235 slug = unicodedata.normalize("NFKD", title).encode("ascii", "ignore")
236 slug = re.sub(r"[^\w]+", " ", slug)
237 slug = "-".join(slug.lower().strip().split())
238
239 if not slug:
240 slug = "entry"
241
242 while True:
243 e = self.db.get("SELECT * FROM planet WHERE slug = %s", slug)
244 if not e:
245 break
246 slug += "-"
247
248 return slug
249
250 def create(self, title, markdown, author, status="published", tags=None, published=None):
251 slug = self._generate_slug(title)
252 markup = self.render(markdown)
253
254 if published is None:
255 published = datetime.datetime.utcnow()
256
257 id = self.db.execute("INSERT INTO planet(author_id, slug, title, status, \
258 markdown, markup, published) VALUES(%s, %s, %s, %s, %s, %s, %s)",
259 author.uid, slug, title, status, markdown, markup, published)
260
261 entry = self.get_entry_by_id(id)
262
263 if tags:
264 entry.tags = tags
265
266 return entry
267
268 def update_entry(self, entry):
269 self.db.execute("UPDATE planet SET title = %s, markdown = %s WHERE id = %s",
270 entry.title, entry.markdown, entry.id)
271
272 def save_entry(self, entry):
273 slug = self._generate_slug(entry.title)
274
275 id = self.db.execute("INSERT INTO planet(author_id, title, slug, markdown, published) "
276 "VALUES(%s, %s, %s, %s, UTC_TIMESTAMP())", entry.author.uid, entry.title,
277 slug, entry.markdown)
278
279 return id
280
281 def search(self, what):
282 # Split tags.
283 tags = what.split()
284
285 query = "SELECT planet.* FROM planet INNER JOIN ( \
286 SELECT post_id FROM planet_tags \
287 INNER JOIN planet ON planet_tags.post_id = planet.id \
288 WHERE %s GROUP BY post_id HAVING COUNT(post_id) = %%s \
289 ) pt ON planet.id = pt.post_id ORDER BY published DESC"
290
291 args = (tags, len(tags))
292
293 clauses, args = [], tags
294 for tag in tags:
295 clauses.append("planet_tags.tag = %s")
296 args.append(len(tags))
297
298 entries = self.db.query(query % " OR ".join(clauses), *args)
299 return [PlanetEntry(self.backend, e) for e in entries]
300
301 def search_autocomplete(self, what):
302 tags = what.split()
303 last_tag = tags.pop()
304
305 res = self.db.query("SELECT tag, COUNT(tag) AS count FROM planet_tags \
306 WHERE tag LIKE %s GROUP BY tag ORDER BY count DESC", "%s%%" % last_tag)
307
308 if tags:
309 return ["%s %s" % (" ".join(tags), row.tag) for row in res]
310
311 return [row.tag for row in res]