/
esame.py
208 lines (169 loc) · 7.26 KB
/
esame.py
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# ===================== #
# Cutom exception class #
# ===================== #
class ExamException(Exception):
"""
Exception class for exam-related errors.
"""
pass
# ============= #
# CSVFile class #
# ============= #
class CSVFile:
"""
Represents a CSV file.
Attributes:
name (str): The name of the file.
readable (bool): Indicates if the file is readable.
"""
def __init__(self, name):
"""
Initializes an instance of the Exam class.
Parameters:
name (str): The name of the file to be opened.
Raises:
ExamException: If there is an error opening the file.
"""
# Set the file name
self.name = name
# Set the readability flag to False
self.readable = False
# Test if the file is readable
try:
# Open the file
my_file = open(self.name, 'r')
# Read the first line
my_file.readline()
# Set the flag to True
self.readable = True
except Exception as e:
# If the file is not readable, raise an exception
raise ExamException("Errore in apertura del file: {}".format(e))
# ======================= #
# CSVTimeseriesFile class #
# ======================= #
class CSVTimeSeriesFile(CSVFile):
"""
Represents a CSV file containing timeseries data.
Attributes:
name (str): The name of the file.
"""
def __init__(self, name):
"""
Initializes a new instance of the CSVTimeseriesFile class.
Args:
name (str): The name of the file.
"""
# Set the file name
self.name = name
def get_data(self):
"""
Reads the CSV file and returns the timeseries data.
Returns:
list: A list of nested lists representing the timeseries data.
Each nested list contains a date (in the format YYYY-MM) and a numeric value.
"""
# Initialize the list
data = []
# Test if the file is openable
try:
file = open(self.name, 'r')
except Exception as e:
# If the file is not openable, raise an exception
raise ExamException("Errore in apertura del file: {}".format(e))
# Read each line of the file
for line in file:
# The key value pair is split at the comma
elements = line.split(',')
# Remove leading and trailing whitespaces
elements[0] = elements[0].strip()
# Any string shorter or longer than 7 characters is not a date in the format YYYY-MM
if len(elements[0]) != 7:
continue
# Any string that doesn't have a '-' in the 5th position is not a date in the format YYYY-MM
if elements[0][4] != '-':
continue
try:
# Extract the year and the month
year = int(elements[0][0:4])
month = int(elements[0][5:7])
except ValueError:
# Catch only ValueError exceptions: any string whose first 4 or last 2 characters are not numeric is not a date in the format YYYY-MM
continue
# Any string whose last 2 characters are not between 1 and 12 is not a date in the format YYYY-MM
if month < 1 or month > 12:
continue
if len(data) > 0:
# Check if the year is older than the last year in the list or if the year is the same and the month is older than or the same of the last month in the list
if year < int(data[-1][0][0:4]) or (year == int(data[-1][0][0:4]) and month <= int(data[-1][0][5:7])):
if year == int(data[-1][0][0:4]) and month == int(data[-1][0][5:7]):
# If the date is duplicated, raise an exception
raise ExamException("Errore: date duplicate")
else:
# If the date older than the last date in the list, raise an exception
raise ExamException("Errore: date non in ordine")
# Check if the value of the second element is numeric and integer
try:
elements[1] = int(elements[1])
except ValueError:
# If the values are not numeric and integer, skip the line
continue
# Add the key value pair to the list
data.append(elements[0:2])
# Close the file
file.close()
if len(data) == 0:
# If the list is empty, raise an exception
raise ExamException("Errore: nessun dato valido presente")
# Return the list of nested lists
return data
# ============== #
# find_min_max() #
# ============== #
def find_min_max(data):
"""
Finds the minimum and maximum values for each year in a list of nested lists.
If the year has more than one month as maximum or minimum, every of these months must be present in the output.
If the year has all the months with the same value, all the months must be present in the output, both in the maximum and in the minimum field.
Args:
data (list): A list of nested lists. Each nested list contains a date (in the format YYYY-MM) and a numeric value.
Returns:
dict: A dictionary containing the string of the year as key and a dictionary with "min" and "max" as keys and a list with the minimum and maximum month(s) as values.
"""
# Create the dictionary
result = {}
# Create the dictionary for each year
years = {}
# Iterate over the list
for element in data:
# Extract the year and the month
year = element[0][0:4]
month = element[0][5:7]
# If the year is not in the dictionary, add it
if year not in years:
years[year] = []
# Add the month and the value to the list of the year
years[year].append([month, element[1]])
# Iterate over the dictionary
for year in list(years):
# Sort the values of the year
# Note: python .sort() method is stable, so if two months have the same value their order will be ascending, as they were read from the file
years[year].sort(key=lambda tuple: tuple[1])
# Add the minimum and maximum values to the dictionary
result[year] = {"min": [years[year][0][0]], "max": [years[year][-1][0]]}
# Check if the year has more than one month as minimum
counter = 1
while counter < len(years[year]) and years[year][counter][1] == years[year][0][1]:
# Append the month to the list
result[year]["min"].append(years[year][counter][0])
counter += 1
# Note: the months are already sorted ascendingly, as they were read from the file
# Check if the year has more than one month as maximum
counter = 2
while counter <= len(years[year]) and years[year][-counter][1] == years[year][-1][1]:
# Append the month to the list
result[year]["max"].append(years[year][-counter][0])
counter += 1
# Reverse the lists, since the months were appended in descending order
result[year]["max"].reverse()
return result