#!/usr/bin/python
+import datetime
import re
import textile
-import tornado.database
import unicodedata
-from accounts import Accounts
-from databases import Databases
+from misc import Object
-from misc import Singleton
+class PlanetEntry(Object):
+ def __init__(self, backend, data):
+ Object.__init__(self, backend)
-class PlanetEntry(object):
- def __init__(self, db, entry=None):
- self.db = db
-
- if entry:
- self.__entry = entry
- else:
- self.__entry = tornado.database.Row({
- "id" : None,
- "title" : "",
- "markdown" : "",
- "tags" : [],
- })
-
- def set(self, key, val):
- self.__entry[key] = val
-
- @property
- def planet(self):
- return Planet()
+ self.data = data
@property
def id(self):
- return self.__entry.id
+ return self.data.id
@property
def slug(self):
- return self.__entry.slug
+ return self.data.slug
- @property
- def title(self):
- return self.__entry.title
+ def set_title(self, title):
+ if self.title == title:
+ return
+
+ self.db.execute("UPDATE planet SET title = %s WHERE id = %s", title, self.id)
+ self.data["title"] = title
+
+ title = property(lambda s: s.data.title, set_title)
@property
- def published(self):
- return self.__entry.published
+ def url(self):
+ return "http://planet.ipfire.org/post/%s" % self.slug
+
+ def set_published(self, published):
+ if self.published == published:
+ return
+
+ self.db.execute("UPDATE planet SET published = %s WHERE id = %s",
+ published, self.id)
+ self.data["published"] = published
+
+ published = property(lambda s: s.data.published, set_published)
@property
def year(self):
@property
def updated(self):
- return self.__entry.updated
+ return self.data.updated
+
+ def get_markdown(self):
+ return self.data.markdown
+
+ def set_markdown(self, markdown):
+ if self.markdown == markdown:
+ return
+
+ markup = self.render(markdown)
+ self.db.execute("UPDATE planet SET markdown = %s, markup = %s WHERE id = %s",
+ markdown, markup, self.id)
+
+ self.data.update({
+ "markdown" : markdown,
+ "markup" : markup,
+ })
+
+ markdown = property(get_markdown, set_markdown)
@property
- def markdown(self):
- return self.__entry.markdown
+ def markup(self):
+ if self.data.markup:
+ return self.data.markup
+
+ return self.render(self.markdown)
@property
def abstract(self):
@property
def text(self):
- return self.render(self.markdown)
+ # Compat for markup
+ return self.markup
@property
def author(self):
- return Accounts().search(self.__entry.author_id)
+ if not hasattr(self, "__author"):
+ self.__author = self.accounts.search(self.data.author_id)
+
+ return self.__author
+
+ def set_status(self, status):
+ if self.status == status:
+ return
+
+ self.db.execute("UPDATE planet SET status = %s WHERE id = %s", status, self.id)
+ self.data["status"] = status
+
+ status = property(lambda s: s.data.status, set_status)
+
+ def is_draft(self):
+ return self.status == "draft"
+
+ def is_published(self):
+ return self.status == "published"
# Tags
tags = property(get_tags, set_tags)
-class Planet(object):
- __metaclass__ = Singleton
-
- @property
- def db(self):
- return Databases().webapp
-
+class Planet(Object):
def get_authors(self):
+ query = self.db.query("SELECT DISTINCT author_id FROM planet WHERE status = %s \
+ AND published IS NOT NULL AND published <= NOW()", "published")
+
authors = []
- for author in self.db.query("SELECT DISTINCT author_id FROM planet"):
- author = Accounts().search(author.author_id)
+ for author in query:
+ author = self.accounts.search(author.author_id)
if author:
authors.append(author)
return sorted(authors)
def get_years(self):
- res = self.db.query("SELECT DISTINCT YEAR(published) AS year \
- FROM planet ORDER BY year DESC")
+ res = self.db.query("SELECT DISTINCT EXTRACT(YEAR FROM published)::integer AS year \
+ FROM planet WHERE status = %s ORDER BY year DESC", "published")
return [row.year for row in res]
def get_entry_by_slug(self, slug):
entry = self.db.get("SELECT * FROM planet WHERE slug = %s", slug)
+
if entry:
- return PlanetEntry(self.db, entry)
+ return PlanetEntry(self.backend, entry)
def get_entry_by_id(self, id):
entry = self.db.get("SELECT * FROM planet WHERE id = %s", id)
+
if entry:
- return PlanetEntry(self.db, entry)
+ return PlanetEntry(self.backend, entry)
- def _limit_and_offset_query(self, limit=None, offset=None):
- query = " "
+ def get_entries(self, limit=3, offset=None, status="published", author_id=None):
+ query = "SELECT * FROM planet"
+ args, clauses = [], []
- if limit:
- if offset:
- query += "LIMIT %d,%d" % (offset, limit)
- else:
- query += "LIMIT %d" % limit
+ if status:
+ clauses.append("status = %s")
+ args.append(status)
- return query
+ if author_id:
+ clauses.append("author_id = %s")
+ args.append(author_id)
- def get_entries(self, limit=3, offset=None):
- query = "SELECT * FROM planet WHERE acknowledged='Y' ORDER BY published DESC"
+ if clauses:
+ query += " WHERE %s" % " AND ".join(clauses)
+
+ query += " ORDER BY published DESC"
+
+ # Respect limit and offset
+ if limit:
+ query += " LIMIT %s"
+ args.append(limit)
- # Respect limit and offset
- query += self._limit_and_offset_query(limit=limit, offset=offset)
+ if offset:
+ query += " OFFSET %s"
+ args.append(offset)
entries = []
- for entry in self.db.query(query):
- entries.append(PlanetEntry(self.db, entry))
+ for entry in self.db.query(query, *args):
+ entry = PlanetEntry(self.backend, entry)
+ entries.append(entry)
return entries
def get_entries_by_author(self, author_id, limit=None, offset=None):
- query = "SELECT * FROM planet WHERE author_id = '%s'" % author_id
- query += " AND acknowledged='Y' ORDER BY published DESC"
-
- # Respect limit and offset
- query += self._limit_and_offset_query(limit=limit, offset=offset)
-
- entries = self.db.query(query)
-
- return [PlanetEntry(self.db, e) for e in entries]
+ return self.get_entries(limit=limit, offset=offset, author_id=author_id)
def get_entries_by_year(self, year):
entries = self.db.query("SELECT * FROM planet \
- WHERE YEAR(published) = %s ORDER BY published DESC", year)
+ WHERE status = %s AND EXTRACT(YEAR FROM published) = %s \
+ ORDER BY published DESC", "published", year)
- return [PlanetEntry(self.db, e) for e in entries]
+ return [PlanetEntry(self.backend, e) for e in entries]
def render(self, text, limit=0):
if limit and len(text) >= limit:
text = text[:limit] + "..."
+
return textile.textile(text)
def _generate_slug(self, title):
return slug
- def update_entry(self, entry):
- self.db.execute("UPDATE planet SET title = %s, markdown = %s WHERE id = %s",
- entry.title, entry.markdown, entry.id)
+ def create(self, title, markdown, author, status="published", tags=None, published=None):
+ slug = self._generate_slug(title)
+ markup = self.render(markdown)
- def save_entry(self, entry):
- slug = self._generate_slug(entry.title)
+ if published is None:
+ published = datetime.datetime.utcnow()
- self.db.execute("INSERT INTO planet(author_id, title, slug, markdown, published) "
- "VALUES(%s, %s, %s, %s, UTC_TIMESTAMP())", entry.author.uid, entry.title,
- slug, entry.markdown)
+ id = self.db.execute("INSERT INTO planet(author_id, slug, title, status, \
+ markdown, markup, published) VALUES(%s, %s, %s, %s, %s, %s, %s)",
+ author.uid, slug, title, status, markdown, markup, published)
- def search(self, what):
- # Split tags.
- tags = what.split()
+ entry = self.get_entry_by_id(id)
- query = "SELECT planet.* FROM planet INNER JOIN ( \
- SELECT post_id FROM planet_tags \
- INNER JOIN planet ON planet_tags.post_id = planet.id \
- WHERE %s GROUP BY post_id HAVING COUNT(post_id) = %%s \
- ) pt ON planet.id = pt.post_id ORDER BY published DESC"
-
- args = (tags, len(tags))
+ if tags:
+ entry.tags = tags
- clauses, args = [], tags
- for tag in tags:
- clauses.append("planet_tags.tag = %s")
- args.append(len(tags))
+ return entry
- entries = self.db.query(query % " OR ".join(clauses), *args)
- return [PlanetEntry(self.db, e) for e in entries]
+ def update_entry(self, entry):
+ self.db.execute("UPDATE planet SET title = %s, markdown = %s WHERE id = %s",
+ entry.title, entry.markdown, entry.id)
- def search_autocomplete(self, what):
- tags = what.split()
- last_tag = tags.pop()
+ def save_entry(self, entry):
+ slug = self._generate_slug(entry.title)
- res = self.db.query("SELECT tag, COUNT(tag) AS count FROM planet_tags \
- WHERE tag LIKE %s GROUP BY tag ORDER BY count DESC", "%s%%" % last_tag)
+ id = self.db.execute("INSERT INTO planet(author_id, title, slug, markdown, published) \
+ VALUES(%s, %s, %s, %s, NOW())", entry.author.uid, entry.title, slug, entry.markdown)
- if tags:
- return ["%s %s" % (" ".join(tags), row.tag) for row in res]
+ return id
- return [row.tag for row in res]
+ def search(self, what):
+ res = self.db.query("WITH \
+ q AS (SELECT plainto_tsquery(%s, %s) AS query), \
+ ranked AS (SELECT id, query, ts_rank_cd(to_tsvector(%s, markdown), query) AS rank \
+ FROM planet, q WHERE markdown @@ query ORDER BY rank DESC) \
+ SELECT *, ts_headline(markup, ranked.query, 'MinWords=100, MaxWords=110') AS markup FROM planet \
+ JOIN ranked ON planet.id = ranked.id \
+ WHERE status = %s AND published IS NOT NULL AND published <= NOW() \
+ ORDER BY ranked DESC LIMIT 10",
+ "english", what, "english", "published")
+
+ return [PlanetEntry(self.backend, e) for e in res]