Skip to content

Commit

Permalink
Merge pull request #67 from fluffy-critter/feature/view-names
Browse files Browse the repository at this point in the history
Add `view.range` property
  • Loading branch information
fluffy-critter committed May 10, 2018
2 parents a27c90a + 07c9d21 commit 6a6ce6a
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 38 deletions.
3 changes: 2 additions & 1 deletion publ/markdown.py
Expand Up @@ -100,7 +100,8 @@ def link(self, content, link, title=''):
}),
content)

def paragraph(self, content):
@staticmethod
def paragraph(content):
""" emit a paragraph, stripping out any leading or following empty paragraphs """
text = '<p>' + content + '</p>'
if text.startswith('<p></p>'):
Expand Down
126 changes: 89 additions & 37 deletions publ/view.py
Expand Up @@ -5,6 +5,7 @@

import arrow
import flask
from werkzeug.utils import cached_property

from . import model, utils, queries
from .entry import Entry
Expand All @@ -18,6 +19,8 @@
# overrides the count
PAGINATION_PRIORITY = ['date', 'count']

# All spec keys that indicate a pagination
PAGINATION_SPECS = OFFSET_PRIORITY + PAGINATION_PRIORITY

#: Ordering queries for different sort orders
ORDER_BY = {
Expand Down Expand Up @@ -49,7 +52,7 @@ def __init__(self, input_spec=None):
# filter out any priority override things
spec = {
k: v for k, v in input_spec.items()
if k not in [*OFFSET_PRIORITY, *PAGINATION_PRIORITY]
if k not in PAGINATION_SPECS
}

# pull in the first offset type that appears
Expand All @@ -68,6 +71,8 @@ def __init__(self, input_spec=None):
self._where = queries.build_query(spec)
self._query = model.Entry.select().where(self._where)

self.range = utils.CallableProxy(self._view_name)

if 'count' in spec:
self._query = self._query.limit(spec['count'])

Expand All @@ -84,10 +89,10 @@ def __init__(self, input_spec=None):
self.type = None

def _spec_filtered(self):
# Return a version of our spec where all pagination stuff has been
# Return a version of our spec where all pagination boundary constraints have been
# removed
return {k: v for k, v in self.spec.items()
if k not in [*OFFSET_PRIORITY, *PAGINATION_PRIORITY]}
if k not in OFFSET_PRIORITY}

def __str__(self):
return str(self._link())
Expand All @@ -111,50 +116,59 @@ def _link(self, template='', absolute=False):
category=self.spec.get('category'),
_external=absolute)

@property
@cached_property
def first(self):
""" Gets the first entry in the view """
return self.entries[0] if self.entries else None

@property
@cached_property
def last(self):
""" Gets the last entry in the view """
return self.entries[-1] if self.entries else None

def __getattr__(self, name):
""" Lazy evaluation of properties """
# pylint: disable=attribute-defined-outside-init

if name == 'entries':
self.entries = [Entry(e) for e in self._entries]
return self.entries

if name == 'last_modified':
# Get the most recent entry in the view
try:
latest = self._query.order_by(-model.Entry.entry_date)[0]
self.last_modified = arrow.get(Entry(latest).last_modified)
except IndexError:
self.last_modified = arrow.get()
return self.last_modified

if name == 'previous' or name == 'next':
self.previous, self.next = self._get_pagination()
return getattr(self, name)

if name == 'newest' or name == 'oldest':
if self._order_by == 'newest':
self.newest, self.oldest = self.first, self.last
elif self._order_by == 'oldest':
self.newest, self.oldest = self.last, self.first
else:
raise ValueError(
'newest/oldest not supported on sort type {}'.format(self._order_by))
return getattr(self, name)
@cached_property
def entries(self):
""" Gets the entries for the view """
return [Entry(e) for e in self._entries]

@cached_property
def last_modified(self):
""" Gets the most recent modification time for all entries in the view """
if self.entries:
latest = max(self.entries, key=lambda x: x.last_modified)
return arrow.get(latest.last_modified)
return arrow.get()

@cached_property
def previous(self):
""" Gets the previous page """
return self._pagination[0]

@cached_property
def next(self):
""" Gets the next page """
return self._pagination[1]

@cached_property
def newest(self):
""" Gets the newest entry in the view, regardless of sort order """
if self._order_by == 'newest':
return self.first
if self._order_by == 'oldest':
return self.last
return max(self.entries, key=lambda x: (x.date, x.id))

raise AttributeError("Unknown view attribute {}".format(name))
@cached_property
def oldest(self):
""" Gets the odlest entry in the view, regardless of sort order """
if self._order_by == 'newest':
return self.last
if self._order_by == 'oldest':
return self.first
return min(self.entries, key=lambda x: (x.date, -x.id))

def _get_pagination(self):
@cached_property
def _pagination(self):
""" Compute the next/previous pages from this view.
Returns a tuple of previous page, next page.
Expand Down Expand Up @@ -255,6 +269,44 @@ def _get_date_pagination(self, base, oldest_neighbor, newest_neighbor):
def __call__(self, **restrict):
return View({**self.spec, **restrict})

def _view_name(self, **formats):
if not any(k for k in PAGINATION_SPECS if k in self.spec):
# We don't have anything that specifies a pagination constraint, so
# we don't have a name
return None

if not self.oldest or not self.newest:
# We don't have any entries, so we don't have a name
return None

if 'date' in self.spec:
_, span_type, span_format = utils.parse_date(self.spec['date'])
elif self.oldest.date.year != self.newest.date.year:
span_type = 'year'
span_format = utils.YEAR_FORMAT
elif self.oldest.date.month != self.newest.date.month:
span_type = 'month'
span_format = utils.MONTH_FORMAT
else:
span_type = 'day'
span_format = utils.DAY_FORMAT

date_format = formats.get(span_type, span_format)

oldest = self.oldest.date.format(date_format)
if len(self.entries) == 1:
return oldest

newest = self.newest.date.format(date_format)

if oldest == newest:
template = formats.get('span', '{oldest} ({count})')
else:
template = formats.get(
'single', '{oldest} — {newest} ({count})')

return template.format(count=len(self.entries), oldest=oldest, newest=newest)


def get_view(**kwargs):
""" Wrapper function for constructing a view from scratch """
Expand Down

0 comments on commit 6a6ce6a

Please sign in to comment.