File: report.py

package info (click to toggle)
python-pbcommand 2.1.1%2Bgit20231020.28d1635-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,016 kB
  • sloc: python: 7,676; makefile: 220; sh: 73
file content (185 lines) | stat: -rw-r--r-- 4,954 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
"""
Loading a report from JSON

This manual marshalling/de-marshalling is not awesome.
"""

import json
import logging
import uuid as U

from pbcommand.models.report import (Report, Plot, PlotGroup, Attribute,
                                     Table, Column, ReportSpec, PlotlyPlot)
from pbcommand.schemas import validate_report, validate_report_spec


log = logging.getLogger(__name__)

__all__ = [
    "load_report_from_json",
    "load_report_from",
    "load_report_spec_from_json",
]


def _to_id(s):
    if '.' in s:
        return s.split('.')[-1]
    else:
        return s


def _to_plot(d):
    id_ = _to_id(d['id'])
    caption = d.get('caption', None)
    image = d['image']
    thumbnail = d.get('thumbnail', None)
    title = d.get('title', None)
    plot_type = d.get("plotType", Plot.PLOT_TYPE)
    plotly_version = d.get("plotlyVersion", None)
    if plot_type == Plot.PLOT_TYPE:
        return Plot(id_, image, caption=caption,
                    thumbnail=thumbnail, title=title)
    elif plot_type == PlotlyPlot.PLOT_TYPE:
        return PlotlyPlot(id_, image, caption=caption, thumbnail=thumbnail,
                          title=title, plotly_version=plotly_version)
    else:
        raise ValueError("Unrecognized plotType '{t}'".format(t=plot_type))


def _to_plot_group(d):
    id_ = _to_id(d['id'])
    legend = d.get('legend', None)
    thumbnail = d.get('thumbnail', None)
    # is this optional?
    title = d.get('title', None)

    if 'plots' in d:
        plots = [_to_plot(pd) for pd in d['plots']]
    else:
        plots = []

    return PlotGroup(id_, title=title, legend=legend, plots=plots,
                     thumbnail=thumbnail)


def _to_attribute(d):
    id_ = _to_id(d['id'])
    name = d.get('name', None)
    # this can't be none
    value = d['value']
    return Attribute(id_, value, name=name)


def _to_column(d):
    id_ = _to_id(d['id'])
    header = d.get('header', None)
    values = d.get('values', [])
    return Column(id_, header=header, values=values)


def _to_table(d):
    id_ = _to_id(d['id'])
    title = d.get('title', None)

    columns = []
    for column_d in d.get('columns', []):
        c = _to_column(column_d)
        columns.append(c)

    # assert that all the columns have the same number of values
    nvalues = {len(c.values) for c in columns}
    assert len(nvalues) == 1

    return Table(id_, title=title, columns=columns)


def dict_to_report(dct):
    # Use `load_report_from` instead.
    # FIXME. Add support for different version schemas in a cleaner, more
    # concrete manner.

    report_id = dct['id']

    # Make this optional for now
    report_uuid = dct.get('uuid', str(U.uuid4()))

    tags = dct.get('tags', [])

    # Make sure the UUID is well formed
    _ = U.UUID(report_uuid)

    # Legacy Reports > 0.3.9 will not have the title key
    title = dct.get('title', "Report {i}".format(i=report_id))

    plot_groups = []
    if 'plotGroups' in dct:
        pg = dct['plotGroups']
        if pg:
            plot_groups = [_to_plot_group(d) for d in pg]

    attributes = []
    for r_attr in dct.get('attributes', []):
        attr = _to_attribute(r_attr)
        attributes.append(attr)

    tables = []
    for table_d in dct.get('tables', []):
        t = _to_table(table_d)
        tables.append(t)

    report = Report(report_id,
                    title=title,
                    plotgroups=plot_groups,
                    tables=tables,
                    attributes=attributes,
                    dataset_uuids=dct.get('dataset_uuids', ()),
                    uuid=report_uuid, tags=tags)

    return report


def __load_json_or_dict(processor_func):
    def wrapper(json_path_or_dict):
        if isinstance(json_path_or_dict, dict):
            return processor_func(json_path_or_dict)
        else:
            with open(json_path_or_dict, 'r') as f:
                d = json.loads(f.read())
            return processor_func(d)
    return wrapper


def load_report_from(json_path_or_dict):
    """
    Load a Report from a raw dict or path to JSON file

    :param json_path_or_dict:
    :type json_path_or_dict: dict | str
    :return:
    """
    return __load_json_or_dict(dict_to_report)(json_path_or_dict)


def load_report_from_json(json_file):
    """Convert a report json file to Report instance."""
    # This should go way in favor of `load_report_from`
    return load_report_from(json_file)


def _to_report(nfiles, attribute_id, report_id):
    # this should have version of the bax/bas files, chemistry
    attributes = [Attribute(attribute_id, nfiles)]
    return Report(report_id, attributes=attributes)


def fofn_to_report(nfofns):
    return _to_report(nfofns, "nfofns", "fofn_report")


def load_report_spec_from_json(json_file, validate=True):
    with open(json_file, 'r') as f:
        d = json.loads(f.read())
        if validate:
            validate_report_spec(d)
        return ReportSpec.from_dict(d)