From dc2f45208ff306926c99dffa55b78089ef85c476 Mon Sep 17 00:00:00 2001 From: Jacob Mason Date: Thu, 15 Jul 2010 13:25:12 -0500 Subject: [PATCH] separate sqlalchemystorage from __init__ --- doc/web/searchadapters.rst | 47 +++++++ doc/web/storagebackends.rst | 47 +++++++ doc/websupport.rst | 3 +- sphinx/websupport/__init__.py | 14 +- sphinx/websupport/comments/__init__.py | 123 +---------------- .../websupport/comments/sqlalchemystorage.py | 125 ++++++++++++++++++ 6 files changed, 229 insertions(+), 130 deletions(-) create mode 100644 doc/web/searchadapters.rst create mode 100644 doc/web/storagebackends.rst create mode 100644 sphinx/websupport/comments/sqlalchemystorage.py diff --git a/doc/web/searchadapters.rst b/doc/web/searchadapters.rst new file mode 100644 index 000000000..83e928baa --- /dev/null +++ b/doc/web/searchadapters.rst @@ -0,0 +1,47 @@ +.. _searchadapters: + +.. currentmodule:: sphinx.websupport.search + +Search Adapters +=============== + +To create a custom search adapter you will need to subclass the +:class:`~BaseSearch` class. Then create an instance of the new class +and pass that as the `search` keyword argument when you create the +:class:`~sphinx.websupport.WebSupport` object:: + + support = Websupport(srcdir=srcdir, + outdir=outdir, + search=MySearch()) + +For more information about creating a custom search adapter, please see +the documentation of the :class:`BaseSearch` class below. + +.. class:: BaseSearch + + Defines an interface for search adapters. + +BaseSearch Methods +~~~~~~~~~~~~~~~~~~ + + The following methods are defined in the BaseSearch class. Some methods + do not need to be overridden, but some ( + :meth:`~sphinx.websupport.search.BaseSearch.add_document` and + :meth:`~sphinx.websupport.search.BaseSearch.handle_query`) must be + overridden in your subclass. For a working example, look at the + built-in adapter for whoosh. + +.. automethod:: sphinx.websupport.search.BaseSearch.init_indexing + +.. automethod:: sphinx.websupport.search.BaseSearch.finish_indexing + +.. automethod:: sphinx.websupport.search.BaseSearch.feed + +.. automethod:: sphinx.websupport.search.BaseSearch.add_document + +.. automethod:: sphinx.websupport.search.BaseSearch.query + +.. automethod:: sphinx.websupport.search.BaseSearch.handle_query + +.. automethod:: sphinx.websupport.search.BaseSearch.extract_context + diff --git a/doc/web/storagebackends.rst b/doc/web/storagebackends.rst new file mode 100644 index 000000000..83e928baa --- /dev/null +++ b/doc/web/storagebackends.rst @@ -0,0 +1,47 @@ +.. _searchadapters: + +.. currentmodule:: sphinx.websupport.search + +Search Adapters +=============== + +To create a custom search adapter you will need to subclass the +:class:`~BaseSearch` class. Then create an instance of the new class +and pass that as the `search` keyword argument when you create the +:class:`~sphinx.websupport.WebSupport` object:: + + support = Websupport(srcdir=srcdir, + outdir=outdir, + search=MySearch()) + +For more information about creating a custom search adapter, please see +the documentation of the :class:`BaseSearch` class below. + +.. class:: BaseSearch + + Defines an interface for search adapters. + +BaseSearch Methods +~~~~~~~~~~~~~~~~~~ + + The following methods are defined in the BaseSearch class. Some methods + do not need to be overridden, but some ( + :meth:`~sphinx.websupport.search.BaseSearch.add_document` and + :meth:`~sphinx.websupport.search.BaseSearch.handle_query`) must be + overridden in your subclass. For a working example, look at the + built-in adapter for whoosh. + +.. automethod:: sphinx.websupport.search.BaseSearch.init_indexing + +.. automethod:: sphinx.websupport.search.BaseSearch.finish_indexing + +.. automethod:: sphinx.websupport.search.BaseSearch.feed + +.. automethod:: sphinx.websupport.search.BaseSearch.add_document + +.. automethod:: sphinx.websupport.search.BaseSearch.query + +.. automethod:: sphinx.websupport.search.BaseSearch.handle_query + +.. automethod:: sphinx.websupport.search.BaseSearch.extract_context + diff --git a/doc/websupport.rst b/doc/websupport.rst index 1b6725df1..c7833e7ab 100644 --- a/doc/websupport.rst +++ b/doc/websupport.rst @@ -12,4 +12,5 @@ into your web application. To learn more read the web/quickstart web/api web/frontend - web/searchadapters \ No newline at end of file + web/searchadapters + web/storagebackends \ No newline at end of file diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 407cb4c94..c60c93505 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -18,7 +18,7 @@ from jinja2 import Environment, FileSystemLoader from sphinx.application import Sphinx from sphinx.util.osutil import ensuredir from sphinx.websupport.search import BaseSearch, search_adapters -from sphinx.websupport import comments as sphinxcomments +from sphinx.websupport.comments import StorageBackend class WebSupportApp(Sphinx): def __init__(self, *args, **kwargs): @@ -46,17 +46,18 @@ class WebSupport(object): self._init_comments(comments) def _init_comments(self, comments): - if isinstance(comments, sphinxcomments.CommentBackend): + if isinstance(comments, StorageBackend): self.comments = comments else: - # If a CommentBackend isn't provided, use the default + # If a StorageBackend isn't provided, use the default # SQLAlchemy backend with an SQLite db. - from sphinx.websupport.comments import SQLAlchemyComments + from sphinx.websupport.comments.sqlalchemystorage \ + import SQLAlchemyStorage from sqlalchemy import create_engine db_path = path.join(self.outdir, 'comments', 'comments.db') ensuredir(path.dirname(db_path)) engine = create_engine('sqlite:///%s' % db_path) - self.comments = SQLAlchemyComments(engine) + self.comments = SQLAlchemyStorage(engine) def _init_templating(self): import sphinx @@ -93,8 +94,7 @@ class WebSupport(object): self.outdir, doctreedir, 'websupport', search=self.search, comments=self.comments) - # TODO: - # Hook comments into Sphinx signals. + self.comments.pre_build() app.build() self.comments.post_build() diff --git a/sphinx/websupport/comments/__init__.py b/sphinx/websupport/comments/__init__.py index 395bde580..66b9012af 100644 --- a/sphinx/websupport/comments/__init__.py +++ b/sphinx/websupport/comments/__init__.py @@ -1,12 +1,5 @@ -from datetime import datetime -from sqlalchemy.orm import sessionmaker - -from sphinx.websupport.comments.db import Base, Node, Comment, Vote - -Session = sessionmaker() - -class CommentBackend(object): +class StorageBackend(object): def pre_build(self): pass @@ -22,117 +15,3 @@ class CommentBackend(object): def get_comments(self, parent_id): raise NotImplemented - - -class SQLAlchemyComments(CommentBackend): - def __init__(self, engine): - self.engine = engine - Base.metadata.bind = engine - Base.metadata.create_all() - Session.configure(bind=engine) - self.session = Session() - - def pre_build(self): - self.current_pk = None - - def add_node(self, document, line, source, treeloc): - node = Node(document, line, source, treeloc) - self.session.add(node) - if self.current_pk is None: - self.session.commit() - self.current_pk = node.id - else: - self.current_pk += 1 - return self.current_pk - - def post_build(self): - self.session.commit() - - def add_comment(self, parent_id, text, displayed, - username, rating, time): - time = time or datetime.now() - - id = parent_id[1:] - if parent_id[0] == 's': - node = self.session.query(Node).filter(Node.id == id).first() - comment = Comment(text, displayed, username, rating, - time, node=node) - elif parent_id[0] == 'c': - parent = self.session.query(Comment).filter(Comment.id == id).first() - comment = Comment(text, displayed, username, rating, - time, parent=parent) - - self.session.add(comment) - self.session.commit() - return self.serializable(comment) - - def get_comments(self, parent_id, user_id): - parent_id = parent_id[1:] - node = self.session.query(Node).filter(Node.id == parent_id).first() - comments = [] - for comment in node.comments: - comments.append(self.serializable(comment, user_id)) - - return comments - - def process_vote(self, comment_id, user_id, value): - vote = self.session.query(Vote).filter( - Vote.comment_id == comment_id).filter( - Vote.user_id == user_id).first() - - comment = self.session.query(Comment).filter( - Comment.id == comment_id).first() - - if vote is None: - vote = Vote(comment_id, user_id, value) - comment.rating += value - else: - comment.rating += value - vote.value - vote.value = value - self.session.add(vote) - self.session.commit() - - def serializable(self, comment, user_id=None): - delta = datetime.now() - comment.time - - time = {'year': comment.time.year, - 'month': comment.time.month, - 'day': comment.time.day, - 'hour': comment.time.hour, - 'minute': comment.time.minute, - 'second': comment.time.second, - 'iso': comment.time.isoformat(), - 'delta': self.pretty_delta(delta)} - - vote = '' - if user_id is not None: - vote = self.session.query(Vote).filter( - Vote.comment_id == comment.id).filter( - Vote.user_id == user_id).first() - if vote is not None: - vote = vote.value - - return {'text': comment.text, - 'username': comment.username or 'Anonymous', - 'id': comment.id, - 'rating': comment.rating, - 'age': delta.seconds, - 'time': time, - 'vote': vote or 0, - 'node': comment.node.id if comment.node else None, - 'parent': comment.parent.id if comment.parent else None, - 'children': [self.serializable(child, user_id) - for child in comment.children]} - - def pretty_delta(self, delta): - days = delta.days - seconds = delta.seconds - hours = seconds / 3600 - minutes = seconds / 60 - - if days == 0: - dt = (minutes, 'minute') if hours == 0 else (hours, 'hour') - else: - dt = (days, 'day') - - return '%s %s ago' % dt if dt[0] == 1 else '%s %ss ago' % dt diff --git a/sphinx/websupport/comments/sqlalchemystorage.py b/sphinx/websupport/comments/sqlalchemystorage.py new file mode 100644 index 000000000..31403c0ab --- /dev/null +++ b/sphinx/websupport/comments/sqlalchemystorage.py @@ -0,0 +1,125 @@ +from datetime import datetime + +from sqlalchemy.orm import sessionmaker + +from sphinx.websupport.comments import StorageBackend +from sphinx.websupport.comments.db import Base, Node, Comment, Vote + +Session = sessionmaker() + +class SQLAlchemyStorage(StorageBackend): + def __init__(self, engine): + self.engine = engine + Base.metadata.bind = engine + Base.metadata.create_all() + Session.configure(bind=engine) + + def pre_build(self): + self.build_session = Session() + + def add_node(self, document, line, source, treeloc): + node = Node(document, line, source, treeloc) + self.build_session.add(node) + self.build_session.flush() + return node.id + + def post_build(self): + self.build_session.commit() + self.build_session.close() + + def add_comment(self, parent_id, text, displayed, + username, rating, time): + time = time or datetime.now() + + session = Session() + + id = parent_id[1:] + if parent_id[0] == 's': + node = session.query(Node).filter(Node.id == id).first() + comment = Comment(text, displayed, username, rating, + time, node=node) + elif parent_id[0] == 'c': + parent = session.query(Comment).filter(Comment.id == id).first() + comment = Comment(text, displayed, username, rating, + time, parent=parent) + + session.add(comment) + session.commit() + comment = self.serializable(session, comment) + session.close() + return comment + + def get_comments(self, parent_id, user_id): + parent_id = parent_id[1:] + session = Session() + node = session.query(Node).filter(Node.id == parent_id).first() + comments = [] + for comment in node.comments: + comments.append(self.serializable(session, comment, user_id)) + + session.close() + return comments + + def process_vote(self, comment_id, user_id, value): + session = Session() + vote = session.query(Vote).filter( + Vote.comment_id == comment_id).filter( + Vote.user_id == user_id).first() + + comment = session.query(Comment).filter( + Comment.id == comment_id).first() + + if vote is None: + vote = Vote(comment_id, user_id, value) + comment.rating += value + else: + comment.rating += value - vote.value + vote.value = value + session.add(vote) + session.commit() + session.close() + + def serializable(self, session, comment, user_id=None): + delta = datetime.now() - comment.time + + time = {'year': comment.time.year, + 'month': comment.time.month, + 'day': comment.time.day, + 'hour': comment.time.hour, + 'minute': comment.time.minute, + 'second': comment.time.second, + 'iso': comment.time.isoformat(), + 'delta': self.pretty_delta(delta)} + + vote = '' + if user_id is not None: + vote = session.query(Vote).filter( + Vote.comment_id == comment.id).filter( + Vote.user_id == user_id).first() + if vote is not None: + vote = vote.value + + return {'text': comment.text, + 'username': comment.username or 'Anonymous', + 'id': comment.id, + 'rating': comment.rating, + 'age': delta.seconds, + 'time': time, + 'vote': vote or 0, + 'node': comment.node.id if comment.node else None, + 'parent': comment.parent.id if comment.parent else None, + 'children': [self.serializable(session, child, user_id) + for child in comment.children]} + + def pretty_delta(self, delta): + days = delta.days + seconds = delta.seconds + hours = seconds / 3600 + minutes = seconds / 60 + + if days == 0: + dt = (minutes, 'minute') if hours == 0 else (hours, 'hour') + else: + dt = (days, 'day') + + return '%s %s ago' % dt if dt[0] == 1 else '%s %ss ago' % dt