Completed
Push — master ( da0908...c8344f )
by P.R.
01:36
created

Type2Helper.__init__()   A

Complexity

Conditions 1

Size

Total Lines 51

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 51
ccs 8
cts 8
cp 1
rs 9.4109
cc 1
crap 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
"""
2
ETLT
3
4
Copyright 2016 Set Based IT Consultancy
5
6
Licence MIT
7
"""
8 1
import copy
9 1
import datetime
10
11
12 1
class Type2Helper:
13
    """
14
    A helper class for reference data with date intervals.
15
    """
16
17
    # ------------------------------------------------------------------------------------------------------------------
18 1
    def __init__(self, key_start_date, key_end_date, natural_key):
19
        """
20
        Object constructor.
21
22
        :param str key_start_date: The key of the start date in the rows.
23
        :param str key_end_date: The key of the end date in the rows.
24
        :param list[str] natural_key: The keys of the columns that form the natural key.
25
        """
26 1
        self._natural_key = list(natural_key)
27
        """
28
        The keys of the columns that form the natural key.
29
30
        :type list[str]:
31
        """
32
33 1
        self._key_end_date = key_end_date
34
        """
35
        The key of the end date in the rows.
36
37
        :type str:
38
        """
39 1
        self._key_start_date = key_start_date
40
        """
41
        The key of the start date in the rows.
42
43
        :type str:
44
        """
45
46 1
        self.rows = dict()
47
        """
48
        The data set.
49
50
        :type dict:
51
        """
52
53 1
        self._copy = True
54
        """
55
        If set to true a copy will be made from the original rows such that the original rows is not modified.
56
57
         :type bool:
58
        """
59
60 1
        self._date_type = ''
61 1
        """
62
        The type of the date fields.
63
        - date for datetime.date objects
64
        - str  for strings in ISO 8601 (YYYY-MM-DD) format
65
        - int for integers
66
67
        :type str:
68
        """
69
70
    # ------------------------------------------------------------------------------------------------------------------
71 1
    def _get_natural_key(self, row):
72
        """
73
        Returns the natural key in a row.
74
75
        :param dict row: The row.
76
77
        :rtype: tuple
78
        """
79 1
        ret = list()
80 1
        for key in self._natural_key:
81 1
            ret.append(row[key])
82
83 1
        return tuple(ret)
84
85
    # ------------------------------------------------------------------------------------------------------------------
86 1
    @staticmethod
87
    def _date2int(date):
88
        """
89
        Returns an integer representation of a date.
90
91
        :param str|datetime.date date: The date.
92
93
        :rtype: int
94
        """
95 1
        if isinstance(date, str):
96 1
            tmp = datetime.datetime.strptime(date, '%Y-%m-%d')
97 1
            return tmp.toordinal()
98
99
        if isinstance(date, datetime.date):
100
            return date.toordinal()
101
102
        if isinstance(date, int):
103
            return date
104
105
        raise ValueError('Unexpected type {0!s}'.format(date.__class__))
106
107
    # ------------------------------------------------------------------------------------------------------------------
108 1
    def _rows_date2int(self, rows):
109
        """
110
        Replaces start and end dates in a row set with their integer representation
111
112
        :param list[dict[str,T]] rows: The list of rows.
113
114
        :rtype: list[dict[str,T]]
115
        """
116 1
        ret = list()
117 1
        for row in rows:
118
            # Determine the type of dates based on the first start date.
119 1
            if not self._date_type:
120 1
                self._date_type = self._get_date_type(row[self._key_start_date])
121
122
            # Convert dates to integers.
123 1
            row[self._key_start_date] = self._date2int(row[self._key_start_date])
124 1
            row[self._key_end_date] = self._date2int(row[self._key_end_date])
125 1
            ret.append(row)
126
127 1
        return ret
128
129
    # ------------------------------------------------------------------------------------------------------------------
130 1
    def _rows_int2date(self, rows):
131
        """
132
        Replaces start and end dates in the row set with their integer representation
133
134
        :param list[dict[str,T]] rows: The list of rows.
135
        """
136 1
        for row in rows:
137 1
            if self._date_type == 'str':
138 1
                row[self._key_start_date] = datetime.date.fromordinal(row[self._key_start_date]).isoformat()
139 1
                row[self._key_end_date] = datetime.date.fromordinal(row[self._key_end_date]).isoformat()
140
            elif self._date_type == 'date':
141
                row[self._key_start_date] = datetime.date.fromordinal(row[self._key_start_date])
142
                row[self._key_end_date] = datetime.date.fromordinal(row[self._key_end_date])
143
            elif self._date_type == 'int':
144
                # Nothing to do.
145
                pass
146
            else:
147
                raise ValueError('Unexpected date type {0!s}'.format(self._date_type))
148
149
    # ------------------------------------------------------------------------------------------------------------------
150 1
    def _rows_sort(self, rows):
151
        """
152
        Returns a list of rows sorted by start and end date.
153
154
        :param list[dict[str,T]] rows: The list of rows.
155
156
        :rtype: list[dict[str,T]]
157
        """
158 1
        return sorted(rows, key=lambda row: (row[self._key_start_date], row[self._key_end_date]))
159
160
    # ------------------------------------------------------------------------------------------------------------------
161 1
    @staticmethod
162
    def _get_date_type(date):
163
        """
164
        Returns the type of a date.
165
166
        :param str|datetime.date date: The date.
167
168
        :rtype: str
169
        """
170 1
        if isinstance(date, str):
171 1
            return 'str'
172
173
        if isinstance(date, datetime.date):
174
            return 'date'
175
176
        if isinstance(date, int):
177
            return 'int'
178
179
        raise ValueError('Unexpected type {0!s}'.format(date.__class__))
180
181
    # ------------------------------------------------------------------------------------------------------------------
182 1
    def enumerate(self, name, start=1):
183
        """
184
        Enumerates all rows such that the natural key and the ordinal number are a unique key.
185
186
        :param str name: The key holding the ordinal number.
187
        :param start: The start of the ordinal numbers. Foreach natural key the first row has this ordinal number.
188
        """
189 1
        for natural_key, rows in self.rows.items():
190 1
            rows = self._rows_sort(rows)
191 1
            ordinal = start
192 1
            for row in rows:
193 1
                row[name] = ordinal
194 1
                ordinal += 1
195 1
            self.rows[natural_key] = rows
196
197
    # ------------------------------------------------------------------------------------------------------------------
198 1
    def get_rows(self, sort=False):
199
        """
200
        Returns the rows of this Type2Helper.
201
202
        :param bool sort: If True the rows are sorted by the natural key.
203
        """
204 1
        ret = []
205 1
        for _, rows in sorted(self.rows.items()) if sort else self.rows.items():
206 1
            self._rows_int2date(rows)
207 1
            ret.extend(rows)
208
209 1
        return ret
210
211
    # ------------------------------------------------------------------------------------------------------------------
212 1
    def prepare_data(self, rows):
213
        """
214
        Sets and prepares the rows. The rows are stored in groups in a dictionary. A group is a list of rows with the
215
        same natural key. The key in the dictionary is a tuple with the values of the natural key.
216
217
        :param list[dict] rows: The rows
218
        """
219 1
        self.rows = dict()
220 1
        for row in copy.copy(rows) if self._copy else rows:
221 1
            natural_key = self._get_natural_key(row)
222 1
            if natural_key not in self.rows:
223 1
                self.rows[natural_key] = list()
224 1
            self.rows[natural_key].append(row)
225
226
        # Convert begin and end dates to integers.
227 1
        self._date_type = None
228 1
        for natural_key, rows in self.rows.items():
229 1
            self.rows[natural_key] = self._rows_date2int(rows)
230
231
# ----------------------------------------------------------------------------------------------------------------------
232