1 # Copyright (c) 2016 Comcast Cable Communications Management, LLC.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at:
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Generation template class
25 # Classes register themselves in this dictionary
26 """Mapping of known processors to their classes"""
29 """Mapping of known output formats to their classes"""
34 """Generate rendered output for siphoned data."""
41 """Name of an identifier used by this siphon"""
45 """The pyparsing object to use to parse with"""
51 """Group key to (directory,file) mapping"""
57 """Directory to look for siphon rendering templates"""
58 template_directory = None
60 """Template environment, if we're using templates"""
63 def __init__(self, template_directory, format):
64 super(Siphon, self).__init__()
65 self.log = logging.getLogger("siphon.process.%s" % self.name)
67 # Get our output format details
68 fmt_klass = formats[format]
72 # Sort out the template search path
74 return os.sep.join((template_directory, fmt.name, name))
76 self.template_directory = template_directory
81 loader = jinja2.FileSystemLoader(searchpath=searchpath)
82 self._tplenv = jinja2.Environment(
85 keep_trailing_newline=True)
87 # Convenience, get a reference to the internal escape and
88 # unescape methods in html.parser. These then become
89 # available to templates to use, if needed.
90 self._h = html.parser.HTMLParser()
91 self.escape = html.escape
92 self.unescape = html.unescape
96 """Returns an object to be used as the sorting key in the item index."""
97 def index_sort_key(self, group):
100 """Returns a string to use as the header at the top of the item index."""
101 def index_header(self):
102 return self.template("index_header")
104 """Returns the string fragment to use for each section in the item
106 def index_section(self, group):
107 return self.template("index_section", group=group)
109 """Returns the string fragment to use for each entry in the item index."""
110 def index_entry(self, meta, item):
111 return self.template("index_entry", meta=meta, item=item)
113 """Returns an object, typically a string, to be used as the sorting key
114 for items within a section."""
115 def item_sort_key(self, item):
118 """Returns a key for grouping items together."""
119 def group_key(self, directory, file, macro, name):
120 _global = self._cmds['_global']
122 if file in _global and 'group_label' in _global[file]:
123 self._group[file] = (directory, file)
126 self._group[directory] = (directory, None)
129 """Returns a key for identifying items within a grouping."""
130 def item_key(self, directory, file, macro, name):
133 """Returns a string to use as the header when rendering the item."""
134 def item_header(self, group):
135 return self.template("item_header", group=group)
137 """Returns a string to use as the body when rendering the item."""
138 def item_format(self, meta, item):
139 return self.template("item_format", meta=meta, item=item)
141 """Returns a string to use as the label for the page reference."""
142 def page_label(self, group):
145 self.sanitize_label(group)
148 """Returns a title to use for a page."""
149 def page_title(self, group):
150 _global = self._cmds['_global']
151 (directory, file) = self._group[group]
153 if file and file in _global and 'group_label' in _global[file]:
154 return _global[file]['group_label']
156 if directory in _global and 'group_label' in _global[directory]:
157 return _global[directory]['group_label']
161 """Returns a string to use as the label for the section reference."""
162 def item_label(self, group, item):
168 """Label sanitizer; for creating Doxygen references"""
169 def sanitize_label(self, value):
170 return value.replace(" ", "_") \
174 """Template processor"""
175 def template(self, name, **kwargs):
176 tpl = self._tplenv.get_template(name + self._format.extension)
183 """Parse the input file into a more usable dictionary structure."""
184 def load_json(self, files):
190 for filename in files:
191 filename = os.path.relpath(filename)
192 self.log.info("Parsing items in file \"%s\"." % filename)
194 with open(filename, "r") as fd:
197 self._cmds['_global'] = data['global']
199 # iterate the items loaded and regroup it
200 for item in data["items"]:
202 o = self._parser.parse(item['block'])
204 self.log.error("Exception parsing item: %s\n%s"
205 % (json.dumps(item, separators=(',', ': '),
210 # Augment the item with metadata
215 o['meta'][key] = item[key]
217 # Load some interesting fields
218 directory = item['directory']
223 # Generate keys to group items by
224 group_key = self.group_key(directory, file, macro, name)
225 item_key = self.item_key(directory, file, macro, name)
227 if group_key not in self._cmds:
228 self._cmds[group_key] = {}
230 self._cmds[group_key][item_key] = o
232 """Iterate over the input data, calling render methods to generate the
234 def process(self, out=None):
239 # Accumulated body contents
242 # Write the header for this siphon type
243 out.write(self.index_header())
245 # Sort key helper for the index
246 def group_sort_key(group):
247 return self.index_sort_key(group)
249 # Iterate the dictionary and process it
250 for group in sorted(self._cmds.keys(), key=group_sort_key):
251 if group.startswith('_'):
254 self.log.info("Processing items in group \"%s\" (%s)." %
255 (group, group_sort_key(group)))
257 # Generate the section index entry (write it now)
258 out.write(self.index_section(group))
260 # Generate the item header (save for later)
261 contents += self.item_header(group)
263 def item_sort_key(key):
264 return self.item_sort_key(self._cmds[group][key])
266 for key in sorted(self._cmds[group].keys(), key=item_sort_key):
267 self.log.debug("--- Processing key \"%s\" (%s)." %
268 (key, item_sort_key(key)))
270 o = self._cmds[group][key]
272 "directory": o['meta']['directory'],
273 "file": o['meta']['file'],
277 "label": self.item_label(group, key),
280 # Generate the index entry for the item (write it now)
281 out.write(self.index_entry(meta, o))
283 # Generate the item itself (save for later)
284 contents += self.item_format(meta, o)
286 # Deliver the accumulated body output
290 class Format(object):
291 """Output format class"""
293 """Name of this output format"""
296 """Expected file extension of templates that build this format"""
300 class FormatMarkdown(Format):
301 """Markdown output format"""
306 # Register 'markdown'
307 formats["markdown"] = FormatMarkdown
310 class FormatItemlist(Format):
311 """Itemlist output format"""
313 extension = ".itemlist"
316 # Register 'itemlist'
317 formats["itemlist"] = FormatItemlist