From f65a42cdce3e9a8a00eb8489a92f61760916d078 Mon Sep 17 00:00:00 2001 From: Bernd Date: Sun, 20 Jul 2025 13:21:00 +0500 Subject: [PATCH] integrated Python's logging module (wrap I/O operations with try/except, log exceptions to file) --- .gitignore | 1 + backend.py | 228 +++++++++++++++++++++++++++++++--------------------- frontend.py | 52 ++++++++---- 3 files changed, 175 insertions(+), 106 deletions(-) diff --git a/.gitignore b/.gitignore index fb1768e..b20174f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ anime_backlog.db images/ __pycache__/ +anime_tracker.log diff --git a/backend.py b/backend.py index 6fe0866..a981f05 100644 --- a/backend.py +++ b/backend.py @@ -1,5 +1,10 @@ import sqlite3 import csv +import logging + +# Set up logging +logging.basicConfig(filename='anime_tracker.log', level=logging.ERROR, + format='%(asctime)s - %(levelname)s - %(message)s') class AnimeBackend: def __init__(self): @@ -7,114 +12,157 @@ class AnimeBackend: self.create_table() def create_table(self): - cursor = self.db.cursor() - cursor.execute(""" - CREATE TABLE IF NOT EXISTS anime ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL, - year INTEGER NOT NULL, - season TEXT, - status TEXT NOT NULL DEFAULT 'unwatched', - type TEXT, - comment TEXT, - url TEXT - ) - """) - self.db.commit() + try: + cursor = self.db.cursor() + cursor.execute(""" + CREATE TABLE IF NOT EXISTS anime ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + year INTEGER NOT NULL, + season TEXT, + status TEXT NOT NULL DEFAULT 'unwatched', + type TEXT, + comment TEXT, + url TEXT + ) + """) + self.db.commit() + except Exception as e: + logging.error(f"Error creating table: {e}") def get_pre_2010_entries(self): - cursor = self.db.cursor() - cursor.execute("SELECT * FROM anime WHERE year < 2010 ORDER BY year DESC, name") - return cursor.fetchall() + try: + cursor = self.db.cursor() + cursor.execute("SELECT * FROM anime WHERE year < 2010 ORDER BY year DESC, name") + return cursor.fetchall() + except Exception as e: + logging.error(f"Error getting pre-2010 entries: {e}") + return [] def get_years(self): - cursor = self.db.cursor() - cursor.execute("SELECT DISTINCT year FROM anime WHERE year >= 2010 ORDER BY year DESC") - return [row[0] for row in cursor.fetchall()] + try: + cursor = self.db.cursor() + cursor.execute("SELECT DISTINCT year FROM anime WHERE year >= 2010 ORDER BY year DESC") + return [row[0] for row in cursor.fetchall()] + except Exception as e: + logging.error(f"Error getting years: {e}") + return [] def get_entries_for_season(self, year, season): - cursor = self.db.cursor() - cursor.execute("SELECT * FROM anime WHERE year = ? AND season = ? ORDER BY name", (year, season)) - return cursor.fetchall() + try: + cursor = self.db.cursor() + cursor.execute("SELECT * FROM anime WHERE year = ? AND season = ? ORDER BY name", (year, season)) + return cursor.fetchall() + except Exception as e: + logging.error(f"Error getting entries for season {season} in year {year}: {e}") + return [] def get_anime_by_id(self, anime_id): - cursor = self.db.cursor() - cursor.execute("SELECT * FROM anime WHERE id = ?", (anime_id,)) - return cursor.fetchone() + try: + cursor = self.db.cursor() + cursor.execute("SELECT * FROM anime WHERE id = ?", (anime_id,)) + return cursor.fetchone() + except Exception as e: + logging.error(f"Error getting anime by id {anime_id}: {e}") + return None def add_anime(self, data): - cursor = self.db.cursor() - cursor.execute( - "INSERT INTO anime (name, year, season, status, type, comment, url) VALUES (?, ?, ?, ?, ?, ?, ?)", - (data['name'], data['year'], data['season'], data['status'], data['type'], data['comment'], data['url']) - ) - self.db.commit() - - def edit_anime(self, anime_id, data): - cursor = self.db.cursor() - cursor.execute( - "UPDATE anime SET name=?, year=?, season=?, status=?, type=?, comment=?, url=? WHERE id=?", - (data['name'], data['year'], data['season'], data['status'], data['type'], data['comment'], data['url'], anime_id) - ) - self.db.commit() - - def delete_anime(self, anime_id): - cursor = self.db.cursor() - cursor.execute("DELETE FROM anime WHERE id = ?", (anime_id,)) - self.db.commit() - - def change_status(self, anime_id, new_status): - cursor = self.db.cursor() - cursor.execute("UPDATE anime SET status = ? WHERE id = ?", (new_status, anime_id)) - self.db.commit() - - def add_placeholders_for_year(self, year): - cursor = self.db.cursor() - for season in ['winter', 'spring', 'summer', 'fall', '']: + try: + cursor = self.db.cursor() cursor.execute( "INSERT INTO anime (name, year, season, status, type, comment, url) VALUES (?, ?, ?, ?, ?, ?, ?)", - ('Placeholder', year, season, 'unwatched', '', 'Delete or edit me', '') + (data['name'], data['year'], data['season'], data['status'], data['type'], data['comment'], data['url']) ) - self.db.commit() + self.db.commit() + except Exception as e: + logging.error(f"Error adding anime: {e}") + + def edit_anime(self, anime_id, data): + try: + cursor = self.db.cursor() + cursor.execute( + "UPDATE anime SET name=?, year=?, season=?, status=?, type=?, comment=?, url=? WHERE id=?", + (data['name'], data['year'], data['season'], data['status'], data['type'], data['comment'], data['url'], anime_id) + ) + self.db.commit() + except Exception as e: + logging.error(f"Error editing anime id {anime_id}: {e}") + + def delete_anime(self, anime_id): + try: + cursor = self.db.cursor() + cursor.execute("DELETE FROM anime WHERE id = ?", (anime_id,)) + self.db.commit() + except Exception as e: + logging.error(f"Error deleting anime id {anime_id}: {e}") + + def change_status(self, anime_id, new_status): + try: + cursor = self.db.cursor() + cursor.execute("UPDATE anime SET status = ? WHERE id = ?", (new_status, anime_id)) + self.db.commit() + except Exception as e: + logging.error(f"Error changing status for anime id {anime_id}: {e}") + + def add_placeholders_for_year(self, year): + try: + cursor = self.db.cursor() + for season in ['winter', 'spring', 'summer', 'fall', '']: + cursor.execute( + "INSERT INTO anime (name, year, season, status, type, comment, url) VALUES (?, ?, ?, ?, ?, ?, ?)", + ('Placeholder', year, season, 'unwatched', '', 'Delete or edit me', '') + ) + self.db.commit() + except Exception as e: + logging.error(f"Error adding placeholders for year {year}: {e}") def import_from_csv(self, file_name): - with open(file_name, 'r', newline='') as f: - reader = csv.reader(f) - header = next(reader, None) - if header: - cursor = self.db.cursor() - for row in reader: - if len(row) == 7: - name, year_str, season, status, type_, comment, url = row - elif len(row) == 8: - _, name, year_str, season, status, type_, comment, url = row - else: - continue - try: - year = int(year_str) - except ValueError: - continue - cursor.execute( - "SELECT id FROM anime WHERE name = ? AND year = ? AND season = ?", - (name, year, season) - ) - if not cursor.fetchone(): + try: + with open(file_name, 'r', newline='') as f: + reader = csv.reader(f) + header = next(reader, None) + if header: + cursor = self.db.cursor() + for row in reader: + if len(row) == 7: + name, year_str, season, status, type_, comment, url = row + elif len(row) == 8: + _, name, year_str, season, status, type_, comment, url = row + else: + continue + try: + year = int(year_str) + except ValueError: + continue cursor.execute( - "INSERT INTO anime (name, year, season, status, type, comment, url) VALUES (?, ?, ?, ?, ?, ?, ?)", - (name, year, season, status, type_, comment, url) + "SELECT id FROM anime WHERE name = ? AND year = ? AND season = ?", + (name, year, season) ) - self.db.commit() + if not cursor.fetchone(): + cursor.execute( + "INSERT INTO anime (name, year, season, status, type, comment, url) VALUES (?, ?, ?, ?, ?, ?, ?)", + (name, year, season, status, type_, comment, url) + ) + self.db.commit() + except Exception as e: + logging.error(f"Error importing from CSV {file_name}: {e}") def export_to_csv(self, file_name): - cursor = self.db.cursor() - cursor.execute("SELECT * FROM anime") - rows = cursor.fetchall() - with open(file_name, 'w', newline='') as f: - writer = csv.writer(f) - writer.writerow(['id', 'name', 'year', 'season', 'status', 'type', 'comment', 'url']) - writer.writerows(rows) + try: + cursor = self.db.cursor() + cursor.execute("SELECT * FROM anime") + rows = cursor.fetchall() + with open(file_name, 'w', newline='') as f: + writer = csv.writer(f) + writer.writerow(['id', 'name', 'year', 'season', 'status', 'type', 'comment', 'url']) + writer.writerows(rows) + except Exception as e: + logging.error(f"Error exporting to CSV {file_name}: {e}") def delete_year(self, year): - cursor = self.db.cursor() - cursor.execute("DELETE FROM anime WHERE year = ?", (year,)) - self.db.commit() \ No newline at end of file + try: + cursor = self.db.cursor() + cursor.execute("DELETE FROM anime WHERE year = ?", (year,)) + self.db.commit() + except Exception as e: + logging.error(f"Error deleting year {year}: {e}") \ No newline at end of file diff --git a/frontend.py b/frontend.py index 8b8cb8c..c6a1733 100644 --- a/frontend.py +++ b/frontend.py @@ -4,6 +4,7 @@ import random import re import hashlib import html +import logging from datetime import datetime from PyQt5.QtWidgets import ( QMainWindow, QTabWidget, QScrollArea, QWidget, QVBoxLayout, QTableWidget, QTableWidgetItem, @@ -16,6 +17,10 @@ from PyQt5.QtGui import QColor, QIcon, QKeySequence from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply from backend import AnimeBackend +# Set up logging +logging.basicConfig(filename='anime_tracker.log', level=logging.ERROR, + format='%(asctime)s - %(levelname)s - %(message)s') + class AnimeDialog(QDialog): def __init__(self, parent, entry=None, default_year=None, default_season=None): super().__init__(parent) @@ -161,7 +166,10 @@ class AnimeTracker(QMainWindow): self.resize(800, 600) self.backend = AnimeBackend() self.image_cache_dir = 'images' - os.makedirs(self.image_cache_dir, exist_ok=True) + try: + os.makedirs(self.image_cache_dir, exist_ok=True) + except Exception as e: + logging.error(f"Error creating image cache directory: {e}") self.network_manager = QNetworkAccessManager(self) self.tab_widget = QTabWidget() self.setCentralWidget(self.tab_widget) @@ -461,29 +469,41 @@ class AnimeTracker(QMainWindow): if os.path.exists(image_file): label.setToolTip(f'') return - request = QNetworkRequest(QUrl(url)) - reply = self.network_manager.get(request) - reply.finished.connect(lambda: self.handle_html_reply(reply, image_file, label)) + try: + request = QNetworkRequest(QUrl(url)) + reply = self.network_manager.get(request) + reply.finished.connect(lambda: self.handle_html_reply(reply, image_file, label)) + except Exception as e: + logging.error(f"Error fetching poster from {url}: {e}") + label.setToolTip('Failed to load poster') def handle_html_reply(self, reply, image_file, label): if reply.error() != QNetworkReply.NoError: label.setToolTip('Failed to load poster') return - html = reply.readAll().data().decode('utf-8', errors='ignore') - match = re.search(r'') + try: + with open(image_file, 'wb') as f: + f.write(reply.readAll().data()) + label.setToolTip(f'') + except Exception as e: + logging.error(f"Error saving image to {image_file}: {e}") + label.setToolTip('Failed to load poster') else: label.setToolTip('Failed to load poster')