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')