comparison piecrust/sources/blogarchives.py @ 852:4850f8c21b6e

core: Start of the big refactor for PieCrust 3.0. * Everything is a `ContentSource`, including assets directories. * Most content sources are subclasses of the base file-system source. * A source is processed by a "pipeline", and there are 2 built-in pipelines, one for assets and one for pages. The asset pipeline is vaguely functional, but the page pipeline is completely broken right now. * Rewrite the baking process as just running appropriate pipelines on each content item. This should allow for better parallelization.
author Ludovic Chabant <ludovic@chabant.com>
date Wed, 17 May 2017 00:11:48 -0700
parents
children f070a4fc033c
comparison
equal deleted inserted replaced
851:2c7e57d80bba 852:4850f8c21b6e
1 import logging
2 import datetime
3 from piecrust.chefutil import format_timed_scope
4 from piecrust.data.filters import PaginationFilter, IFilterClause
5 from piecrust.data.iterators import PageIterator
6 from piecrust.routing import RouteParameter
7 from piecrust.sources.base import ContentSource, GeneratedContentException
8
9
10 logger = logging.getLogger(__name__)
11
12
13 class BlogArchivesSource(ContentSource):
14 SOURCE_NAME = 'blog_archives'
15
16 def __init__(self, app, name, config):
17 super().__init__(app, name, config)
18
19 def getContents(self, group):
20 raise GeneratedContentException()
21
22 def prepareRenderContext(self, ctx):
23 ctx.pagination_source = self.source
24
25 year = ctx.page.route_metadata.get('year')
26 if year is None:
27 raise Exception(
28 "Can't find the archive year in the route metadata")
29 if type(year) is not int:
30 raise Exception(
31 "The route for generator '%s' should specify an integer "
32 "parameter for 'year'." % self.name)
33
34 flt = PaginationFilter()
35 flt.addClause(IsFromYearFilterClause(year))
36 ctx.pagination_filter = flt
37
38 ctx.custom_data['year'] = year
39
40 flt2 = PaginationFilter()
41 flt2.addClause(IsFromYearFilterClause(year))
42 it = PageIterator(self.source, pagination_filter=flt2,
43 sorter=_date_sorter)
44 ctx.custom_data['archives'] = it
45
46 def bake(self, ctx):
47 if not self.page_ref.exists:
48 logger.debug(
49 "No page found at '%s', skipping %s archives." %
50 (self.page_ref, self.source_name))
51 return
52
53 logger.debug("Baking %s archives...", self.source_name)
54 with format_timed_scope(logger, 'gathered archive years',
55 level=logging.DEBUG, colored=False):
56 all_years, dirty_years = self._buildDirtyYears(ctx)
57
58 with format_timed_scope(logger, "baked %d %s archives." %
59 (len(dirty_years), self.source_name)):
60 self._bakeDirtyYears(ctx, all_years, dirty_years)
61
62 def _getSource(self):
63 return self.app.getSource(self.config['source'])
64
65 def _buildDirtyYears(self, ctx):
66 logger.debug("Gathering dirty post years.")
67 all_years = set()
68 dirty_years = set()
69 for _, cur_entry in ctx.getAllPageRecords():
70 if cur_entry and cur_entry.source_name == self.source_name:
71 dt = datetime.datetime.fromtimestamp(cur_entry.timestamp)
72 all_years.add(dt.year)
73 if cur_entry.was_any_sub_baked:
74 dirty_years.add(dt.year)
75 return all_years, dirty_years
76
77 def _bakeDirtyYears(self, ctx, all_years, dirty_years):
78 route = self.app.getGeneratorRoute(self.name)
79 if route is None:
80 raise Exception(
81 "No routes have been defined for generator: %s" %
82 self.name)
83
84 logger.debug("Using archive page: %s" % self.page_ref)
85 fac = self.page_ref.getFactory()
86
87 for y in dirty_years:
88 extra_route_metadata = {'year': y}
89
90 logger.debug("Queuing: %s [%s]" % (fac.ref_spec, y))
91 ctx.queueBakeJob(fac, route, extra_route_metadata, str(y))
92 ctx.runJobQueue()
93
94 # Create bake entries for the years that were *not* dirty.
95 # Otherwise, when checking for deleted pages, we would not find any
96 # outputs and would delete those files.
97 all_str_years = [str(y) for y in all_years]
98 for prev_entry, cur_entry in ctx.getAllPageRecords():
99 if prev_entry and not cur_entry:
100 try:
101 y = ctx.getSeedFromRecordExtraKey(prev_entry.extra_key)
102 except InvalidRecordExtraKey:
103 continue
104 if y in all_str_years:
105 logger.debug(
106 "Creating unbaked entry for year %s archive." % y)
107 ctx.collapseRecord(prev_entry)
108 else:
109 logger.debug(
110 "No page references year %s anymore." % y)
111
112 def getSupportedRouteParameters(self):
113 return [RouteParameter('year', RouteParameter.TYPE_INT4)]
114
115
116 class IsFromYearFilterClause(IFilterClause):
117 def __init__(self, year):
118 self.year = year
119
120 def pageMatches(self, fil, page):
121 return (page.datetime.year == self.year)
122
123
124 def _date_sorter(it):
125 return sorted(it, key=lambda x: x.datetime)
126