integrated Python's logging module (wrap I/O operations with try/except, log exceptions to file)
This commit is contained in:
parent
282556790c
commit
f65a42cdce
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
anime_backlog.db
|
anime_backlog.db
|
||||||
images/
|
images/
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
anime_tracker.log
|
||||||
|
48
backend.py
48
backend.py
@ -1,5 +1,10 @@
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
import csv
|
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:
|
class AnimeBackend:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -7,6 +12,7 @@ class AnimeBackend:
|
|||||||
self.create_table()
|
self.create_table()
|
||||||
|
|
||||||
def create_table(self):
|
def create_table(self):
|
||||||
|
try:
|
||||||
cursor = self.db.cursor()
|
cursor = self.db.cursor()
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
CREATE TABLE IF NOT EXISTS anime (
|
CREATE TABLE IF NOT EXISTS anime (
|
||||||
@ -21,54 +27,85 @@ class AnimeBackend:
|
|||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error creating table: {e}")
|
||||||
|
|
||||||
def get_pre_2010_entries(self):
|
def get_pre_2010_entries(self):
|
||||||
|
try:
|
||||||
cursor = self.db.cursor()
|
cursor = self.db.cursor()
|
||||||
cursor.execute("SELECT * FROM anime WHERE year < 2010 ORDER BY year DESC, name")
|
cursor.execute("SELECT * FROM anime WHERE year < 2010 ORDER BY year DESC, name")
|
||||||
return cursor.fetchall()
|
return cursor.fetchall()
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error getting pre-2010 entries: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
def get_years(self):
|
def get_years(self):
|
||||||
|
try:
|
||||||
cursor = self.db.cursor()
|
cursor = self.db.cursor()
|
||||||
cursor.execute("SELECT DISTINCT year FROM anime WHERE year >= 2010 ORDER BY year DESC")
|
cursor.execute("SELECT DISTINCT year FROM anime WHERE year >= 2010 ORDER BY year DESC")
|
||||||
return [row[0] for row in cursor.fetchall()]
|
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):
|
def get_entries_for_season(self, year, season):
|
||||||
|
try:
|
||||||
cursor = self.db.cursor()
|
cursor = self.db.cursor()
|
||||||
cursor.execute("SELECT * FROM anime WHERE year = ? AND season = ? ORDER BY name", (year, season))
|
cursor.execute("SELECT * FROM anime WHERE year = ? AND season = ? ORDER BY name", (year, season))
|
||||||
return cursor.fetchall()
|
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):
|
def get_anime_by_id(self, anime_id):
|
||||||
|
try:
|
||||||
cursor = self.db.cursor()
|
cursor = self.db.cursor()
|
||||||
cursor.execute("SELECT * FROM anime WHERE id = ?", (anime_id,))
|
cursor.execute("SELECT * FROM anime WHERE id = ?", (anime_id,))
|
||||||
return cursor.fetchone()
|
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):
|
def add_anime(self, data):
|
||||||
|
try:
|
||||||
cursor = self.db.cursor()
|
cursor = self.db.cursor()
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"INSERT INTO anime (name, year, season, status, type, comment, url) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
"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'])
|
(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):
|
def edit_anime(self, anime_id, data):
|
||||||
|
try:
|
||||||
cursor = self.db.cursor()
|
cursor = self.db.cursor()
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"UPDATE anime SET name=?, year=?, season=?, status=?, type=?, comment=?, url=? WHERE id=?",
|
"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)
|
(data['name'], data['year'], data['season'], data['status'], data['type'], data['comment'], data['url'], anime_id)
|
||||||
)
|
)
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error editing anime id {anime_id}: {e}")
|
||||||
|
|
||||||
def delete_anime(self, anime_id):
|
def delete_anime(self, anime_id):
|
||||||
|
try:
|
||||||
cursor = self.db.cursor()
|
cursor = self.db.cursor()
|
||||||
cursor.execute("DELETE FROM anime WHERE id = ?", (anime_id,))
|
cursor.execute("DELETE FROM anime WHERE id = ?", (anime_id,))
|
||||||
self.db.commit()
|
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):
|
def change_status(self, anime_id, new_status):
|
||||||
|
try:
|
||||||
cursor = self.db.cursor()
|
cursor = self.db.cursor()
|
||||||
cursor.execute("UPDATE anime SET status = ? WHERE id = ?", (new_status, anime_id))
|
cursor.execute("UPDATE anime SET status = ? WHERE id = ?", (new_status, anime_id))
|
||||||
self.db.commit()
|
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):
|
def add_placeholders_for_year(self, year):
|
||||||
|
try:
|
||||||
cursor = self.db.cursor()
|
cursor = self.db.cursor()
|
||||||
for season in ['winter', 'spring', 'summer', 'fall', '']:
|
for season in ['winter', 'spring', 'summer', 'fall', '']:
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
@ -76,8 +113,11 @@ class AnimeBackend:
|
|||||||
('Placeholder', year, season, 'unwatched', '', 'Delete or edit me', '')
|
('Placeholder', year, season, 'unwatched', '', 'Delete or edit me', '')
|
||||||
)
|
)
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error adding placeholders for year {year}: {e}")
|
||||||
|
|
||||||
def import_from_csv(self, file_name):
|
def import_from_csv(self, file_name):
|
||||||
|
try:
|
||||||
with open(file_name, 'r', newline='') as f:
|
with open(file_name, 'r', newline='') as f:
|
||||||
reader = csv.reader(f)
|
reader = csv.reader(f)
|
||||||
header = next(reader, None)
|
header = next(reader, None)
|
||||||
@ -104,8 +144,11 @@ class AnimeBackend:
|
|||||||
(name, year, season, status, type_, comment, url)
|
(name, year, season, status, type_, comment, url)
|
||||||
)
|
)
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error importing from CSV {file_name}: {e}")
|
||||||
|
|
||||||
def export_to_csv(self, file_name):
|
def export_to_csv(self, file_name):
|
||||||
|
try:
|
||||||
cursor = self.db.cursor()
|
cursor = self.db.cursor()
|
||||||
cursor.execute("SELECT * FROM anime")
|
cursor.execute("SELECT * FROM anime")
|
||||||
rows = cursor.fetchall()
|
rows = cursor.fetchall()
|
||||||
@ -113,8 +156,13 @@ class AnimeBackend:
|
|||||||
writer = csv.writer(f)
|
writer = csv.writer(f)
|
||||||
writer.writerow(['id', 'name', 'year', 'season', 'status', 'type', 'comment', 'url'])
|
writer.writerow(['id', 'name', 'year', 'season', 'status', 'type', 'comment', 'url'])
|
||||||
writer.writerows(rows)
|
writer.writerows(rows)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error exporting to CSV {file_name}: {e}")
|
||||||
|
|
||||||
def delete_year(self, year):
|
def delete_year(self, year):
|
||||||
|
try:
|
||||||
cursor = self.db.cursor()
|
cursor = self.db.cursor()
|
||||||
cursor.execute("DELETE FROM anime WHERE year = ?", (year,))
|
cursor.execute("DELETE FROM anime WHERE year = ?", (year,))
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error deleting year {year}: {e}")
|
20
frontend.py
20
frontend.py
@ -4,6 +4,7 @@ import random
|
|||||||
import re
|
import re
|
||||||
import hashlib
|
import hashlib
|
||||||
import html
|
import html
|
||||||
|
import logging
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QMainWindow, QTabWidget, QScrollArea, QWidget, QVBoxLayout, QTableWidget, QTableWidgetItem,
|
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 PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
|
||||||
from backend import AnimeBackend
|
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):
|
class AnimeDialog(QDialog):
|
||||||
def __init__(self, parent, entry=None, default_year=None, default_season=None):
|
def __init__(self, parent, entry=None, default_year=None, default_season=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
@ -161,7 +166,10 @@ class AnimeTracker(QMainWindow):
|
|||||||
self.resize(800, 600)
|
self.resize(800, 600)
|
||||||
self.backend = AnimeBackend()
|
self.backend = AnimeBackend()
|
||||||
self.image_cache_dir = 'images'
|
self.image_cache_dir = 'images'
|
||||||
|
try:
|
||||||
os.makedirs(self.image_cache_dir, exist_ok=True)
|
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.network_manager = QNetworkAccessManager(self)
|
||||||
self.tab_widget = QTabWidget()
|
self.tab_widget = QTabWidget()
|
||||||
self.setCentralWidget(self.tab_widget)
|
self.setCentralWidget(self.tab_widget)
|
||||||
@ -461,14 +469,19 @@ class AnimeTracker(QMainWindow):
|
|||||||
if os.path.exists(image_file):
|
if os.path.exists(image_file):
|
||||||
label.setToolTip(f'<img src="{image_file}" width="200">')
|
label.setToolTip(f'<img src="{image_file}" width="200">')
|
||||||
return
|
return
|
||||||
|
try:
|
||||||
request = QNetworkRequest(QUrl(url))
|
request = QNetworkRequest(QUrl(url))
|
||||||
reply = self.network_manager.get(request)
|
reply = self.network_manager.get(request)
|
||||||
reply.finished.connect(lambda: self.handle_html_reply(reply, image_file, label))
|
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):
|
def handle_html_reply(self, reply, image_file, label):
|
||||||
if reply.error() != QNetworkReply.NoError:
|
if reply.error() != QNetworkReply.NoError:
|
||||||
label.setToolTip('Failed to load poster')
|
label.setToolTip('Failed to load poster')
|
||||||
return
|
return
|
||||||
|
try:
|
||||||
html = reply.readAll().data().decode('utf-8', errors='ignore')
|
html = reply.readAll().data().decode('utf-8', errors='ignore')
|
||||||
match = re.search(r'<meta property="og:image" content="([^"]+)"', html)
|
match = re.search(r'<meta property="og:image" content="([^"]+)"', html)
|
||||||
if match:
|
if match:
|
||||||
@ -478,12 +491,19 @@ class AnimeTracker(QMainWindow):
|
|||||||
img_reply.finished.connect(lambda: self.handle_image_reply(img_reply, image_file, label))
|
img_reply.finished.connect(lambda: self.handle_image_reply(img_reply, image_file, label))
|
||||||
else:
|
else:
|
||||||
label.setToolTip('No poster found')
|
label.setToolTip('No poster found')
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error handling HTML reply: {e}")
|
||||||
|
label.setToolTip('Failed to load poster')
|
||||||
|
|
||||||
def handle_image_reply(self, reply, image_file, label):
|
def handle_image_reply(self, reply, image_file, label):
|
||||||
if reply.error() == QNetworkReply.NoError:
|
if reply.error() == QNetworkReply.NoError:
|
||||||
|
try:
|
||||||
with open(image_file, 'wb') as f:
|
with open(image_file, 'wb') as f:
|
||||||
f.write(reply.readAll().data())
|
f.write(reply.readAll().data())
|
||||||
label.setToolTip(f'<img src="{image_file}" width="200">')
|
label.setToolTip(f'<img src="{image_file}" width="200">')
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error saving image to {image_file}: {e}")
|
||||||
|
label.setToolTip('Failed to load poster')
|
||||||
else:
|
else:
|
||||||
label.setToolTip('Failed to load poster')
|
label.setToolTip('Failed to load poster')
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user