1#!/usr/bin/env python3
2
3"""This module outputs a skeleton Wiki page describing either:
4
5 - An activity which writes to a derived table
6 - An event class
7 - A view
8
9It only produces the framework of a wiki page with a few sections filled in, not a final page.
10The result should be edited by hand.
11The environment variables CHART_WEB and CHART_PREFIX affect the generated URLs.
12"""
13
14
15
16import sys
17import ast
18
19from chart.project import settings
20from chart.common.args import ArgumentParser
21from chart.common.util import nvl
22from chart.events.eventclass import EventClass
23from chart.db.model.table import TableInfo
24from chart.alg.views import constants_in_file
25from chart.alg.constants import get_constant_description
26
27# disable all pylint T-B-D warnings for the module because we print lots of strings containing
28# the acronym
29# pylint: disable=W0511
30
31
32def italic(text):
33 """Markup `text` as wiki italic text."""
34 return "''{text}''".format(text=text) # italic
35
36
37def bold(text):
38 """Mark up `text` as wiki bold text."""
39 return "'''{text}'''".format(text=text) # bold
40
41
42def table_header(*args):
43 """Emit a line of wiki code marking a table header.
44 `args` should be a list of strings giving the column names."""
45 return '||=' + '=||='.join(bold(h) for h in args) + '=||\n'
46
47
48def table_row(*args):
49 """Emit a table row."""
50 return '||' + '||'.join(args) + '||\n'
51
52
53def tobedone(text):
54 """Markup `text` as a T-B-D."""
55 # (we are avoiding triggering pylint errors here)
56 return "''{t}{b}{d}: {text}''".format(t='T', b='B', d='D', text=text)
57
58
59def list_formatter(items, common_delimiter=', ', final_delimiter=' and '):
60 """Return a string giving `items` separated by `common_delimiter.
61 The final item uses `final_delimiter`.
62
63 >>> list_formatter(['apple', 'banana', 'orange'])
64 'apple, banana and orange'
65 """
66 items = list(items)
67 result = ''
68 for i, item in enumerate(items):
69 if i == 0:
70 result += item
71
72 elif i != (len(items) - 1):
73 result += common_delimiter + item
74
75 else:
76 result += final_delimiter + item
77
78 return result
79
80
81def sentance(text):
82 """Ensure `text` ends in a full stop.
83 """
84
85 if text.endswith('.'):
86 return text
87
88 else:
89 return text + '.'
90
91
92def wiki_tabledef(tableinfo, target=sys.stdout):
93 """Describe `table` in wiki format.
94 """
95
96 if tableinfo.type == 'view':
97 typ = 'View'
98
99 else:
100 typ = 'Table'
101
102 has_units = False
103 for f in tableinfo.fields.values():
104 if f.unit is not None and len(f.unit) > 0:
105 has_units = True
106
107 target.write("{typ} [{link} {name}]\n\n||='''Field'''=||={unit}'''Type'''"
108 "=||='''Description'''=||\n".format(
109 typ=typ,
110 link=tableinfo.browse_url,
111 name=tableinfo.name,
112 unit="'''Unit'''=||=" if has_units else ''))
113
114 for f in tableinfo.fields.values():
115 # note the trac wiki engine will treat '||||' as a cell joined to the next cell to the
116 # right, so we use '|| ||' for an empty cell.
117 target.write('||{name}||{unit}{type}||{description}||\n'.format(
118 name=f.name,
119 unit=nvl(f.unit, ' ') + '||' if has_units else '',
120 type=f.datatype,
121 description=nvl(f.description)))
122
123 target.write('\n')
124
125
126def wiki_title(thing, target=sys.stdout):
127 """Write a title section"""
128 if isinstance(thing, EventClass):
129 target.write('= {name} class =\n\n'.format(name=thing.name))
130
131 else:
132 target.write('= {name} =\n\n'.format(name=thing.name))
133
134
135def wiki_introduction_activity(activity, target=sys.stdout):
136 """Write an introduction section using the <description> of the activity"""
137
138 # heading
139 target.write('== Introduction ==\n\n')
140
141 if len(activity.eventnames) > 0:
142 target.write("""Warning: Since this Activity can generate Events, you probably wanted to
143run the wiki skeleton tool with the "--event xxx" option instead.\n\n""")
144
145 # simple summary of the nature of this page and the purpose of the thing it describes
146 if len(activity.output_tables) > 0:
147 target.write('This page describes the activity {act} which writes to the table{plural} '
148 '{outputs}.\n\n'.format(
149 plural='s' if len(activity.output_tables) != 1 else '',
150 act=activity.name,
151 outputs=list_formatter(o.name for o in activity.output_tables)))
152
153 # now try to find more information from the activity.xml ...
154 if activity.description is not None:
155 target.write('{description}\n\n'.format(description=activity.description))
156
157 # ... and code docstring
158 docstring = activity.docstring
159 if docstring is not None and len(docstring) > 0:
160 target.write('{docstring}\n\n'.format(docstring=docstring))
161
162 # remind the author to check the results
163 target.write(tobedone(
164 'This section is autogenerated from the activity <description> and the code '
165 'docstring. It should be checked and edited.') + '\n\n')
166
167
168def wiki_introduction_view(viewinfo, target=sys.stdout):
169 """Write an introduction section using the <description> of the activity"""
170
171 # heading
172 target.write('== Introduction ==\n\n')
173
174 # simple summary of the nature of this page and the purpose of the thing it describes
175 target.write('This page describes the view {view}. A view is a database object which '
176 'contains computed values derived from other tables or views.'.format(
177 view=viewinfo.name))
178
179 target.write('{description}\n\n'.format(description=sentance(viewinfo.description)))
180
181 # remind the author to check the results
182 # disable pylint T-B-D warning
183 target.write("''TBD: This section is autogenerated from the view "
184 "<description>. It should be checked and edited.''\n\n")
185
186
187def wiki_introduction_eventclass(eventclass, target=sys.stdout):
188 """Write an introduction section using the <description> of the activity"""
189
190 # heading
191 target.write('== Introduction ==\n\n')
192
193 # remind the author to check the results
194 # disable pylint T-B-D warning
195 target.write(tobedone('This section is autogenerated from the event class description and '
196 'the activity <description>. It should be checked and edited') + '\n\n')
197
198 if eventclass.description is not None and len(eventclass.description) > 0:
199 target.write(sentance(eventclass.description) + '\n\n')
200
201 else:
202 target.write(tobedone('Add a <description> element to the event class description') +
203 '\n\n')
204
205 activities = list(eventclass.raised_by())
206 if len(activities) == 1:
207 target.write('This event is raised by the activity {act}.\n\n'.format(
208 act=activities[0].name))
209
210 elif len(activities) > 1:
211 target.write('This event is raised by the activities: {acts}.\n\n'.format(
212 acts=', '.join(a.name for a in activities)))
213
214 else:
215 target.write('This event is not raised by any activities.\n\n')
216
217 for a in activities:
218 if a.description is not None and len(a.description) > 0:
219 target.write(' {act}::\n {desc}\n\n'.format(
220 act=a.name, desc=sentance(a.description)))
221
222
223def wiki_definition_view(viewinfo, target=sys.stdout):
224 """Show a table definition for a view."""
225 target.write('== Definition ==\n\n')
226
227 wiki_tabledef(viewinfo, target)
228
229
230def wiki_definition_eventclass(eventclass, target=sys.stdout):
231 """List the instance properties of an event class."""
232 target.write('== Event class definition ==\n\n')
233
234 non_comments = len(list(e for e in eventclass.instance_properties.keys() if e != 'comment'))
235
236 if non_comments == 0:
237 target.write('This event class only uses the standard properties (start time etc.).\n\n')
238
239 else:
240 target.write(
241 "In addition to the standard properties (start time etc.) this event class "
242 "has the following instance properties defined:\n\n||='''Name'''=||='''Unit'''=||"
243 "='''Datatype'''=||='''Optional'''=||='''Choices'''=||='''Description'''=||\n")
244
245 for i in eventclass.instance_properties.values():
246 if i['name'] == 'comment':
247 continue
248
249 if 'choices' not in i:
250 choices = ' '
251
252 else:
253 choices = str(i['choices'])
254
255 target.write('||{name}||{unit}||{type}||{optional}||{choices}||{desc}||\n'.format(
256 name=i['name'],
257 unit=i.get('unit'),
258 type=i['type'],
259 optional=nvl(i.get('optional', ' ')),
260 choices=choices,
261 desc=nvl(i.get('description'), ' ')))
262
263 target.write('\n')
264
265
266def wiki_inputs_activity(activity, target=sys.stdout):
267 """Write Inputs section by scanning the source."""
268 target.write('== Inputs ==\n\n')
269
270 target.write('Inputs from timeseries tables:\n\n')
271
272 # for t in activity.triggers:
273 # target.write(' * Table [{link} {table}]\n * Field SENSING_TIME\n'.format(
274 # table=t['table'].name,
275 # link=t['table'].browse_url))
276
277 for t in ts_in_file(activity.abs_executable):
278 # target.write(str(t))
279 target.write(' * Table [{link} {table}]\n'.format(
280 table=t['table'].name,
281 link=t['table'].browse_url))
282 for f in t['fields']:
283 target.write(' * {f}\n'.format(f=f))
284
285 target.write('\n')
286
287
288def wiki_inputs_view(viewinfo, target=sys.stdout):
289 """Write Inputs section using the view inputs elements."""
290 target.write('== Inputs ==\n\n')
291
292 target.write('The following tables are inputs to this view:\n\n')
293
294 for s in viewinfo.source_tables:
295 target.write(" * Table [{link} {name}]\n * Field SENSING_TIME\n ''TBD: add any other "
296 "input fields here''\n".format(
297 link=s.browse_url,
298 name=s.name))
299
300 target.write('\n')
301
302
303def wiki_inputs_eventclass(eventclass, target=sys.stdout):
304 """Show a sample Inputs section."""
305 target.write('== Inputs ==\n\n')
306
307 for a in eventclass.raised_by():
308 target.write('This event is raised by the activity {act} which uses as inputs:\n\n'.format(
309 act=a.name))
310
311 # for t in a.triggers:
312 # target.write(' * Table [{link} {table}]\n * Field SENSING_TIME\n'.format(
313 # table=t['table'].name,
314 # link=t['table'].browse_url))
315
316 for t in ts_in_file(a.abs_executable):
317 target.write(' * Table [{link} {table}]\n'.format(
318 table=t['table'].name,
319 link=t['table'].browse_url))
320 for f in t['fields']:
321 target.write(' * {f}\n'.format(f=f))
322
323 target.write('\n')
324
325 target.write("''TBD: This list may input inputs which are not " # pylint: disable=W0611
326 "relevant to this event and should be edited by hand.''\n\n")
327
328
329def wiki_outputs(activity, target=sys.stdout):
330 """Write outputs section (either derived table, view desc or event class desc)."""
331 target.write('== Outputs ==\n\n')
332
333 for t in activity.output_tables:
334 wiki_tabledef(t)
335
336
337def wiki_constants(activity, target=sys.stdout): # (unused arg) pylint: disable=W0613
338 """Write a Constants section.
339 This should list all values used from constants.py.
340 Auto-scan is possible but not implemented.
341 """
342 target.write('== Constants ==\n\n')
343 target.write('The following constants are read from the constants file:\n\n')
344
345 for constant in constants_in_file(activity.abs_executable):
346 desc = get_constant_description(constant)
347 if desc is None:
348 target.write(' * {name}\n'.format(name=constant))
349
350 else:
351 target.write(' * {name} ({desc})\n'.format(name=constant,
352 desc=desc))
353
354 target.write('\n')
355
356
357def wiki_method_activity(activity, target=sys.stdout):
358 """This section must be hand written.
359 To help the author we include a blank template section listing fields and properties."""
360 target.write('== Method ==\n\n')
361
362 # switch off pylint T-B-D warning
363 target.write(tobedone('This section should be rewritten to describe the algorithm processing') +
364 '\n\n')
365
366 for t in activity.output_tables:
367 target.write(tobedone('Describe how each field in {name} is set').format(name=t.name) +
368 '\n\n')
369
370 target.write(table_header('Field', 'Algorithm'))
371
372 for f in t.fields.values():
373 target.write(table_row(f.name, ' '))
374
375 target.write('\n')
376
377
378def wiki_method_view(viewinfo, target=sys.stdout):
379 """Write a method section."""
380 target.write('== Method ==\n\n')
381 target.write(tobedone(
382 'In this section describe the overall algorithm used to compute the '
383 'view members, and fill in below the specific method for each field.') + '\n\n')
384 target.write(table_header('Field', 'Algorithm'))
385 for f in viewinfo.fields.values():
386 target.write(table_row(f.name, ' '))
387
388 target.write('\n')
389
390
391def wiki_method_eventclass(eventclass, target=sys.stdout):
392 """Add a fairly empty Method section for an event page."""
393 target.write('== Method ==\n\n')
394
395 # switch off pylint T-B-D warning
396 target.write("''TBD: This section should be rewritten to describe the "
397 "algorithm processing.")
398
399 if len(eventclass.instance_properties) > 0:
400 target.write(" Include a description of how each property is set.''\n\n")
401
402 target.write(table_header('Name', 'Method'))
403
404 for i in eventclass.instance_properties.values():
405 if i['name'] == 'comment':
406 continue
407
408 target.write('||{name}|| ||\n'.format(
409 name=i['name']))
410
411 target.write('\n')
412
413 else:
414 target.write("''\n\n")
415
416
417def wiki_implementation_activity(activity, target=sys.stdout):
418 """Write an implementation section."""
419 target.write('== Implementation ==\n\n')
420
421 activity_path = settings.ACTIVITY_DIR.relative_to(settings.PROJ_HOME_DIR)
422
423 target.write('This algorithm is implemented in the file [{exe}] '
424 'and configured in the activity [{act}].'.format(
425 exe=activity.browse_executable_url,
426 act=activity.browse_source_url))
427
428 if len(activity.output_tables) > 0:
429 target.write('The output table{plural2} {plural1} defined in the file{plural2} '
430 '{files}.'.format(
431 plural1='are' if len(activity.output_tables) != 1 else 'is',
432 plural2='s:' if len(activity.output_tables) != 1 else '',
433 files=', '.join(t.browse_source_url for t in activity.output_tables)))
434
435 target.write('\n\n')
436
437
438def wiki_implementation_view(viewinfo, target=sys.stdout):
439 """Write an implementation section."""
440 target.write('== Implementation ==\n\n')
441
442 ts_path = settings.TS_TABLE_DIR.relative_to(settings.PROJ_HOME_DIR)
443 target.write('This view is defined and implemented in the file '
444 '[{base}/{rel}/{name}.xml].\n\n'.format(
445 base=settings.PROJ_WIKI_BROWSE_URL,
446 rel=ts_path,
447 name=viewinfo.name))
448
449
450def wiki_implementation_eventclass(eventclass, target=sys.stdout):
451 """Write an implementation section."""
452 target.write('== Implementation ==\n\n')
453
454 for a in eventclass.raise_by():
455 activity_path = settings.ACTIVITY_DIR.relative_to(settings.PROJ_HOME_DIR)
456 target.write("This algorithm is implemented by the function ''TBD'' in the file "
457 "[{base}/{exe}] and configured in the activity "
458 "[{base}/{rel}/{act}].".format(
459 base=settings.PROJ_WIKI_BROWSE_URL,
460 exe=a.executable,
461 rel=activity_path,
462 act=a.name + '.xml'))
463
464 if len(a.output_tables) > 0:
465 ts_path = settings.TS_TABLE_DIR.relative_to(settings.PROJ_HOME_DIR)
466 target.write(' The output tables {plural1} defined in the file{plural2} '
467 '{files}.'.format(
468 plural1='are' if len(a.output_tables) != 1 else 'is',
469 plural2='s:' if len(a.output_tables) != 1 else '',
470 files=', '.join('[{base}/{rel}/{name}.xml]'.format(
471 base=settings.PROJ_WIKI_BROWSE_URL,
472 rel=ts_path,
473 name=t.name) for t in a.output_tables)))
474
475 target.write('\n\n')
476
477
478def wiki_triggering(activity, target=sys.stdout):
479 """Should be created fully automatically."""
480 target.write('== Triggering ==\n\n')
481
482 # test for presence of any orbital triggers
483 orbital_triggers = []
484 for t in activity.triggers:
485 if t['type'] == 'orbital':
486 orbital_triggers.append(t)
487
488 # have we automatically detected any triggers?
489 auto_trigger = False
490 if len(orbital_triggers) > 0:
491 target.write('This algorithm is triggered on a per-orbit basis by changes to the following '
492 'tables: {triggers}.'.format(
493 triggers=', '.join(t['table'].name for t in orbital_triggers)))
494 auto_trigger = True
495
496 if not auto_trigger:
497 target.write(tobedone('Describe how the algorithm is triggered.') + '\n\n')
498
499 target.write('\n\n')
500
501
502def wiki_testing(activity=None, target=sys.stdout): # pylint: disable=W0613
503 """Could possibly auto generate this by scanning the tests directory
504 or by adding meta data to the activity file."""
505 target.write('== Testing ==\n\n')
506
507 target.write(tobedone('describe any unit tests here.') + '\n\n')
508
509
510def wiki_testing_eventclass(eventclass, target=sys.stdout):
511 """Generate a wiki section describing tests written for `eventclass`."""
512 target.write('== Testing ==\n\n')
513
514 if eventclass.test is None:
515 target.write(tobedone('write an automated test and describe it here.'))
516
517 else:
518 target.write('This event is covered by the automated test [{base}/{test}].'.format(
519 base=settings.PROJ_WIKI_BROWSE_URL,
520 test=eventclass.test))
521
522 target.write('\n\n')
523
524
525def wiki_gallery(target=sys.stdout):
526 """Gallery section."""
527 target.write('== Gallery ==\n\n')
528
529 target.write(tobedone('Link to one or more sample plots showing the algorithm results.') +
530 '\n\n')
531
532
533def wiki_tickets(target=sys.stdout):
534 """Maybe possibly could set this automatically be scanning the tickets."""
535 target.write('== Tickets ==\n\n')
536
537 target.write(tobedone('List any related tickets.') + '\n\n')
538
539
540def wiki_page_activity(activity, target=sys.stdout):
541 """Given an `activity` object, output a skeleton Wiki page describing that activity.
542 URLs are adjusted according to the current environment so setting i.e. CHART_URL
543 may help.
544
545 Different sections are output depending on if this page describes:
546
547 - An algorithm which writes to a derived table
548 - An algorithm which creates events
549 - A view
550 """
551 wiki_title(activity, target)
552 wiki_introduction_activity(activity=activity, target=target)
553 wiki_inputs_activity(activity, target)
554 wiki_outputs(activity, target)
555 wiki_constants(activity, target)
556 wiki_method_activity(activity, target)
557 wiki_implementation_activity(activity, target)
558 wiki_triggering(activity, target)
559 wiki_testing(target=target)
560 wiki_gallery(target)
561 wiki_tickets(target)
562
563
564def wiki_page_view(viewinfo, target=sys.stdout):
565 """Generate a skeleton wiki page describing a database view."""
566 wiki_title(viewinfo, target)
567 wiki_introduction_view(viewinfo=viewinfo, target=target)
568 wiki_definition_view(viewinfo, target)
569 wiki_inputs_view(viewinfo, target)
570 wiki_method_view(viewinfo, target)
571 wiki_implementation_view(viewinfo, target)
572 wiki_testing(target)
573 wiki_gallery(target)
574 wiki_tickets(target)
575
576
577def wiki_page_eventclass(eventclass, target=sys.stdout):
578 """Generate a skeleton wiki page describing an event class."""
579 wiki_title(eventclass, target)
580 wiki_introduction_eventclass(eventclass, target)
581 wiki_definition_eventclass(eventclass, target)
582 wiki_inputs_eventclass(eventclass, target)
583 wiki_method_eventclass(eventclass, target)
584 wiki_implementation_eventclass(eventclass, target)
585 wiki_testing_eventclass(eventclass, target)
586 wiki_gallery(target)
587 wiki_tickets(target)
588
589
590def ts_in_file(filename):
591 """
592 Yield a series of dictionaries describing calls to db.ts.select.
593 Each contains keys:
594 `tablename` (str):
595 `fields` (list of str)
596 """
597 for fn in functions_in_file(filename):
598 if fn['name'] == 'db.ts.select':
599 ts = {}
600 if 'table' in fn['kwargs']:
601 ts['table'] = fn['kwargs']['table']
602
603 elif len(fn['args']) >= 1:
604 ts['table'] = fn['args'][0]
605
606 ts['table'] = TableInfo(ts['table'])
607
608 if 'fields' in fn['kwargs']:
609 ts['fields'] = fn['kwargs']['fields']
610
611 elif len(fn['args']) >= 2:
612 ts['fields'] = fn['args'][1]
613
614 # clients can pass in fields as a single string
615 if isinstance(ts['fields'], str):
616 ts['fields'] = [ts['fields']]
617
618 yield ts
619
620
621def strings(elem):
622 """Assuming `elem` is a list representing element, return the strings it contains.
623 Not very accurate, this is not recursive and can omit loads of things.
624 """
625 res = []
626 for elt in elem.elts:
627 if isinstance(elt, ast.Str):
628 res.append(elt.s)
629
630 return res
631
632
633def functions_in_file(filename):
634 """Given a Python file name yield a series of dictionaries containing keys:
635 `name` (str): Qualified function name
636 `args` (list of str): Numeric function arguments
637 `kwargs` (str vs list of str): Keyword function arguments.
638
639 This probably only works where all the arguments are fixed strings.
640 """
641 for fn, elem in function_elem_in_file(filename):
642 # print('fn ' + str(fn))
643 res = {'name': fn,
644 'args': [],
645 'kwargs': {}}
646 for kw in elem.keywords:
647 # walk through the keyword args to the function
648 if isinstance(kw.value, ast.Str):
649 # plain string
650 res['kwargs'][kw.arg] = kw.value.s
651
652 elif isinstance(kw.value, (ast.Tuple, ast.List)):
653 # a list of things
654 tup = []
655 for elt in kw.value.elts:
656 # print(type(elt))
657 if isinstance(elt, ast.Dict):
658 # if the thing is complicated we give up
659 pass
660
661 elif isinstance(elt, ast.Str):
662 # if the thing is simple, assume its a string and return it
663 tup.append(elt.s)
664
665 else:
666 pass
667
668 res['kwargs'][kw.arg] = tup
669
670 for a in elem.args:
671 # walk through the positional arguments
672 # print('arg ' + str(a))
673 if isinstance(a, ast.Str):
674 # plain string
675 res['args'].append(a.s)
676
677 elif isinstance(a, (ast.Tuple, ast.List)):
678 # list/tuple
679 tup = []
680 for elt in a.elts:
681 if isinstance(elt, ast.Str):
682 tup.append(elt.s)
683
684 res['args'].append(tup)
685
686 elif isinstance(a, ast.BinOp):
687 # probably something clever like computing a list of fields
688 if isinstance(a.right, ast.List):
689 res['args'].append(strings(a.right))
690
691 # else:
692 # print(type(a))
693
694 # if fn == 'db.ts.select':
695 # a = 1 / 0
696
697 yield res
698
699
700def function_elem_in_file(filename):
701 """Yield tuples of (name, elem) for function calls in file.
702 Name is fully qualified dot delimited function name e.g. "db.ts.select".
703 It could be a single name "fn()" or a module function "db.fn()" or a submodule function.
704 Elem is the ast elem object.
705 """
706 tree = ast.parse(filename.open('r').read())
707 for elem in ast.walk(tree):
708 # print(elem)
709 if isinstance(elem, ast.Call):
710 if isinstance(elem.func, ast.Name):
711 # free function
712 # print('free function ' + elem.func.id)
713 yield elem.func.id, elem
714
715 elif isinstance(elem.func, ast.Attribute):
716 # function in module
717 if isinstance(elem.func.value, ast.Name):
718 # print('module function ' + elem.func.attr + '.' + elem.func.value.id)
719 yield '{mod}.{fn}'.format(mod=elem.func.value.id, fn=elem.func.attr), elem
720
721 elif isinstance(elem.func.value, ast.Attribute):
722 # function in module in module
723 if isinstance(elem.func.value.value, ast.Name):
724 # print('nested module function ' + elem.func.value.value.id + ' . ' + \
725 # elem.func.value.attr + ' .' + elem.func.attr)
726 yield '{mod}.{submod}.{fn}'.format(mod=elem.func.value.value.id,
727 submod=elem.func.value.attr,
728 fn=elem.func.attr), elem
729
730 else:
731 # function in module in module in module
732 pass
733
734 # else:
735 # pass
736
737
738def main():
739 """Command line entry point."""
740
741 parser = ArgumentParser(__doc__)
742 parser.add_argument('--view',
743 type=ArgumentParser.table,
744 help='XML view description')
745 parser.add_argument('--activity', '-a',
746 type=ArgumentParser.activity,
747 help='Activity XML file name')
748 parser.add_argument('--event', '--eventclass', '-e',
749 type=ArgumentParser.eventclass,
750 help='Name of event class to document')
751 parser.add_argument('--ts',
752 metavar='FILE',
753 help='Show db.ts.select() calls in FILE')
754 parser.add_argument('--tabledef-fragment',
755 metavar='TABLE',
756 help='Output only a wiki fragment describing TABLE')
757 parser.add_argument('--prefix',
758 help='Override PREFIX setting',
759 default=settings.PREFIX)
760 args = parser.parse_args()
761
762 if args.prefix:
763 settings.PREFIX = args.prefix
764
765 if args.tabledef_fragment:
766 print(wiki_tabledef(TableInfo(args.tabledef_fragment)))
767 parser.exit()
768
769 if args.ts:
770 for row in ts_in_file(args.ts):
771 print(row)
772
773 parser.exit()
774
775 if sum((args.view is not None, args.activity is not None, args.event is not None)) != 1:
776 parser.error('Select one of --activity, --event, --view')
777
778 if args.activity:
779 wiki_page_activity(args.activity)
780 parser.exit()
781
782 elif args.view:
783 wiki_page_view(args.view)
784 parser.exit()
785
786 elif args.event:
787 # print(str(args.event) + ' ' + str(type(args.event)))
788 wiki_page_eventclass(args.event)
789
790 else:
791 parser.error('No actions specified')
792
793if __name__ == '__main__':
794 main()