comparison piecrust/page.py @ 142:0bdd938d6b9f

Better date/time handling for pages: - Date can be specified in the page config. - Fall back to timestamp 0 (which is fixed) instead of the timestamp of the file (which could vary). Sources can return additional config values to merge with the page's YAML front matter.
author Ludovic Chabant <ludovic@chabant.com>
date Sat, 29 Nov 2014 18:33:29 -0800
parents 28444014ce7d
children ab6e7e0e9d44
comparison
equal deleted inserted replaced
141:666ee97e77a9 142:0bdd938d6b9f
77 return self._raw_content 77 return self._raw_content
78 78
79 @property 79 @property
80 def datetime(self): 80 def datetime(self):
81 if self._datetime is None: 81 if self._datetime is None:
82 if 'datetime' in self.source_metadata: 82 try:
83 self._datetime = self.source_metadata['datetime'] 83 if 'datetime' in self.source_metadata:
84 elif 'date' in self.source_metadata: 84 # Get the date/time from the source.
85 page_date = self.source_metadata['date'] 85 self._datetime = self.source_metadata['datetime']
86 page_time = self.config.get('time') 86 elif 'date' in self.source_metadata:
87 if page_time is not None: 87 # Get the date from the source. Potentially get the
88 if isinstance(page_time, str): 88 # time from the page config.
89 # Need to parse it... 89 page_date = self.source_metadata['date']
90 try: 90 page_time = _parse_config_time(self.config.get('time'))
91 parsed_t = dateutil.parser.parse(page_time) 91 if page_time is not None:
92 except Exception as ex:
93 raise ConfigurationError(
94 "Invalid time '%s' in page: %s" %
95 (page_time, self.path)) from ex
96 page_time = datetime.timedelta(
97 hours=parsed_t.hour,
98 minutes=parsed_t.minute,
99 seconds=parsed_t.second)
100
101 elif isinstance(page_time, int):
102 # Total seconds... convert to a time struct.
103 page_time = datetime.timedelta(seconds=page_time)
104
105 elif not isinstance(page_time, datetime.timedelta):
106 raise ConfigurationError(
107 "Invalid time '%s' in page: %s" %
108 (page_time, self.path))
109
110 try:
111 self._datetime = datetime.datetime( 92 self._datetime = datetime.datetime(
112 page_date.year, 93 page_date.year,
113 page_date.month, 94 page_date.month,
114 page_date.day) + page_time 95 page_date.day) + page_time
115 except Exception as ex: 96 else:
116 raise ConfigurationError( 97 self._datetime = datetime.datetime(
117 "Invalid page time '%s' for: %s" % ( 98 page_date.year, page_date.month, page_date.day)
118 page_time, self.path)) from ex 99 elif 'date' in self.config:
100 # Get the date from the page config, and maybe the
101 # time too.
102 page_date = _parse_config_date(self.config.get('date'))
103 self._datetime = datetime.datetime(
104 page_date.year,
105 page_date.month,
106 page_date.day)
107 page_time = _parse_config_time(self.config.get('time'))
108 if page_time is not None:
109 self._datetime += page_time
119 else: 110 else:
120 self._datetime = datetime.datetime( 111 # No idea what the date/time for this page is.
121 page_date.year, page_date.month, page_date.day) 112 self._datetime = datetime.datetime.fromtimestamp(0)
122 else: 113 except Exception as ex:
123 self._datetime = datetime.datetime.fromtimestamp( 114 raise Exception(
124 self.path_mtime) 115 "Error computing time for page: %s" %
125 if self._datetime is None: 116 self.path) from ex
126 raise Exception("Got null datetime from path! %s" % self.path)
127 return self._datetime 117 return self._datetime
128 118
129 @datetime.setter 119 @datetime.setter
130 def datetime(self, value): 120 def datetime(self, value):
131 self._datetime = value 121 self._datetime = value
137 if self._config is not None: 127 if self._config is not None:
138 return 128 return
139 129
140 config, content, was_cache_valid = load_page(self.app, self.path, 130 config, content, was_cache_valid = load_page(self.app, self.path,
141 self.path_mtime) 131 self.path_mtime)
132 if 'config' in self.source_metadata:
133 config.merge(self.source_metadata['config'])
134
142 self._config = config 135 self._config = config
143 self._raw_content = content 136 self._raw_content = content
144 if was_cache_valid: 137 if was_cache_valid:
145 self._flags |= FLAG_RAW_CACHE_VALID 138 self._flags |= FLAG_RAW_CACHE_VALID
139
140
141 def _parse_config_date(page_date):
142 if page_date is None:
143 return None
144
145 if isinstance(page_date, str):
146 try:
147 parsed_d = dateutil.parser.parse(page_date)
148 except Exception as ex:
149 raise ConfigurationError("Invalid date: %s" % page_date) from ex
150 return datetime.date(
151 year=parsed_d.year,
152 month=parsed_d.month,
153 day=parsed_d.day)
154
155 raise ConfigurationError("Invalid date: %s" % page_date)
156
157
158 def _parse_config_time(page_time):
159 if page_time is None:
160 return None
161
162 if isinstance(page_time, datetime.timedelta):
163 return page_time
164
165 if isinstance(page_time, str):
166 try:
167 parsed_t = dateutil.parser.parse(page_time)
168 except Exception as ex:
169 raise ConfigurationError("Invalid time: %s" % page_time) from ex
170 return datetime.timedelta(
171 hours=parsed_t.hour,
172 minutes=parsed_t.minute,
173 seconds=parsed_t.second)
174
175 if isinstance(page_time, int):
176 # Total seconds... convert to a time struct.
177 return datetime.timedelta(seconds=page_time)
178
179 raise ConfigurationError("Invalid time: %s" % page_time)
146 180
147 181
148 class PageLoadingError(Exception): 182 class PageLoadingError(Exception):
149 def __init__(self, path, inner=None): 183 def __init__(self, path, inner=None):
150 super(PageLoadingError, self).__init__( 184 super(PageLoadingError, self).__init__(