Mercurial > piecrust2
comparison piecrust/sources/posts.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 os | |
2 import os.path | |
3 import re | |
4 import glob | |
5 import logging | |
6 import datetime | |
7 from piecrust import CONTENT_DIR | |
8 from piecrust.sources.base import (PageSource, IPreparingSource, | |
9 PageNotFoundError, InvalidFileSystemEndpointError, | |
10 PageFactory, MODE_CREATING) | |
11 | |
12 | |
13 logger = logging.getLogger(__name__) | |
14 | |
15 | |
16 class PostsSource(PageSource, IPreparingSource): | |
17 PATH_FORMAT = None | |
18 | |
19 def __init__(self, app, name, config): | |
20 super(PostsSource, self).__init__(app, name, config) | |
21 self.fs_endpoint = config.get('fs_endpoint', name) | |
22 self.fs_endpoint_path = os.path.join(self.root_dir, CONTENT_DIR, self.fs_endpoint) | |
23 self.supported_extensions = app.config.get('site/auto_formats').keys() | |
24 | |
25 @property | |
26 def path_format(self): | |
27 return self.__class__.PATH_FORMAT | |
28 | |
29 def resolveRef(self, ref_path): | |
30 return os.path.join(self.fs_endpoint_path, ref_path) | |
31 | |
32 def findPagePath(self, metadata, mode): | |
33 year = metadata.get('year') | |
34 month = metadata.get('month') | |
35 day = metadata.get('day') | |
36 slug = metadata.get('slug') | |
37 | |
38 ext = metadata.get('ext') | |
39 if ext is None: | |
40 if len(self.supported_extensions) == 1: | |
41 ext = self.supported_extensions[0] | |
42 | |
43 replacements = { | |
44 'year': year, | |
45 'month': month, | |
46 'day': day, | |
47 'slug': slug, | |
48 'ext': ext | |
49 } | |
50 needs_recapture = False | |
51 if year is None: | |
52 needs_recapture = True | |
53 replacements['year'] = '????' | |
54 if month is None: | |
55 needs_recapture = True | |
56 replacements['month'] = '??' | |
57 if day is None: | |
58 needs_recapture = True | |
59 replacements['day'] = '??' | |
60 if slug is None: | |
61 needs_recapture = True | |
62 replacements['slug'] = '*' | |
63 if ext is None: | |
64 needs_recapture = True | |
65 replacements['ext'] = '*' | |
66 path = os.path.join(self.fs_endpoint_path, self.path_format % replacements) | |
67 | |
68 if needs_recapture: | |
69 if mode == MODE_CREATING: | |
70 raise ValueError("Not enough information to find a post path.") | |
71 possible_paths = glob.glob(path) | |
72 if len(possible_paths) != 1: | |
73 raise PageNotFoundError() | |
74 path = possible_paths[0] | |
75 elif not os.path.isfile(path): | |
76 raise PageNotFoundError() | |
77 | |
78 regex_repl = { | |
79 'year': '(?P<year>\d{4})', | |
80 'month': '(?P<month>\d{2})', | |
81 'day': '(?P<day>\d{2})', | |
82 'slug': '(?P<slug>.*)', | |
83 'ext': '(?P<ext>.*)' | |
84 } | |
85 pattern = os.path.join(self.fs_endpoint_path, self.path_format) % regex_repl | |
86 m = re.match(pattern, path) | |
87 if not m: | |
88 raise Exception("Expected to be able to match path with path " | |
89 "format: %s" % path) | |
90 fac_metadata = { | |
91 'year': m.group('year'), | |
92 'month': m.group('month'), | |
93 'day': m.group('day'), | |
94 'slug': m.group('slug') | |
95 } | |
96 | |
97 return path, fac_metadata | |
98 | |
99 def setupPrepareParser(self, parser, app): | |
100 parser.add_argument('-d', '--date', help="The date of the post, " | |
101 "in `year/month/day` format (defaults to today).") | |
102 parser.add_argument('slug', help="The URL slug for the new post.") | |
103 | |
104 def buildMetadata(self, args): | |
105 today = datetime.date.today() | |
106 year, month, day = today.year, today.month, today.day | |
107 if args.date: | |
108 year, month, day = filter( | |
109 lambda s: int(s), | |
110 args.date.split('/')) | |
111 return {'year': year, 'month': month, 'day': day, 'slug': args.slug} | |
112 | |
113 def _checkFsEndpointPath(self): | |
114 if not os.path.isdir(self.fs_endpoint_path): | |
115 raise InvalidFileSystemEndpointError(self.name, self.fs_endpoint_path) | |
116 | |
117 def _makeFactory(self, path, slug, year, month, day): | |
118 timestamp = datetime.date(year, month, day) | |
119 metadata = { | |
120 'slug': slug, | |
121 'year': year, | |
122 'month': month, | |
123 'day': day, | |
124 'date': timestamp} | |
125 return PageFactory(self, path, metadata) | |
126 | |
127 | |
128 class FlatPostsSource(PostsSource): | |
129 SOURCE_NAME = 'posts/flat' | |
130 PATH_FORMAT = '%(year)s-%(month)s-%(day)s_%(slug)s.%(ext)s' | |
131 | |
132 def __init__(self, app, name, config): | |
133 super(FlatPostsSource, self).__init__(app, name, config) | |
134 | |
135 def buildPageFactories(self): | |
136 logger.debug("Scanning for posts (flat) in: %s" % self.fs_endpoint_path) | |
137 pattern = re.compile(r'(\d{4})-(\d{2})-(\d{2})_(.*)\.(\w+)$') | |
138 _, __, filenames = next(os.walk(self.fs_endpoint_path)) | |
139 for f in filenames: | |
140 match = pattern.match(f) | |
141 if match is None: | |
142 name, ext = os.path.splitext(f) | |
143 logger.warning("'%s' is not formatted as 'YYYY-MM-DD_slug-title.%s' " | |
144 "and will be ignored. Is that a typo?" % (f, ext)) | |
145 continue | |
146 yield self._makeFactory( | |
147 f, | |
148 match.group(4), | |
149 int(match.group(1)), | |
150 int(match.group(2)), | |
151 int(match.group(3))) | |
152 | |
153 | |
154 class ShallowPostsSource(PostsSource): | |
155 SOURCE_NAME = 'posts/shallow' | |
156 PATH_FORMAT = '%(year)s/%(month)s-%(day)s_%(slug)s.%(ext)s' | |
157 | |
158 def __init__(self, app, name, config): | |
159 super(ShallowPostsSource, self).__init__(app, name, config) | |
160 | |
161 def buildPageFactories(self): | |
162 logger.debug("Scanning for posts (shallow) in: %s" % self.fs_endpoint_path) | |
163 year_pattern = re.compile(r'(\d{4})$') | |
164 file_pattern = re.compile(r'(\d{2})-(\d{2})_(.*)\.(\w+)$') | |
165 _, year_dirs, __ = next(os.walk(self.fs_endpoint_path)) | |
166 year_dirs = filter(lambda d: year_pattern.match(d), year_dirs) | |
167 for yd in year_dirs: | |
168 if year_pattern.match(yd) is None: | |
169 logger.warning("'%s' is not formatted as 'YYYY' and will be ignored. " | |
170 "Is that a typo?") | |
171 continue | |
172 year = int(yd) | |
173 year_dir = os.path.join(self.fs_endpoint_path, yd) | |
174 | |
175 _, __, filenames = os.walk(year_dir) | |
176 for f in filenames: | |
177 match = file_pattern.match(f) | |
178 if match is None: | |
179 name, ext = os.path.splitext(f) | |
180 logger.warning("'%s' is not formatted as 'MM-DD_slug-title.%s' " | |
181 "and will be ignored. Is that a typo?" % (f, ext)) | |
182 continue | |
183 yield self._makeFactory( | |
184 os.path.join(yd, f), | |
185 match.group(3), | |
186 year, | |
187 int(match.group(1)), | |
188 int(match.group(2))) | |
189 | |
190 | |
191 class HierarchyPostsSource(PostsSource): | |
192 SOURCE_NAME = 'posts/hierarchy' | |
193 PATH_FORMAT = '%(year)s/%(month)s/%(day)s_%(slug)s.%(ext)s' | |
194 | |
195 def __init__(self, app, name, config): | |
196 super(HierarchyPostsSource, self).__init__(app, name, config) | |
197 | |
198 def buildPageFactories(self): | |
199 logger.debug("Scanning for posts (hierarchy) in: %s" % self.fs_endpoint_path) | |
200 year_pattern = re.compile(r'(\d{4})$') | |
201 month_pattern = re.compile(r'(\d{2})$') | |
202 file_pattern = re.compile(r'(\d{2})_(.*)\.(\w+)$') | |
203 _, year_dirs, __ = next(os.walk(self.fs_endpoint_path)) | |
204 year_dirs = filter(lambda d: year_pattern.match(d), year_dirs) | |
205 for yd in year_dirs: | |
206 year = int(yd) | |
207 year_dir = os.path.join(self.fs_endpoint_path, yd) | |
208 | |
209 _, month_dirs, __ = next(os.walk(year_dir)) | |
210 month_dirs = filter(lambda d: month_pattern.match(d), month_dirs) | |
211 for md in month_dirs: | |
212 month = int(md) | |
213 month_dir = os.path.join(year_dir, md) | |
214 | |
215 _, __, filenames = next(os.walk(month_dir)) | |
216 for f in filenames: | |
217 match = file_pattern.match(f) | |
218 if match is None: | |
219 name, ext = os.path.splitext(f) | |
220 logger.warning("'%s' is not formatted as 'DD_slug-title.%s' " | |
221 "and will be ignored. Is that a typo?" % (f, ext)) | |
222 continue | |
223 rel_name = os.path.join(yd, md, f) | |
224 yield self._makeFactory( | |
225 rel_name, | |
226 match.group(2), | |
227 year, | |
228 month, | |
229 int(match.group(1))) | |
230 |