From 5cea7c9b8413f030b5c2e9203f1a4340743ad7a9 Mon Sep 17 00:00:00 2001 From: Matteo Pelliccione Date: Sat, 3 Feb 2024 00:34:23 +0100 Subject: [PATCH] Refactoring of project structure (#4) * Refactoring * Refactoring * gitignore resources folder * Refactoring * README to reflect changes to project structure --- .gitignore | 11 ++--- README.md | 3 +- env.example | 6 +-- pyproject.toml | 18 ++++++++ requirements.txt | 6 +-- src/fantaformazionireminder/__init__.py | 0 src/fantaformazionireminder/config.py | 28 ++++++++++++ .../constants/__init__.py | 0 .../constants/notifications_interval.py | 0 .../fantaformazionireminder/main.py | 44 +++++++------------ src/fantaformazionireminder/utils/__init__.py | 0 .../utils/clean_dates.py | 17 +++---- .../utils/default_dates.py | 20 ++++----- .../utils/expiry_message.py | 3 +- 14 files changed, 89 insertions(+), 67 deletions(-) create mode 100644 pyproject.toml create mode 100644 src/fantaformazionireminder/__init__.py create mode 100644 src/fantaformazionireminder/config.py create mode 100644 src/fantaformazionireminder/constants/__init__.py rename config.py => src/fantaformazionireminder/constants/notifications_interval.py (100%) rename main.py => src/fantaformazionireminder/main.py (92%) create mode 100644 src/fantaformazionireminder/utils/__init__.py rename clean_wiki_data.py => src/fantaformazionireminder/utils/clean_dates.py (79%) rename write_default_dates.py => src/fantaformazionireminder/utils/default_dates.py (68%) rename utils.py => src/fantaformazionireminder/utils/expiry_message.py (99%) diff --git a/.gitignore b/.gitignore index 59e2c08..de0bfe8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # Specific -data/ +resources/ # Byte-compiled / optimized / DLL files __pycache__/ @@ -88,7 +88,7 @@ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: -# .python-version +.python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. @@ -160,10 +160,7 @@ cython_debug/ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ +.idea/ # IDEs -.vscode/ - -TODO.md -.python-version \ No newline at end of file +.vscode/ \ No newline at end of file diff --git a/README.md b/README.md index 7ba551c..9ae81be 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ Before running the bot, you'll need the following: ```bash pip install -r requirements.txt + pip install -e . ``` 4. Create a `.env` file in the project directory. @@ -67,7 +68,7 @@ Before running the bot, you'll need the following: 5. Start the bot: ```bash - python main.py + python src\fantaformazionireminder\main.py ``` Now, your Fanta Formazioni Reminder bot should be up and running. diff --git a/env.example b/env.example index 4a16102..5bff23e 100644 --- a/env.example +++ b/env.example @@ -5,9 +5,9 @@ TOKEN=YOUR_BOT_TOKEN BOT_USERNAME=@your_username_bot # Filepaths -SAVED_DATES_FILEPATH=data/saved_dates.pkl -CHAT_IDS_FILEPATH=data/chat_ids.pkl +SAVED_DATES_FILEPATH=saved_dates.pkl +CHAT_IDS_FILEPATH=chat_ids.pkl # Serie A Calendar SERIE_A_CALENDAR_URL=https://fixturedownload.com/download/serie-a-2023-WEuropeStandardTime.csv -SERIE_A_CALENDAR_PATH=data/seriea_calendar.csv \ No newline at end of file +SERIE_A_CALENDAR_PATH=seriea_calendar.csv \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..42ddcab --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,18 @@ +[project] +name = "fantaformazionireminder" +version = "0.1.0" +dependencies = [ + "python-dotenv", + "python-telegram-bot[job_queue]>=20.5", + "requests", +] +requires-python = ">=3.11.4" +authors = [{ name = "Matteo Pelliccione", email = "mat.pelliccione@gmail.com" }] +description = "Telegram Bot to remind you of Fantacalcio's line-up" +readme = "README.md" +license = { file = "LICENSE" } +classifiers = ["Programming Language :: Python :: 3.11.4"] + +[build-system] +requires = ["setuptools >= 69.0"] +build-backend = "setuptools.build_meta" diff --git a/requirements.txt b/requirements.txt index 39d7390..bbb57d5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -python-dotenv==1.0.0 -python-telegram-bot[job_queue]==20.5 -requests==2.31.0 \ No newline at end of file +python-dotenv==1.0.1 +python-telegram-bot[job-queue]>=20.5 +requests==2.31.0 diff --git a/src/fantaformazionireminder/__init__.py b/src/fantaformazionireminder/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/fantaformazionireminder/config.py b/src/fantaformazionireminder/config.py new file mode 100644 index 0000000..c0c36e1 --- /dev/null +++ b/src/fantaformazionireminder/config.py @@ -0,0 +1,28 @@ +import os +from typing import Final + +from dotenv import load_dotenv + +# Load .env +load_dotenv() + + +# Values +BASE_RESOURCES_PATH: Final = os.path.join( + os.getcwd(), "src\\fantaformazionireminder\\resources" +) + +TOKEN: Final[str] = os.getenv("TOKEN") +BOT_USERNAME: Final[str] = os.getenv("BOT_USERNAME") + +SAVED_DATES_FILEPATH: Final[str] = os.path.join( + BASE_RESOURCES_PATH, os.getenv("SAVED_DATES_FILEPATH") +) +CHAT_IDS_FILEPATH: Final[str] = os.path.join( + BASE_RESOURCES_PATH, os.getenv("CHAT_IDS_FILEPATH") +) + +SERIE_A_CALENDAR_PATH: Final = os.path.join( + BASE_RESOURCES_PATH, os.getenv("SERIE_A_CALENDAR_PATH") +) +SERIE_A_CALENDAR_URL: Final = os.getenv("SERIE_A_CALENDAR_URL") diff --git a/src/fantaformazionireminder/constants/__init__.py b/src/fantaformazionireminder/constants/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/config.py b/src/fantaformazionireminder/constants/notifications_interval.py similarity index 100% rename from config.py rename to src/fantaformazionireminder/constants/notifications_interval.py diff --git a/main.py b/src/fantaformazionireminder/main.py similarity index 92% rename from main.py rename to src/fantaformazionireminder/main.py index a4cec23..accd26a 100644 --- a/main.py +++ b/src/fantaformazionireminder/main.py @@ -1,9 +1,8 @@ -import os import pickle from datetime import datetime, time, timedelta -from typing import Final -from dotenv import load_dotenv +import config +from constants.notifications_interval import NOTIFICATIONS_INTERVAL from telegram import Update from telegram.ext import ( Application, @@ -13,34 +12,23 @@ MessageHandler, filters, ) +from utils.expiry_message import get_expiry_message +from src.fantaformazionireminder.utils.default_dates import write_default_dates -from config import NOTIFICATIONS_INTERVAL -from utils import get_expiry_message -from write_default_dates import write_default_dates - -# Env -# Load environment variables from .env file -load_dotenv() - -# Access constants from environment variables -TOKEN: Final[str] = os.getenv("TOKEN") -BOT_USERNAME: Final[str] = os.getenv("BOT_USERNAME") - -SAVED_DATES_FILEPATH: Final[str] = os.getenv("SAVED_DATES_FILEPATH") -CHAT_IDS_FILEPATH: Final[str] = os.getenv("CHAT_IDS_FILEPATH") # Load active chat IDs from a file or initialize an empty list try: - with open(CHAT_IDS_FILEPATH, "rb") as file: + with open(config.CHAT_IDS_FILEPATH, "rb") as file: print(f"[CHAT_ID][FILE_OPEN] File {file.name} opened!") active_chat_ids = pickle.load(file) except FileNotFoundError: print("[CHAT_ID][FILE_NOT_FOUND] File not found") active_chat_ids = [] + # Load saved dates from a file or initialize an empty list try: - with open(SAVED_DATES_FILEPATH, "rb") as file: + with open(config.SAVED_DATES_FILEPATH, "rb") as file: print(f"[DATE][FILE_OPEN] File {file.name} opened!") saved_dates = pickle.load(file) except FileNotFoundError: @@ -49,7 +37,7 @@ ) write_default_dates() try: - with open(SAVED_DATES_FILEPATH, "rb") as file: + with open(config.SAVED_DATES_FILEPATH, "rb") as file: print(f"[DATE][FILE_OPEN] File {file.name} opened!") saved_dates = pickle.load(file) except FileNotFoundError: @@ -63,7 +51,7 @@ async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE): if chat_id not in active_chat_ids: active_chat_ids.append(chat_id) - with open(CHAT_IDS_FILEPATH, "wb") as file: + with open(config.CHAT_IDS_FILEPATH, "wb") as file: pickle.dump(active_chat_ids, file) print( f"[CHAT_ID][FILE_UPDATE] {active_chat_ids[-1]} saved in file {file.name}" @@ -183,7 +171,7 @@ async def save_date(update, date_str: str): try: # Parse the date string to a datetime object date_obj = datetime.strptime(date_str, "%d/%m/%Y,%H:%M") - saved_date_obj = (date_obj - timedelta(minutes=5)) + saved_date_obj = date_obj - timedelta(minutes=5) if date_obj < datetime.now(): await update.message.reply_text( @@ -199,7 +187,7 @@ async def save_date(update, date_str: str): saved_dates.append((chat_id, saved_date_obj)) # Save the updated list to the file - with open(SAVED_DATES_FILEPATH, "wb") as file: + with open(config.SAVED_DATES_FILEPATH, "wb") as file: pickle.dump(saved_dates, file) print( f"[DATE][FILE_UPDATE] {saved_dates[-1]} saved in file {file.name}" @@ -264,8 +252,8 @@ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE): ) if message_type == "group" or message_type == "supergroup": - if BOT_USERNAME in text: - new_text: str = text.replace(BOT_USERNAME, "").strip() + if config.BOT_USERNAME in text: + new_text: str = text.replace(config.BOT_USERNAME, "").strip() response: str = handle_response(new_text) else: return @@ -380,7 +368,7 @@ def remove_expired_dates(): ] # Save the updated list to the file - with open(SAVED_DATES_FILEPATH, "wb") as file: + with open(config.SAVED_DATES_FILEPATH, "wb") as file: pickle.dump(updated_saved_dates, file) saved_dates = updated_saved_dates @@ -389,7 +377,7 @@ def remove_expired_dates(): # Application if __name__ == "__main__": print("[SELF] Starting bot...") - app = Application.builder().token(TOKEN).build() + app = Application.builder().token(config.TOKEN).build() job_queue = app.job_queue # Cleanup @@ -428,4 +416,4 @@ def remove_expired_dates(): # Polling print("[SELF] Polling...") - app.run_polling(poll_interval=1) \ No newline at end of file + app.run_polling(poll_interval=1) diff --git a/src/fantaformazionireminder/utils/__init__.py b/src/fantaformazionireminder/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/clean_wiki_data.py b/src/fantaformazionireminder/utils/clean_dates.py similarity index 79% rename from clean_wiki_data.py rename to src/fantaformazionireminder/utils/clean_dates.py index 5dbad85..dfdd137 100644 --- a/clean_wiki_data.py +++ b/src/fantaformazionireminder/utils/clean_dates.py @@ -1,20 +1,13 @@ import csv -import os from datetime import datetime, timedelta -from typing import Final, List +from typing import List import requests -from dotenv import load_dotenv -# Load .env -load_dotenv() +from fantaformazionireminder import config -# Constants -CSV_PATH: Final = os.getenv("SERIE_A_CALENDAR_PATH") -CSV_URL: Final = os.getenv("SERIE_A_CALENDAR_URL") - -def download_csv(url, dest_path) -> None: +def __download_csv(url, dest_path) -> None: response = requests.get(url) with open(dest_path, "wb") as file: file.write(response.content) @@ -22,10 +15,10 @@ def download_csv(url, dest_path) -> None: def get_cleaned_dates() -> List[datetime]: # Download the CSV file - download_csv(CSV_URL, CSV_PATH) + __download_csv(config.SERIE_A_CALENDAR_URL, config.SERIE_A_CALENDAR_PATH) # Read the data from the CSV file - with open(CSV_PATH, newline="", encoding="utf-8") as csvfile: + with open(config.SERIE_A_CALENDAR_PATH, newline="", encoding="utf-8") as csvfile: reader = csv.DictReader(csvfile) # Initialize a dictionary to store the earliest date for each round diff --git a/write_default_dates.py b/src/fantaformazionireminder/utils/default_dates.py similarity index 68% rename from write_default_dates.py rename to src/fantaformazionireminder/utils/default_dates.py index b5e5efd..c4e54d1 100644 --- a/write_default_dates.py +++ b/src/fantaformazionireminder/utils/default_dates.py @@ -1,27 +1,22 @@ -import os import pickle -from dotenv import load_dotenv -from typing import Final -from clean_wiki_data import get_cleaned_dates +import config +from utils.clean_dates import get_cleaned_dates -# Load .env -load_dotenv() - -# Constants -SAVED_DATES_FILEPATH: Final = os.getenv("SAVED_DATES_FILEPATH") def __load_saved_dates(): try: - with open(SAVED_DATES_FILEPATH, "rb") as file: + with open(config.SAVED_DATES_FILEPATH, "rb") as file: return pickle.load(file) except FileNotFoundError: return [] + def __save_dates(saved_dates): - with open(SAVED_DATES_FILEPATH, "wb") as file: + with open(config.SAVED_DATES_FILEPATH, "wb") as file: pickle.dump(saved_dates, file) + def write_default_dates(): # Load the existing saved_dates or initialize an empty list saved_dates = __load_saved_dates() @@ -37,5 +32,6 @@ def write_default_dates(): print("[DEFAULT_DATE][FILE_UPDATE] Default dates added to saved_dates.") + if __name__ == "__main__": - write_default_dates() \ No newline at end of file + write_default_dates() diff --git a/utils.py b/src/fantaformazionireminder/utils/expiry_message.py similarity index 99% rename from utils.py rename to src/fantaformazionireminder/utils/expiry_message.py index e792835..8002db6 100644 --- a/utils.py +++ b/src/fantaformazionireminder/utils/expiry_message.py @@ -1,9 +1,10 @@ from datetime import timedelta + # TODO better handling of time_difference to let the message be more variable def get_expiry_message(time_difference: timedelta, saved_date: any): processed_time = time_difference.total_seconds() - + if processed_time >= 80000: time_word = "24 ore" elif processed_time >= 59 and processed_time <= 61: