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
17 import logging, os,sys, cgi, json, jinja2, HTMLParser
19 # Classes register themselves in this dictionary
20 """Mapping of known processors to their classes"""
23 """Mapping of known output formats to their classes"""
27 """Generate rendered output for siphoned data."""
35 """Name of an identifier used by this siphon"""
39 """The pyparsing object to use to parse with"""
45 """Group key to (directory,file) mapping"""
51 """Directory to look for siphon rendering templates"""
52 template_directory = None
54 """Template environment, if we're using templates"""
57 def __init__(self, template_directory, format):
58 super(Siphon, self).__init__()
59 self.log = logging.getLogger("siphon.process.%s" % self.name)
61 # Get our output format details
62 fmt_klass = formats[format]
66 # Sort out the template search path
68 return os.sep.join((template_directory, fmt.name, name))
70 self.template_directory = template_directory
75 loader = jinja2.FileSystemLoader(searchpath=searchpath)
76 self._tplenv = jinja2.Environment(
79 keep_trailing_newline=True)
81 # Convenience, get a reference to the internal escape and
82 # unescape methods in cgi and HTMLParser. These then become
83 # available to templates to use, if needed.
84 self._h = HTMLParser.HTMLParser()
85 self.escape = cgi.escape
86 self.unescape = self._h.unescape
91 """Returns an object to be used as the sorting key in the item index."""
92 def index_sort_key(self, group):
95 """Returns a string to use as the header at the top of the item index."""
96 def index_header(self):
97 return self.template("index_header")
99 """Returns the string fragment to use for each section in the item
101 def index_section(self, group):
102 return self.template("index_section", group=group)
104 """Returns the string fragment to use for each entry in the item index."""
105 def index_entry(self, meta, item):
106 return self.template("index_entry", meta=meta, item=item)
108 """Returns an object, typically a string, to be used as the sorting key
109 for items within a section."""
110 def item_sort_key(self, item):
113 """Returns a key for grouping items together."""
114 def group_key(self, directory, file, macro, name):
115 _global = self._cmds['_global']
117 if file in _global and 'group_label' in _global[file]:
118 self._group[file] = (directory, file)
121 self._group[directory] = (directory, None)
124 """Returns a key for identifying items within a grouping."""
125 def item_key(self, directory, file, macro, name):
128 """Returns a string to use as the header when rendering the item."""
129 def item_header(self, group):
130 return self.template("item_header", group=group)
132 """Returns a string to use as the body when rendering the item."""
133 def item_format(self, meta, item):
134 return self.template("item_format", meta=meta, item=item)
136 """Returns a string to use as the label for the page reference."""
137 def page_label(self, group):
140 self.sanitize_label(group)
143 """Returns a title to use for a page."""
144 def page_title(self, group):
145 _global = self._cmds['_global']
146 (directory, file) = self._group[group]
148 if file and file in _global and 'group_label' in _global[file]:
149 return _global[file]['group_label']
151 if directory in _global and 'group_label' in _global[directory]:
152 return _global[directory]['group_label']
156 """Returns a string to use as the label for the section reference."""
157 def item_label(self, group, item):
163 """Label sanitizer; for creating Doxygen references"""
164 def sanitize_label(self, value):
165 return value.replace(" ", "_") \
169 """Template processor"""
170 def template(self, name, **kwargs):
171 tpl = self._tplenv.get_template(name + self._format.extension)
179 """Parse the input file into a more usable dictionary structure."""
180 def load_json(self, files):
186 for filename in files:
187 filename = os.path.relpath(filename)
188 self.log.info("Parsing items in file \"%s\"." % filename)
190 with open(filename, "r") as fd:
193 self._cmds['_global'] = data['global']
195 # iterate the items loaded and regroup it
196 for item in data["items"]:
198 o = self._parser.parse(item['block'])
200 self.log.error("Exception parsing item: %s\n%s" \
201 % (json.dumps(item, separators=(',', ': '),
206 # Augment the item with metadata
211 o['meta'][key] = item[key]
213 # Load some interesting fields
214 directory = item['directory']
219 # Generate keys to group items by
220 group_key = self.group_key(directory, file, macro, name)
221 item_key = self.item_key(directory, file, macro, name)
223 if group_key not in self._cmds:
224 self._cmds[group_key] = {}
226 self._cmds[group_key][item_key] = o
228 """Iterate over the input data, calling render methods to generate the
230 def process(self, out=None):
235 # Accumulated body contents
238 # Write the header for this siphon type
239 out.write(self.index_header())
241 # Sort key helper for the index
242 def group_sort_key(group):
243 return self.index_sort_key(group)
245 # Iterate the dictionary and process it
246 for group in sorted(self._cmds.keys(), key=group_sort_key):
247 if group.startswith('_'):
250 self.log.info("Processing items in group \"%s\" (%s)." % \
251 (group, group_sort_key(group)))
253 # Generate the section index entry (write it now)
254 out.write(self.index_section(group))
256 # Generate the item header (save for later)
257 contents += self.item_header(group)
259 def item_sort_key(key):
260 return self.item_sort_key(self._cmds[group][key])
262 for key in sorted(self._cmds[group].keys(), key=item_sort_key):
263 self.log.debug("--- Processing key \"%s\" (%s)." % \
264 (key, item_sort_key(key)))
266 o = self._cmds[group][key]
268 "directory": o['meta']['directory'],
269 "file": o['meta']['file'],
273 "label": self.item_label(group, key),
276 # Generate the index entry for the item (write it now)
277 out.write(self.index_entry(meta, o))
279 # Generate the item itself (save for later)
280 contents += self.item_format(meta, o)
282 # Deliver the accumulated body output
286 """Output format class"""
287 class Format(object):
289 """Name of this output format"""
292 """Expected file extension of templates that build this format"""
296 """Markdown output format"""
297 class FormatMarkdown(Format):
301 # Register 'markdown'
302 formats["markdown"] = FormatMarkdown
305 """Itemlist output format"""
306 class FormatItemlist(Format):
308 extension = ".itemlist"
310 # Register 'itemlist'
311 formats["itemlist"] = FormatItemlist