1#!/usr/bin/env python3
  2
  3"""Examine table, field and calibration definitions.
  4
  5See also srdb_tool and packet_tool.
  6
  7Features:
  8
  9- Examine list and full descriptions of tables
 10 - including filtering and per-SID information
 11- Examine list and full descriptions of fields
 12- Examine list and full descriptions of calibrations
 13
 14"""
 15
 16import sys
 17import logging
 18
 19from chart.common.args import ArgumentParser
 20from chart.common.prettyprint import Table
 21from chart.project import SID
 22from chart.common.matcher import StringTest
 23from chart.db.model.calibration import PolyCalibration
 24from chart.db.model.calibration import LinearCalibration
 25from chart.db.model.calibration import SQLCalibration
 26
 27logger = logging.getLogger()
 28
 29INDENT = '  '
 30
 31def list_tables(tables, target=sys.stdout):
 32    result = Table(headings=('Name', 'Fields', 'Description'))
 33    for table in tables:
 34        result.append((table.name, len(table.fields), table.description))
 35
 36    result.write()
 37
 38def extract_fields(tables):
 39    result = set()
 40    for table in tables:
 41        for field in table.fields.values():
 42            result.add(field)
 43
 44    return result
 45
 46def list_fields(fields):
 47    result = Table(headings=('Name', 'Table', 'Calibration', 'Description'))
 48    for field in fields:
 49        result.append((field.name, field.table.name, field.calibration_name, field.description))
 50
 51    result.write()
 52
 53def find_fields(fields,
 54                field_name=None,
 55                cal_name=None):
 56    """Yield only `fields` members matching criteria.
 57
 58    Args:
 59        `fields` (list of FieldInfo): Master list to filter.
 60        `field_name` (StringTest): Look for fields with matching name.
 61        `cal_name` (StringTest): Look for fields with matching calibration name.
 62    """
 63    tests = []
 64    if field_name is not None:
 65        tests.append('name {t}'.format(t=field_name))
 66
 67    if cal_name is not None:
 68        tests.append('calname {t}'.format(t=cal_name))
 69
 70    if len(tests) > 0:
 71        logger.info('Filtering fields for {t}'.format(t=' and '.join(tests)))
 72
 73    for field in fields:
 74        if field_name is not None and not field_name.matches(field.name):
 75            continue
 76
 77        elif cal_name is not None and not cal_name.matches(field.calibration_name):
 78            continue
 79
 80        yield field
 81
 82
 83def show_fields(fields, target=sys.stdout):
 84    for field in fields:
 85        target.write("""Field {name}:
 86{i}Description: {desc}
 87{i}Datatype: {datatype}
 88{i}Length: {length}
 89{i}Calibration name: {cal}
 90""".format(i=INDENT,
 91           name=field.name,
 92           desc=field.description,
 93           datatype=field.datatype.value,
 94           length=field.length,
 95           cal=field.calibration_name))
 96
 97        if field.cal is not None:
 98            target.write('{i}Calibrations:\n'.format(i=INDENT))
 99            for sid in field.cal.sids():
100                target.write('{i}{i}{sid}:\n'.format(i=INDENT, sid=sid))
101                cal = field.cal[sid]
102                if isinstance(cal, PolyCalibration):
103                    target.write('{i}{i}{i}Polynomial:\n'.format(i=INDENT))
104
105                elif isinstance(cal, LinearCalibration):
106                    target.write('{i}{i}{i}Linear:\n'.format(i=INDENT))
107                    for pair in cal.pairs:
108                        target.write('{ii}{raw}: {cal}\n'.format(
109                            ii=INDENT * 4, raw=pair.raw, cal=pair.cal))
110
111                elif isinstance(cal, SQLCalibration):
112                    target.write('{i}{i}{i}SQL:\n'.format(i=INDENT))
113
114                else:
115                    assert False
116
117
118
119def main():
120    """Command line entry point"""
121    parser = ArgumentParser(__doc__)
122    parser.add_argument('--list-tables',
123                        action='store_true',
124                        help='Show all timeseries tables')
125    parser.add_argument('--list-fields',
126                        action='store_true',
127                        help='List fields')
128    parser.add_argument('--show-fields',
129                        action='store_true',
130                        help='Show information about selected field(s)')
131    parser.add_argument('--sys',
132                        action='store_true',
133                        help='Switch table processing to sys tables instead of timeseries')
134    parser.add_argument('--field-name',
135                        help='Filter to only matching named fields')
136    parser.add_argument('--cal-name',
137                        help='Filter for only fields using named calibration')
138    args = parser.parse_args()
139
140    if args.sys:
141        raise NotImplementedError("This code needs to be revisited: '{code}' : '{error}'".format(code='tables = project.all_sys_tables()', error='project is undefined'))
142        # tables = project.all_sys_tables()
143
144    else:
145        tables = SID().all_tables()
146
147    fields = extract_fields(tables)
148
149    if args.list_tables:
150        list_tables(tables)
151        parser.exit()
152
153    fields = find_fields(
154        fields,
155        field_name=StringTest(args.field_name),
156        cal_name=StringTest(args.cal_name))
157
158    if args.list_fields:
159        list_fields(fields)
160        parser.exit()
161
162    if args.show_fields:
163        show_fields(fields)
164        parser.exit()
165
166    parser.error('No actions specified')