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(
86 keep_trailing_newline=True)
88 # Convenience, get a reference to the internal escape and
89 # unescape methods in html.parser. These then become
90 # available to templates to use, if needed.
91 self._h = html.parser.HTMLParser()
92 self.escape = html.escape
93 self.unescape = html.unescape
97 """Returns an object to be used as the sorting key in the item index."""
98 def index_sort_key(self, group):
101 """Returns a string to use as the header at the top of the item index."""
102 def index_header(self):
103 return self.template("index_header")
105 """Returns the string fragment to use for each section in the item
107 def index_section(self, group):
108 return self.template("index_section", group=group)
110 """Returns the string fragment to use for each entry in the item index."""
111 def index_entry(self, meta, item):
112 return self.template("index_entry", meta=meta, item=item)
114 """Returns an object, typically a string, to be used as the sorting key
115 for items within a section."""
116 def item_sort_key(self, item):
119 """Returns a key for grouping items together."""
120 def group_key(self, directory, file, macro, name):
121 _global = self._cmds['_global']
123 if file in _global and 'group_label' in _global[file]:
124 self._group[file] = (directory, file)
127 self._group[directory] = (directory, None)
130 """Returns a key for identifying items within a grouping."""
131 def item_key(self, directory, file, macro, name):
134 """Returns a string to use as the header when rendering the item."""
135 def item_header(self, group):
136 return self.template("item_header", group=group)
138 """Returns a string to use as the body when rendering the item."""
139 def item_format(self, meta, item):
140 return self.template("item_format", meta=meta, item=item)
142 """Returns a string to use as the label for the page reference."""
143 def page_label(self, group):
146 self.sanitize_label(group)
149 """Returns a title to use for a page."""
150 def page_title(self, group):
151 _global = self._cmds['_global']
152 (directory, file) = self._group[group]
154 if file and file in _global and 'group_label' in _global[file]:
155 return _global[file]['group_label']
157 if directory in _global and 'group_label' in _global[directory]:
158 return _global[directory]['group_label']
162 """Returns a string to use as the label for the section reference."""
163 def item_label(self, group, item):
169 """Label sanitizer; for creating Doxygen references"""
170 def sanitize_label(self, value):
171 return value.replace(" ", "_") \
175 """Template processor"""
176 def template(self, name, **kwargs):
177 tpl = self._tplenv.get_template(name + self._format.extension)
184 """Parse the input file into a more usable dictionary structure."""
185 def load_json(self, files):
191 for filename in files:
192 filename = os.path.relpath(filename)
193 self.log.info("Parsing items in file \"%s\"." % filename)
195 with open(filename, "r") as fd:
198 self._cmds['_global'] = data['global']
200 # iterate the items loaded and regroup it
201 for item in data["items"]:
203 o = self._parser.parse(item['block'])
205 self.log.error("Exception parsing item: %s\n%s"
206 % (json.dumps(item, separators=(',', ': '),
211 # Augment the item with metadata
216 o['meta'][key] = item[key]
218 # Load some interesting fields
219 directory = item['directory']
224 # Generate keys to group items by
225 group_key = self.group_key(directory, file, macro, name)
226 item_key = self.item_key(directory, file, macro, name)
228 if group_key not in self._cmds:
229 self._cmds[group_key] = {}
231 self._cmds[group_key][item_key] = o
233 """Iterate over the input data, calling render methods to generate the
235 def process(self, out=None):
240 # Accumulated body contents
243 # Write the header for this siphon type
244 out.write(self.index_header())
246 # Sort key helper for the index
247 def group_sort_key(group):
248 return self.index_sort_key(group)
250 # Iterate the dictionary and process it
251 for group in sorted(self._cmds.keys(), key=group_sort_key):
252 if group.startswith('_'):
255 self.log.info("Processing items in group \"%s\" (%s)." %
256 (group, group_sort_key(group)))
258 # Generate the section index entry (write it now)
259 out.write(self.index_section(group))
261 # Generate the item header (save for later)
262 contents += self.item_header(group)
264 def item_sort_key(key):
265 return self.item_sort_key(self._cmds[group][key])
267 for key in sorted(self._cmds[group].keys(), key=item_sort_key):
268 self.log.debug("--- Processing key \"%s\" (%s)." %
269 (key, item_sort_key(key)))
271 o = self._cmds[group][key]
273 "directory": o['meta']['directory'],
274 "file": o['meta']['file'],
278 "label": self.item_label(group, key),
281 # Generate the index entry for the item (write it now)
282 out.write(self.index_entry(meta, o))
284 # Generate the item itself (save for later)
285 contents += self.item_format(meta, o)
287 # Deliver the accumulated body output
291 class Format(object):
292 """Output format class"""
294 """Name of this output format"""
297 """Expected file extension of templates that build this format"""
301 class FormatMarkdown(Format):
302 """Markdown output format"""
307 # Register 'markdown'
308 formats["markdown"] = FormatMarkdown
311 class FormatItemlist(Format):
312 """Itemlist output format"""
314 extension = ".itemlist"
317 # Register 'itemlist'
318 formats["itemlist"] = FormatItemlist