Mercurial > piecrust2
comparison piecrust/data/provider.py @ 3:f485ba500df3
Gigantic change to basically make PieCrust 2 vaguely functional.
- Serving works, with debug window.
- Baking works, multi-threading, with dependency handling.
- Various things not implemented yet.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Sun, 10 Aug 2014 23:43:16 -0700 |
parents | |
children | 474c9882decf |
comparison
equal
deleted
inserted
replaced
2:40fa08b261b9 | 3:f485ba500df3 |
---|---|
1 import time | |
2 import itertools | |
3 from piecrust.data.iterators import PageIterator | |
4 from piecrust.sources.base import ArraySource | |
5 | |
6 | |
7 class DataProvider(object): | |
8 debug_render_dynamic = ['_debugRenderUserData'] | |
9 debug_render_invoke_dynamic = ['_debugRenderUserData'] | |
10 | |
11 def __init__(self, source, page, user_data): | |
12 if source.app is not page.app: | |
13 raise Exception("The given source and page don't belong to " | |
14 "the same application.") | |
15 self._source = source | |
16 self._page = page | |
17 self._user_data = user_data | |
18 | |
19 def __getattr__(self, name): | |
20 if self._user_data is not None: | |
21 return self._user_data[name] | |
22 raise AttributeError() | |
23 | |
24 def __getitem__(self, name): | |
25 if self._user_data is not None: | |
26 return self._user_data[name] | |
27 raise KeyError() | |
28 | |
29 def _debugRenderUserData(self): | |
30 if self._user_data: | |
31 return self._user_data.keys() | |
32 return [] | |
33 | |
34 | |
35 class CompositeDataProvider(object): | |
36 def __init__(self, providers): | |
37 self._providers = providers | |
38 | |
39 def __getattr__(self, name): | |
40 for p in self._providers: | |
41 try: | |
42 return getattr(p, name) | |
43 except AttributeError: | |
44 pass | |
45 raise AttributeError() | |
46 | |
47 | |
48 class IteratorDataProvider(DataProvider): | |
49 PROVIDER_NAME = 'iterator' | |
50 | |
51 debug_render_doc = """Provides a list of pages.""" | |
52 | |
53 def __init__(self, source, page, user_data): | |
54 super(IteratorDataProvider, self).__init__(source, page, user_data) | |
55 self._pages = PageIterator(source, current_page=page) | |
56 self._pages._iter_event += self._onIteration | |
57 self._ctx_set = False | |
58 | |
59 def __len__(self): | |
60 return len(self._pages) | |
61 | |
62 def __getitem__(self, key): | |
63 return self._pages[key] | |
64 | |
65 def __iter__(self): | |
66 return iter(self._pages) | |
67 | |
68 def _onIteration(self): | |
69 if not self._ctx_set: | |
70 eis = self._page.app.env.exec_info_stack | |
71 eis.current_page_info.render_ctx.used_source_names.add( | |
72 self._source.name) | |
73 self._ctx_set = True | |
74 | |
75 | |
76 class BlogDataProvider(DataProvider): | |
77 PROVIDER_NAME = 'blog' | |
78 | |
79 debug_render_doc = """Provides a list of blog posts and yearly/monthly | |
80 archives.""" | |
81 debug_render = ['posts', 'years', 'months'] | |
82 debug_render_dynamic = (['_debugRenderTaxonomies'] + | |
83 DataProvider.debug_render_dynamic) | |
84 | |
85 def __init__(self, source, page, user_data): | |
86 super(BlogDataProvider, self).__init__(source, page, user_data) | |
87 self._yearly = None | |
88 self._monthly = None | |
89 self._taxonomies = {} | |
90 self._ctx_set = False | |
91 | |
92 def __getattr__(self, name): | |
93 if self._source.app.getTaxonomy(name) is not None: | |
94 return self._buildTaxonomy(name) | |
95 return super(BlogDataProvider, self).__getattr__(name) | |
96 | |
97 @property | |
98 def posts(self): | |
99 it = PageIterator(self._source, current_page=self._page) | |
100 it._iter_event += self._onIteration | |
101 return it | |
102 | |
103 @property | |
104 def years(self): | |
105 return self._buildYearlyArchive() | |
106 | |
107 @property | |
108 def months(self): | |
109 return self._buildMonthlyArchive() | |
110 | |
111 def _debugRenderTaxonomies(self): | |
112 return [t.name for t in self._source.app.taxonomies] | |
113 | |
114 def _buildYearlyArchive(self): | |
115 if self._yearly is not None: | |
116 return self._yearly | |
117 | |
118 self._yearly = [] | |
119 for fac in self._source.getPageFactories(): | |
120 post = fac.buildPage() | |
121 year = post.datetime.strftime('%Y') | |
122 | |
123 posts_this_year = next( | |
124 itertools.ifilter(lambda y: y.name == year, self._yearly), | |
125 None) | |
126 if posts_this_year is None: | |
127 timestamp = time.mktime( | |
128 (post.datetime.year, 1, 1, 0, 0, 0, 0, 0, -1)) | |
129 posts_this_year = BlogArchiveEntry(self._page, year, timestamp) | |
130 self._yearly.append(posts_this_year) | |
131 | |
132 posts_this_year._data_source.append(post) | |
133 self._yearly = sorted(self._yearly, | |
134 key=lambda e: e.timestamp, | |
135 reverse=True) | |
136 self._onIteration() | |
137 return self._yearly | |
138 | |
139 def _buildMonthlyArchive(self): | |
140 if self._monthly is not None: | |
141 return self._monthly | |
142 | |
143 self._monthly = [] | |
144 for fac in self._source.getPageFactories(): | |
145 post = fac.buildPage() | |
146 month = post.datetime.strftime('%B %Y') | |
147 | |
148 posts_this_month = next( | |
149 itertools.ifilter(lambda m: m.name == month, self._monthly), | |
150 None) | |
151 if posts_this_month is None: | |
152 timestamp = time.mktime( | |
153 (post.datetime.year, post.datetime.month, 1, | |
154 0, 0, 0, 0, 0, -1)) | |
155 posts_this_month = BlogArchiveEntry(self._page, month, timestamp) | |
156 self._monthly.append(posts_this_month) | |
157 | |
158 posts_this_month._data_source.append(post) | |
159 self._monthly = sorted(self._monthly, | |
160 key=lambda e: e.timestamp, | |
161 reverse=True) | |
162 self._onIteration() | |
163 return self._monthly | |
164 | |
165 def _buildTaxonomy(self, tax_name): | |
166 if tax_name in self._taxonomies: | |
167 return self._taxonomies[tax_name] | |
168 | |
169 posts_by_tax_value = {} | |
170 for fac in self._source.getPageFactories(): | |
171 post = fac.buildPage() | |
172 tax_values = post.config.get(tax_name) | |
173 if not isinstance(tax_values, list): | |
174 tax_values = [tax_values] | |
175 for val in tax_values: | |
176 posts_by_tax_value.setdefault(val, []) | |
177 posts_by_tax_value[val].append(post) | |
178 | |
179 entries = [] | |
180 for value, ds in posts_by_tax_value.iteritems(): | |
181 source = ArraySource(self._page.app, ds) | |
182 entries.append(BlogTaxonomyEntry(self._page, source, value)) | |
183 self._taxonomies[tax_name] = sorted(entries, key=lambda k: k.name) | |
184 | |
185 self._onIteration() | |
186 return self._taxonomies[tax_name] | |
187 | |
188 def _onIteration(self): | |
189 if not self._ctx_set: | |
190 eis = self._page.app.env.exec_info_stack | |
191 eis.current_page_info.render_ctx.used_source_names.add( | |
192 self._source.name) | |
193 self._ctx_set = True | |
194 | |
195 | |
196 class BlogArchiveEntry(object): | |
197 def __init__(self, page, name, timestamp): | |
198 self.name = name | |
199 self.timestamp = timestamp | |
200 self._page = page | |
201 self._data_source = [] | |
202 self._iterator = None | |
203 | |
204 def __str__(self): | |
205 return self.name | |
206 | |
207 @property | |
208 def posts(self): | |
209 self._load() | |
210 self._iterator.reset() | |
211 return self._iterator | |
212 | |
213 def _load(self): | |
214 if self._iterator is not None: | |
215 return | |
216 source = ArraySource(self._page.app, self._data_source) | |
217 self._iterator = PageIterator(source, current_page=self._page) | |
218 | |
219 | |
220 class BlogTaxonomyEntry(object): | |
221 def __init__(self, page, source, property_value): | |
222 self._page = page | |
223 self._source = source | |
224 self._property_value = property_value | |
225 self._iterator = None | |
226 | |
227 def __str__(self): | |
228 return self._property_value | |
229 | |
230 @property | |
231 def name(self): | |
232 return self._property_value | |
233 | |
234 @property | |
235 def posts(self): | |
236 self._load() | |
237 self._iterator.reset() | |
238 return self._iterator | |
239 | |
240 @property | |
241 def post_count(self): | |
242 return self._source.page_count | |
243 | |
244 def _load(self): | |
245 if self._iterator is not None: | |
246 return | |
247 | |
248 self._iterator = PageIterator(self._source, self._page) | |
249 |