db.results   A
last analyzed

Complexity

Total Complexity 20

Size/Duplication

Total Lines 263
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 146
dl 0
loc 263
rs 10
c 0
b 0
f 0
wmc 20

2 Functions

Rating   Name   Duplication   Size   Complexity  
B store_results() 0 72 8
D restore_results() 0 56 12
1
from sqlalchemy import ARRAY, Column, ForeignKey, Integer, String
2
from sqlalchemy.dialects.postgresql import DOUBLE_PRECISION
3
from sqlalchemy.ext.declarative import declarative_base
4
from sqlalchemy.orm import relationship
5
import pandas
6
7
from oemof.outputlib.processing import convert_keys_to_strings
8
9
"""
10
Module to store and restore input- and result-data from oemof into database.
11
Works with oemof version 2.0
12
13
Notes
14
-----
15
By now, table names are static - if you are not satisfied with resulting
16
table names, you have to change code yourself!
17
18
Examples
19
--------
20
The following code will setup your sqlalchemy session and
21
create all needed tables in database:
22
>>> from sqlalchemy import orm
23
>>> import sqlahelper
24
>>> from oemof.db import results
25
26
>>> engine = sqlalchemy.create_engine(db_url)
27
>>> sqlahelper.add_engine(engine)
28
>>> SqlAlchemySession = orm.sessionmaker(bind=engine)
29
>>> results.Base.metadata.bind = engine
30
>>> results.Base.metadata.create_all()
31
32
The following code stores your data into DB:
33
>>> sa_session = SqlAlchemySession()
34
>>> results.store_results(sa_session, input_dict, result_dict)
35
>>> sa_session.close()
36
37
The following code restores your data from DB:
38
>>> sa_session = SqlAlchemySession()
39
>>> input_dict, result_dict = results.restore_results(sa_session, result_id)
40
>>> sa_session.close()
41
"""
42
43
Base = declarative_base()
44
45
46
class OemofInputResult(Base):
47
    __tablename__ = 'stemp_oemof_input_result'
48
49
    input_result_id = Column(
50
        Integer,
51
        primary_key=True
52
    )
53
    input_id = Column(Integer, ForeignKey('stemp_oemof_data.data_id'))
54
    result_id = Column(Integer, ForeignKey('stemp_oemof_data.data_id'))
55
    input = relationship(
56
        "OemofData",
57
        backref="input",
58
        uselist=False,
59
        foreign_keys=[input_id],
60
    )
61
    result = relationship(
62
        "OemofData",
63
        backref="result",
64
        uselist=False,
65
        foreign_keys=[result_id],
66
    )
67
68
69
class OemofData(Base):
70
    __tablename__ = 'stemp_oemof_data'
71
72
    data_id = Column(
73
        Integer,
74
        primary_key=True
75
    )
76
    scalars = relationship("OemofScalar", cascade="delete")
77
    sequences = relationship("OemofSequence", cascade="delete")
78
79
80
class OemofScalar(Base):
81
    __tablename__ = 'stemp_oemof_scalar'
82
83
    scalar_id = Column(
84
        Integer,
85
        primary_key=True
86
    )
87
    data_id = Column(
88
        Integer, ForeignKey('stemp_oemof_data.data_id'))
89
    from_node = Column(
90
        String
91
    )
92
    to_node = Column(
93
        String
94
    )
95
    attribute = Column(
96
        String
97
    )
98
    value = Column(
99
        String
100
    )
101
    type = Column(
102
        String
103
    )
104
105
106
class OemofSequence(Base):
107
    __tablename__ = 'stemp_oemof_sequence'
108
109
    sequence_id = Column(
110
        Integer,
111
        primary_key=True
112
    )
113
    data_id = Column(
114
        Integer, ForeignKey('stemp_oemof_data.data_id'))
115
    from_node = Column(
116
        String
117
    )
118
    to_node = Column(
119
        String,
120
        nullable=True
121
    )
122
    attribute = Column(
123
        String
124
    )
125
    value = Column(
126
        ARRAY(DOUBLE_PRECISION)
127
    )
128
    type = Column(
129
        String
130
    )
131
132
133
def store_results(session, input_data, result_data):
134
    """
135
    Stores inputs and results from oemof into DB
136
137
    For each in entry in scalars and sequences of both input- and result-data
138
    an OemofScalar or OemofSequence is build and connected to an OemofData
139
    object representing either input or result data. At last, both OemofData
140
    objects are connected to OemofInputResult object and resulting index is
141
    returned.
142
143
    Parameters
144
    ----------
145
    session: sqlalchemy.session
146
        SQLAlchemy session build via sqlalchemy.orm.sessionmaker
147
    input_data: dict
148
        Output of oemof.outputlib.processing.param_results with nodes as str
149
        (use oemof.outputlib.processing.convert_keys_to_str if necessary)
150
    result_data: dict
151
        Output of oemof.outputlib.processing.param_results with nodes as str
152
        (use oemof.outputlib.processing.convert_keys_to_str if necessary)
153
154
    Returns
155
    -------
156
    int: Index of created OemofInputResult entry
157
    """
158
    # Check if nodes are strings:
159
    if not isinstance(next(iter(input_data)), str):
160
        input_data = convert_keys_to_strings(input_data)
161
    if not isinstance(next(iter(result_data)), str):
162
        result_data = convert_keys_to_strings(result_data)
163
164
    input_result = OemofInputResult()
165
    for input_result_attr, data in (
166
            ('input', input_data), ('result', result_data)):
167
        scalars = []
168
        sequences = []
169
        for (from_node, to_node), sc_sq_dict in data.items():
170
            for key, value in sc_sq_dict['scalars'].items():
171
                scalars.append(
172
                    OemofScalar(
173
                        from_node=from_node,
174
                        to_node=to_node,
175
                        attribute=key,
176
                        value=value,
177
                        type=type(value).__name__
178
                    )
179
                )
180
            session.add_all(scalars)
181
            for key, series in sc_sq_dict['sequences'].items():
182
                list_type = 'list'
183
                if isinstance(series, pandas.Series):
184
                    series = series.values.tolist()
185
                    list_type = 'series'
186
                sequences.append(
187
                    OemofSequence(
188
                        from_node=from_node,
189
                        to_node=to_node,
190
                        attribute=key,
191
                        value=series,
192
                        type=list_type
193
                    )
194
                )
195
            session.add_all(sequences)
196
        oemof_data = OemofData()
197
        oemof_data.scalars = scalars
198
        oemof_data.sequences = sequences
199
        setattr(input_result, input_result_attr, oemof_data)
200
    session.add(input_result)
201
    session.flush()
202
    result_id = input_result.input_result_id
203
    session.commit()
204
    return result_id
205
206
207
def restore_results(session, input_result_id):
208
    """
209
    Restores input and result data from OemofInputResult from DB
210
211
    Parameters
212
    ----------
213
    session: sqlalchemy.session
214
        SQLAlchemy session build via sqlalchemy.orm.sessionmaker
215
    input_result_id: int
216
        Index of OemofInputResult object to restore
217
218
    Returns
219
    -------
220
    (dict, dict):
221
        Restored input- and result-data
222
    """
223
    def type_conversion(value_str, value_type):
224
        if value_type == 'str':
225
            return value_str
226
        elif value_type == 'float':
227
            return float(value_str)
228
        elif value_type == 'int':
229
            return int(value_str)
230
        elif value_type == 'bool':
231
            return bool(value_str)
232
        else:
233
            raise TypeError('Unknown conversion type "' + value_type + '"')
234
235
    # Find results:
236
    input_result = session.query(OemofInputResult).filter(
237
        OemofInputResult.input_result_id == input_result_id).first()
238
    if input_result is None:
239
        raise IndexError(
240
            'Could not find OemofInputResult with ID #' + str(input_result_id))
241
242
    input_data = {}
243
    result_data = {}
244
    for input_result_attr, data in (
245
            ('input', input_data), ('result', result_data)):
246
        ir_attr = getattr(input_result, input_result_attr)
247
        for scalar in ir_attr.scalars:
248
            nodes = (scalar.from_node, scalar.to_node)
249
            if nodes not in data:
250
                data[nodes] = {'scalars': {}, 'sequences': {}}
251
            data[nodes]['scalars'][scalar.attribute] = type_conversion(
252
                scalar.value, scalar.type)
253
        for sequence in ir_attr.sequences:
254
            nodes = (sequence.from_node, sequence.to_node)
255
            if nodes not in data:
256
                data[nodes] = {'scalars': {}, 'sequences': {}}
257
            if sequence.type == 'series':
258
                series = pandas.Series(sequence.value)
259
            else:
260
                series = sequence.value
261
            data[nodes]['sequences'][sequence.attribute] = series
262
    return input_data, result_data
263