Skip to content

Commit 7183fe2

Browse files
authored
Merge pull request #7510 from hozlucas28/Solution-50-Python
#50 - Python
2 parents 1ed7917 + a78ac2b commit 7183fe2

File tree

1 file changed

+369
-0
lines changed
  • Roadmap/50 - PLANIFICADOR DE OBJETIVOS DE AÑO NUEVO/python

1 file changed

+369
-0
lines changed
Lines changed: 369 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,369 @@
1+
# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring,consider-using-dict-items,line-too-long
2+
3+
from datetime import datetime
4+
from pathlib import Path
5+
from typing import TypedDict, Union, TypeVar, Iterable, Callable
6+
from uuid import UUID, uuid4
7+
8+
# ---------------------------------------------------------------------------- #
9+
# TYPES #
10+
# ---------------------------------------------------------------------------- #
11+
12+
YearPlan = TypedDict(
13+
"YearPlan",
14+
{
15+
"january": list[list[Union["Goal", int]]],
16+
"february": list[list[Union["Goal", int]]],
17+
"march": list[list[Union["Goal", int]]],
18+
"april": list[list[Union["Goal", int]]],
19+
"may": list[list[Union["Goal", int]]],
20+
"june": list[list[Union["Goal", int]]],
21+
"july": list[list[Union["Goal", int]]],
22+
"august": list[list[Union["Goal", int]]],
23+
"september": list[list[Union["Goal", int]]],
24+
"october": list[list[Union["Goal", int]]],
25+
"november": list[list[Union["Goal", int]]],
26+
"december": list[list[Union["Goal", int]]],
27+
},
28+
)
29+
30+
# ---------------------------------------------------------------------------- #
31+
# EXCEPTIONS #
32+
# ---------------------------------------------------------------------------- #
33+
34+
35+
class MonthsOutOfRangeException(Exception):
36+
def __init__(self) -> None:
37+
super().__init__("Months out of range")
38+
39+
40+
# ---------------------------------------------------------------------------- #
41+
# CLASSES #
42+
# ---------------------------------------------------------------------------- #
43+
44+
45+
# ----------------------------------- Goal ----------------------------------- #
46+
47+
48+
class Goal:
49+
__amount: int
50+
__description: str
51+
__uid: UUID
52+
__months_limit: int
53+
__units: str
54+
55+
def __init__(
56+
self, *, _amount: int, _description: str, _months_limit: int, _units: str
57+
) -> None:
58+
self.__amount = _amount
59+
self.__description = _description
60+
self.__uid = uuid4()
61+
self.__units = _units
62+
63+
if _months_limit < 1 or _months_limit > Goal.max_months_limit():
64+
raise MonthsOutOfRangeException()
65+
66+
self.__months_limit = _months_limit
67+
68+
@staticmethod
69+
def max_months_limit() -> int:
70+
return 12
71+
72+
@property
73+
def amount(self) -> int:
74+
return self.__amount
75+
76+
@property
77+
def description(self) -> str:
78+
return self.__description
79+
80+
@property
81+
def uid(self) -> UUID:
82+
return self.__uid
83+
84+
@property
85+
def months_limit(self) -> int:
86+
return self.__months_limit
87+
88+
@property
89+
def units(self) -> str:
90+
return self.__units
91+
92+
def to_year_plan(self) -> YearPlan:
93+
year_plan: YearPlan = {
94+
"january": [],
95+
"february": [],
96+
"march": [],
97+
"april": [],
98+
"may": [],
99+
"june": [],
100+
"july": [],
101+
"august": [],
102+
"september": [],
103+
"october": [],
104+
"november": [],
105+
"december": [],
106+
}
107+
108+
counter: int = self.__amount
109+
while counter:
110+
month_counter: int = self.__months_limit
111+
for _key in year_plan:
112+
if not counter or not month_counter:
113+
break
114+
115+
try:
116+
year_plan[_key][0][1] += 1
117+
except IndexError:
118+
year_plan[_key].append([self, 1])
119+
120+
month_counter -= 1
121+
counter -= 1
122+
123+
return year_plan
124+
125+
126+
# --------------------------------- YearGoals -------------------------------- #
127+
128+
129+
class YearGoals:
130+
__goals: list[Goal]
131+
__plan: YearPlan
132+
133+
def __init__(self) -> None:
134+
self.__goals = []
135+
136+
self.__plan = {
137+
"january": [],
138+
"february": [],
139+
"march": [],
140+
"april": [],
141+
"may": [],
142+
"june": [],
143+
"july": [],
144+
"august": [],
145+
"september": [],
146+
"october": [],
147+
"november": [],
148+
"december": [],
149+
}
150+
151+
@staticmethod
152+
def max_goals() -> int:
153+
return 10
154+
155+
@property
156+
def goals(self) -> list[Goal]:
157+
return self.__goals
158+
159+
@property
160+
def plan(self) -> YearPlan:
161+
return self.__plan
162+
163+
def __append_goal_to_plan(self, *, _goal: Goal) -> None:
164+
goal_year_plan: YearPlan = _goal.to_year_plan()
165+
166+
aux: YearPlan = self.plan
167+
168+
for _key in goal_year_plan:
169+
if goal_year_plan[_key]:
170+
aux[_key].append(*goal_year_plan[_key])
171+
continue
172+
break
173+
174+
self.__plan = aux
175+
176+
def __remove_goal_from_plan(self, *, uid: UUID) -> None:
177+
def __fn(element: list[Union[Goal, int]]) -> bool:
178+
return element[0].uid == uid # type: ignore
179+
180+
aux: YearPlan = self.plan
181+
182+
for _key in self.__plan:
183+
goal_i: int = index_fn(iterable=aux[_key], fn=__fn)
184+
if goal_i == -1:
185+
break
186+
187+
aux[_key] = [*aux[_key][:goal_i], aux[_key][goal_i + 1 :]]
188+
189+
def add_goal(self, *, new_goal: Goal) -> bool:
190+
if len(self.__goals) == YearGoals.max_goals():
191+
return False
192+
193+
new_goal_uid: UUID = new_goal.uid
194+
195+
goal_i: int = index_fn(
196+
iterable=self.__goals, fn=lambda value: value.uid == new_goal_uid
197+
)
198+
if goal_i != -1:
199+
return False
200+
201+
self.__goals.append(new_goal)
202+
self.__append_goal_to_plan(_goal=new_goal)
203+
204+
return True
205+
206+
def remove_goal(self, *, uid: UUID) -> bool:
207+
goal_i: int = index_fn(iterable=self.__goals, fn=lambda goal: goal.uid == uid)
208+
if goal_i == -1:
209+
return False
210+
211+
self.__goals = [*self.__goals[:goal_i], *self.__goals[goal_i + 1 :]]
212+
self.__remove_goal_from_plan(uid=uid)
213+
214+
return True
215+
216+
def save_plan(
217+
self,
218+
*,
219+
_path: str,
220+
row: Callable[[Goal, int, int], str],
221+
month_title: Callable[[str], str] = lambda month: month.capitalize(),
222+
) -> None:
223+
data: list[str] = []
224+
225+
for _key in self.__plan:
226+
goals_month: list[list[Union[Goal, int]]] = self.__plan[_key]
227+
228+
rows: list[str] = [month_title(_key)]
229+
for _i, [_goal, _amount] in enumerate(iterable=goals_month):
230+
rows.append(row(_goal, _amount, _i)) # type: ignore
231+
232+
data.append("\n".join(rows))
233+
data.append("\n\n")
234+
235+
with open(file=_path, mode="wt", encoding="utf-8") as file:
236+
file.writelines(data)
237+
file.close()
238+
239+
240+
# ---------------------------------------------------------------------------- #
241+
# UTILITIES #
242+
# ---------------------------------------------------------------------------- #
243+
244+
T = TypeVar("T")
245+
246+
247+
def index_fn(*, iterable: Iterable[T], fn: Callable[[T], bool]) -> int:
248+
for _i, element in enumerate(iterable=iterable):
249+
if fn(element):
250+
return _i
251+
return -1
252+
253+
254+
def goal_to_row(_goal: Goal, _amount: int, index: int) -> str:
255+
_desc: str = _goal.description
256+
_units: str = _goal.units
257+
_total_amount: int = _goal.amount
258+
return f"[ ] {index + 1}. {_desc} ({_amount} {_units}/mes). Total: {_total_amount}."
259+
260+
261+
# ---------------------------------------------------------------------------- #
262+
# MAIN #
263+
# ---------------------------------------------------------------------------- #
264+
265+
year_goals: YearGoals = YearGoals()
266+
267+
268+
print(
269+
"> Available operations:\n\n"
270+
+ " 1 - Add goal.\n"
271+
+ " 2 - Remove goal.\n"
272+
+ " 3 - Show goals.\n"
273+
+ " 4 - Show plan.\n"
274+
+ " 5 - Save plan.\n"
275+
+ " 0 - Exit."
276+
)
277+
278+
user_input: str = input("\n> Select an operation: ").strip()
279+
280+
while user_input != "0":
281+
match (user_input):
282+
case "1":
283+
print("\n> Complete the following goal data...")
284+
units: str = input("\n> Units: ").strip()
285+
amount: int = int(input("\n> Amount (as a number): ").strip())
286+
description: str = input("\n> Description: ").strip()
287+
288+
months_limit: int = int(input("\n> Months limit (as a number): ").strip())
289+
while months_limit < 1 or months_limit > Goal.max_months_limit():
290+
print(
291+
"\n> Error! Months limit must be between 1 and 12 (both included). Try again..."
292+
)
293+
months_limit = int(input("\n> Months limit (as a number): ").strip())
294+
295+
goal: Goal = Goal(
296+
_amount=amount,
297+
_description=description,
298+
_months_limit=months_limit,
299+
_units=units,
300+
)
301+
302+
if year_goals.add_goal(new_goal=goal):
303+
print("\n> Goal added!")
304+
else:
305+
print("\n> An error occurred! The goal was not added.")
306+
307+
case "2":
308+
goal_uid: UUID = UUID(input("\n> Goal id to remove: ").strip())
309+
310+
if year_goals.remove_goal(uid=goal_uid):
311+
print(f'\n> Goal "{goal_uid}" removed!')
312+
else:
313+
print("\n> An error occurred! The goal was not removed.")
314+
315+
case "3":
316+
goals_rows: list[str] = []
317+
for _goal in year_goals.goals:
318+
goals_rows.append(
319+
f'\n• Goal "{_goal.uid}"...'
320+
+ f'\n\n - ID: "{_goal.uid}".'
321+
+ f'\n - Description: "{_goal.description}".'
322+
+ f"\n - Months limit: {_goal.months_limit}."
323+
+ f"\n - Amount: {_goal.amount}."
324+
+ f'\n - Units: "{_goal.units}".'
325+
)
326+
327+
if goals_rows:
328+
print("\n".join(goals_rows))
329+
330+
case "4":
331+
plan: YearPlan = year_goals.plan
332+
333+
plan_rows: list[str] = []
334+
for key in plan:
335+
goals: list[list[Union[Goal, int]]] = plan[key]
336+
337+
plan_row: str = f"\n{key.capitalize()}:"
338+
for i, [goal, amount] in enumerate(iterable=goals): # type: ignore
339+
desc: str = goal.description
340+
units: str = goal.units
341+
total_amount: int = goal.amount
342+
plan_row += f"\n[ ] {i + 1}. {desc} ({amount} {units}/mes). Total: {total_amount}."
343+
344+
plan_rows.append(plan_row)
345+
346+
if plan_rows:
347+
print("\n".join(plan_rows))
348+
349+
case "5":
350+
current_date: datetime = datetime.now()
351+
path: str = f"{Path().absolute()}\\plan-{current_date.year}.txt"
352+
year_goals.save_plan(_path=path, row=goal_to_row)
353+
354+
print(f'\n> Plan saved in "{path}" path.')
355+
356+
case "_":
357+
print("\n> Invalid operation! Try again...")
358+
359+
print(
360+
"\n> Available operations:\n\n"
361+
+ " 1 - Add goal.\n"
362+
+ " 2 - Remove goal.\n"
363+
+ " 3 - Show goals.\n"
364+
+ " 4 - Show plan.\n"
365+
+ " 5 - Save plan.\n"
366+
+ " 0 - Exit."
367+
)
368+
369+
user_input = input("\n> Select an operation: ").strip()

0 commit comments

Comments
 (0)