Passed
Pull Request — dev (#1183)
by Patrik
04:02
created

solph._helpers.create_time_index()   C

Complexity

Conditions 11

Size

Total Lines 91
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 38
dl 0
loc 91
rs 5.4
c 0
b 0
f 0
cc 11
nop 6

How to fix   Long Method    Complexity   

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:

Complexity

Complex classes like solph._helpers.create_time_index() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# -*- coding: utf-8 -*-
2
3
"""
4
Private helper functions.
5
6
SPDX-FileCopyrightText: Uwe Krien <[email protected]>
7
SPDX-FileCopyrightText: Simon Hilpert
8
SPDX-FileCopyrightText: Cord Kaldemeyer
9
SPDX-FileCopyrightText: Stephan Günther
10
SPDX-FileCopyrightText: Birgit Schachler
11
SPDX-FileCopyrightText: David Fuhrländer
12
SPDX-FileCopyrightText: Johannes Röder
13
14
SPDX-License-Identifier: MIT
15
16
"""
17
18
import calendar
19
import datetime
20
from warnings import warn
21
22
import pandas as pd
23
from oemof.tools import debugging
24
25
26
def check_node_object_for_missing_attribute(obj, attribute):
27
    """Raises a predefined warning if object does not have attribute.
28
29
    Arguments
30
    ---------
31
32
    obj : python object
33
    attribute : (string) name of the attribute to test for
34
35
    """
36
    if not getattr(obj, attribute):
37
        warn_if_missing_attribute(obj, attribute)
38
39
40
def warn_if_missing_attribute(obj, attribute):
41
    """Raises warning if attribute is missing for given object"""
42
    msg = (
43
        "Attribute <{0}> is missing in Node <{1}> of {2}.\n"
44
        "If this is intended and you know what you are doing you can"
45
        "disable the SuspiciousUsageWarning globally."
46
    )
47
    warn(
48
        msg.format(attribute, obj.label, type(obj)),
49
        debugging.SuspiciousUsageWarning,
50
    )
51
52
53
def create_time_index(
54
    year: int = None,
55
    interval_length: float = None,
56
    number_of_intervals: int = None,
57
    start: datetime.date = None,
58
    number: int = None,
59
    interval: float = None,
60
):
61
    """
62
    Create a datetime index for one year.
63
64
    Notes
65
    -----
66
    To create 8760 hourly intervals for a non leap year a datetime index with
67
    8761 time points need to be created. So the number of time steps is always
68
    the number of intervals plus one.
69
70
    Parameters
71
    ----------
72
    year : int, datetime
73
        The year of the index.
74
        Used to automatically set start and number for the specific year.
75
    interval_length : float
76
        The length time interval in hours e.g. 0.5 for 30min or 2 for a
77
        two hour interval (default: 1).
78
    number_of_intervals : int
79
        The number of time intervals. By default number is calculated to create
80
        an index of one year. For a shorter or longer period the number of
81
        intervals can be set by the user.
82
    start : datetime.datetime or datetime.date
83
        Optional start time. If start is not set, 00:00 of the first day of
84
        the given year is the start time.
85
86
    Examples
87
    --------
88
    >>> len(create_time_index(2014))
89
    8761
90
    >>> len(create_time_index(2012))  # leap year
91
    8785
92
    >>> len(create_time_index(2014, interval=0.5))
93
    17521
94
    >>> len(create_time_index(2014, interval=0.5, number=10))
95
    11
96
    >>> len(create_time_index(2014, number=10))
97
    11
98
    >>> str(create_time_index(2014, interval=0.5, number=10)[-1])
99
    '2014-01-01 05:00:00'
100
    >>> str(create_time_index(2014, interval=2, number=10)[-1])
101
    '2014-01-01 20:00:00'
102
    """
103
    if number is not None:
104
        warnig_text = "The argument 'number_of_intervals' repleaces 'number'."
105
        if number_of_intervals is None:
106
            warn(warnig_text)
107
            number_of_intervals = number
108
        else:
109
            raise AttributeError(warnig_text + " You cannot set both!")
110
111
    if interval is not None:
112
        warnig_text = "The argument 'interval_length' repleaces 'interval'."
113
        if interval_length is None:
114
            warn(warnig_text)
115
            interval_length = interval
116
        else:
117
            raise AttributeError(warnig_text + " You cannot set both!")
118
    if interval_length is None:
119
        interval_length = 1
120
121
    if number_of_intervals is None:
122
        if calendar.isleap(year):
123
            hours_in_year = 8784
124
        else:
125
            hours_in_year = 8760
126
        number_of_intervals = round(hours_in_year / interval_length)
127
    if start is not None:
128
        if year is not None:
129
            raise ValueError(
130
                "Arguments 'start' and 'year' are mutually exclusive."
131
            )
132
    else:
133
        start = f"1/1/{year}"
134
    try:
135
        time_index = pd.date_range(
136
            start, periods=number_of_intervals + 1, freq=f"{interval_length}h"
137
        )
138
    except ValueError:
139
        # Pandas <2.2 compatibility
140
        time_index = pd.date_range(
141
            start, periods=number_of_intervals + 1, freq=f"{interval_length}H"
142
        )
143
    return time_index
144