Mercurial > piecrust2
comparison piecrust/dataproviders/blog.py @ 853:f070a4fc033c
core: Continue PieCrust3 refactor, simplify pages.
The asset pipeline is still the only function pipeline at this point.
* No more `QualifiedPage`, and several other pieces of code deleted.
* Data providers are simpler and more focused. For instance, the page iterator
doesn't try to support other types of items.
* Route parameters are proper known source metadata to remove the confusion
between the two.
* Make the baker and pipeline more correctly manage records and record
histories.
* Add support for record collapsing and deleting stale outputs in the asset
pipeline.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Sun, 21 May 2017 00:06:59 -0700 |
parents | |
children | 08e02c2a2a1a |
comparison
equal
deleted
inserted
replaced
852:4850f8c21b6e | 853:f070a4fc033c |
---|---|
1 import time | |
2 import collections.abc | |
3 from piecrust.dataproviders.base import DataProvider | |
4 from piecrust.generation.taxonomy import Taxonomy | |
5 | |
6 | |
7 class BlogDataProvider(DataProvider, collections.abc.Mapping): | |
8 PROVIDER_NAME = 'blog' | |
9 | |
10 debug_render_doc = """Provides a list of blog posts and yearly/monthly | |
11 archives.""" | |
12 debug_render_dynamic = (['_debugRenderTaxonomies'] + | |
13 DataProvider.debug_render_dynamic) | |
14 | |
15 def __init__(self, source, page, override): | |
16 super(BlogDataProvider, self).__init__(source, page, override) | |
17 self._yearly = None | |
18 self._monthly = None | |
19 self._taxonomies = {} | |
20 self._ctx_set = False | |
21 | |
22 @property | |
23 def posts(self): | |
24 return self._posts() | |
25 | |
26 @property | |
27 def years(self): | |
28 return self._buildYearlyArchive() | |
29 | |
30 @property | |
31 def months(self): | |
32 return self._buildMonthlyArchive() | |
33 | |
34 def __getitem__(self, name): | |
35 if name == 'posts': | |
36 return self._posts() | |
37 elif name == 'years': | |
38 return self._buildYearlyArchive() | |
39 elif name == 'months': | |
40 return self._buildMonthlyArchive() | |
41 | |
42 if self._source.app.config.get('site/taxonomies/' + name) is not None: | |
43 return self._buildTaxonomy(name) | |
44 | |
45 raise KeyError("No such item: %s" % name) | |
46 | |
47 def __iter__(self): | |
48 keys = ['posts', 'years', 'months'] | |
49 keys += list(self._source.app.config.get('site/taxonomies').keys()) | |
50 return iter(keys) | |
51 | |
52 def __len__(self): | |
53 return 3 + len(self._source.app.config.get('site/taxonomies')) | |
54 | |
55 def _debugRenderTaxonomies(self): | |
56 return list(self._source.app.config.get('site/taxonomies').keys()) | |
57 | |
58 def _posts(self): | |
59 it = PageIterator(self._source, current_page=self._page) | |
60 it._iter_event += self._onIteration | |
61 return it | |
62 | |
63 def _buildYearlyArchive(self): | |
64 if self._yearly is not None: | |
65 return self._yearly | |
66 | |
67 self._yearly = [] | |
68 yearly_index = {} | |
69 for post in self._source.getPages(): | |
70 year = post.datetime.strftime('%Y') | |
71 | |
72 posts_this_year = yearly_index.get(year) | |
73 if posts_this_year is None: | |
74 timestamp = time.mktime( | |
75 (post.datetime.year, 1, 1, 0, 0, 0, 0, 0, -1)) | |
76 posts_this_year = BlogArchiveEntry(self._page, year, timestamp) | |
77 self._yearly.append(posts_this_year) | |
78 yearly_index[year] = posts_this_year | |
79 | |
80 posts_this_year._data_source.append(post) | |
81 self._yearly = sorted(self._yearly, | |
82 key=lambda e: e.timestamp, | |
83 reverse=True) | |
84 self._onIteration() | |
85 return self._yearly | |
86 | |
87 def _buildMonthlyArchive(self): | |
88 if self._monthly is not None: | |
89 return self._monthly | |
90 | |
91 self._monthly = [] | |
92 for post in self._source.getPages(): | |
93 month = post.datetime.strftime('%B %Y') | |
94 | |
95 posts_this_month = next( | |
96 filter(lambda m: m.name == month, self._monthly), | |
97 None) | |
98 if posts_this_month is None: | |
99 timestamp = time.mktime( | |
100 (post.datetime.year, post.datetime.month, 1, | |
101 0, 0, 0, 0, 0, -1)) | |
102 posts_this_month = BlogArchiveEntry(self._page, month, timestamp) | |
103 self._monthly.append(posts_this_month) | |
104 | |
105 posts_this_month._data_source.append(post) | |
106 self._monthly = sorted(self._monthly, | |
107 key=lambda e: e.timestamp, | |
108 reverse=True) | |
109 self._onIteration() | |
110 return self._monthly | |
111 | |
112 def _buildTaxonomy(self, tax_name): | |
113 if tax_name in self._taxonomies: | |
114 return self._taxonomies[tax_name] | |
115 | |
116 tax_cfg = self._page.app.config.get('site/taxonomies/' + tax_name) | |
117 tax = Taxonomy(tax_name, tax_cfg) | |
118 | |
119 posts_by_tax_value = {} | |
120 for post in self._source.getPages(): | |
121 tax_values = post.config.get(tax.setting_name) | |
122 if tax_values is None: | |
123 continue | |
124 if not isinstance(tax_values, list): | |
125 tax_values = [tax_values] | |
126 for val in tax_values: | |
127 posts = posts_by_tax_value.setdefault(val, []) | |
128 posts.append(post) | |
129 | |
130 entries = [] | |
131 for value, ds in posts_by_tax_value.items(): | |
132 source = ArraySource(self._page.app, ds) | |
133 entries.append(BlogTaxonomyEntry(self._page, source, value)) | |
134 self._taxonomies[tax_name] = sorted(entries, key=lambda k: k.name) | |
135 | |
136 self._onIteration() | |
137 return self._taxonomies[tax_name] | |
138 | |
139 def _onIteration(self): | |
140 if not self._ctx_set: | |
141 eis = self._page.app.env.exec_info_stack | |
142 if eis.current_page_info: | |
143 eis.current_page_info.render_ctx.addUsedSource(self._source) | |
144 self._ctx_set = True | |
145 | |
146 | |
147 class BlogArchiveEntry(object): | |
148 debug_render = ['name', 'timestamp', 'posts'] | |
149 debug_render_invoke = ['name', 'timestamp', 'posts'] | |
150 | |
151 def __init__(self, page, name, timestamp): | |
152 self.name = name | |
153 self.timestamp = timestamp | |
154 self._page = page | |
155 self._data_source = [] | |
156 self._iterator = None | |
157 | |
158 def __str__(self): | |
159 return self.name | |
160 | |
161 def __int__(self): | |
162 return int(self.name) | |
163 | |
164 @property | |
165 def posts(self): | |
166 self._load() | |
167 self._iterator.reset() | |
168 return self._iterator | |
169 | |
170 def _load(self): | |
171 if self._iterator is not None: | |
172 return | |
173 source = ArraySource(self._page.app, self._data_source) | |
174 self._iterator = PageIterator(source, current_page=self._page) | |
175 | |
176 | |
177 class BlogTaxonomyEntry(object): | |
178 debug_render = ['name', 'post_count', 'posts'] | |
179 debug_render_invoke = ['name', 'post_count', 'posts'] | |
180 | |
181 def __init__(self, page, source, property_value): | |
182 self._page = page | |
183 self._source = source | |
184 self._property_value = property_value | |
185 self._iterator = None | |
186 | |
187 def __str__(self): | |
188 return self._property_value | |
189 | |
190 @property | |
191 def name(self): | |
192 return self._property_value | |
193 | |
194 @property | |
195 def posts(self): | |
196 self._load() | |
197 self._iterator.reset() | |
198 return self._iterator | |
199 | |
200 @property | |
201 def post_count(self): | |
202 return self._source.page_count | |
203 | |
204 def _load(self): | |
205 if self._iterator is not None: | |
206 return | |
207 | |
208 self._iterator = PageIterator(self._source, current_page=self._page) | |
209 |