/splinext/forum/frontpage_sources.py

https://github.com/veekun/spline · Python · 101 lines · 79 code · 14 blank · 8 comment · 6 complexity · fd97d4fb817bccc061e76c1f457038fc MD5 · raw file

  1. from collections import namedtuple
  2. import datetime
  3. from sqlalchemy.orm import contains_eager, joinedload
  4. from sqlalchemy.sql import func
  5. from pylons import tmpl_context as c, url
  6. from spline.model import meta
  7. from splinext.forum import model as forum_model
  8. from splinext.frontpage.sources import Source
  9. FrontPageThread = namedtuple('FrontPageThread', ['source', 'time', 'post'])
  10. class ForumSource(Source):
  11. """Represents a forum whose threads are put on the front page.
  12. ``link``, ``title``, and ``icon`` are all optional; the link and title
  13. default to the forum's thread list and name, and the icon defaults to a
  14. newspaper.
  15. Extra properties:
  16. ``forum_id``
  17. id of the forum to check for new threads.
  18. """
  19. template = '/forum/front_page.mako'
  20. def __init__(self, forum_id, **kwargs):
  21. forum = meta.Session.query(forum_model.Forum).get(forum_id)
  22. # Link is tricky. Needs url(), which doesn't exist when this class is
  23. # loaded. Lazy-load it in poll() below, instead
  24. kwargs.setdefault('link', None)
  25. kwargs.setdefault('title', forum.name)
  26. kwargs.setdefault('icon', 'newspapers')
  27. super(ForumSource, self).__init__(**kwargs)
  28. self.forum_id = forum_id
  29. def _poll(self, limit, max_age):
  30. if not self.link:
  31. self.link = url(
  32. controller='forum', action='threads', forum_id=self.forum_id)
  33. thread_q = meta.Session.query(forum_model.Thread) \
  34. .filter_by(forum_id=self.forum_id) \
  35. .join((forum_model.Post, forum_model.Thread.first_post)) \
  36. .options(
  37. contains_eager(forum_model.Thread.first_post, alias=forum_model.Post),
  38. contains_eager(forum_model.Thread.first_post, forum_model.Post.thread, alias=forum_model.Thread),
  39. joinedload(forum_model.Thread.first_post, forum_model.Post.author),
  40. )
  41. if max_age:
  42. thread_q = thread_q.filter(forum_model.Post.posted_time >= max_age)
  43. threads = thread_q \
  44. .order_by(forum_model.Post.posted_time.desc()) \
  45. [:limit]
  46. updates = []
  47. for thread in threads:
  48. update = FrontPageThread(
  49. source = self,
  50. time = thread.first_post.posted_time,
  51. post = thread.first_post,
  52. )
  53. updates.append(update)
  54. return updates
  55. FrontPageActivity = namedtuple('FrontPageActivity', ['template', 'threads'])
  56. def forum_activity(*args, **kwargs):
  57. """Show recently-active threads on the front page.
  58. Note that this isn't the most recent X threads; it's threads that are more
  59. recent than X, sorted by their activity since X.
  60. """
  61. # XXX this should be configurable probably
  62. cutoff = datetime.datetime.now() - datetime.timedelta(days=7)
  63. # TODO some sort of dropoff here idk
  64. active_threads_subq = meta.Session.query(
  65. forum_model.Post.thread_id.label('thread_id'),
  66. func.count('*').label('ranking'),
  67. ) \
  68. .filter(forum_model.Post.posted_time >= cutoff) \
  69. .group_by(forum_model.Post.thread_id) \
  70. .subquery()
  71. threads_q = meta.Session.query(forum_model.Thread) \
  72. .join((active_threads_subq,
  73. active_threads_subq.c.thread_id == forum_model.Thread.id)) \
  74. .order_by(active_threads_subq.c.ranking.desc()) \
  75. .limit(10)
  76. return FrontPageActivity(
  77. template='/forum/front_page_activity.mako',
  78. threads=threads_q.all(),
  79. )