fixed csv importing and core dump issue

This commit is contained in:
Bernd 2025-07-18 21:57:38 +05:00
parent fbbb7cb8e8
commit 2f2b8a4f29
15 changed files with 70 additions and 38 deletions

Binary file not shown.

View File

@ -84,11 +84,14 @@ class AnimeBackend:
if header: if header:
cursor = self.db.cursor() cursor = self.db.cursor()
for row in reader: for row in reader:
if len(row) < 8: 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 continue
_, name, year, season, status, type_, comment, url = row
try: try:
year = int(year) year = int(year_str)
except ValueError: except ValueError:
continue continue
cursor.execute( cursor.execute(

View File

@ -3,6 +3,7 @@ import os
import random import random
import re import re
import hashlib import hashlib
import html
from datetime import datetime from datetime import datetime
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QMainWindow, QTabWidget, QWidget, QVBoxLayout, QTableWidget, QTableWidgetItem, QMainWindow, QTabWidget, QWidget, QVBoxLayout, QTableWidget, QTableWidgetItem,
@ -10,7 +11,7 @@ from PyQt5.QtWidgets import (
QComboBox, QTextEdit, QDialogButtonBox, QAction, QFileDialog, QMessageBox, QComboBox, QTextEdit, QDialogButtonBox, QAction, QFileDialog, QMessageBox,
QInputDialog, QApplication, QAbstractItemView QInputDialog, QApplication, QAbstractItemView
) )
from PyQt5.QtCore import Qt, QUrl from PyQt5.QtCore import Qt, QUrl, QEvent
from PyQt5.QtGui import QColor, QIcon from PyQt5.QtGui import QColor, QIcon
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
from backend import AnimeBackend from backend import AnimeBackend
@ -76,6 +77,30 @@ class AnimeDialog(QDialog):
'url': self.url_edit.text() 'url': self.url_edit.text()
} }
class HoverLabel(QLabel):
def __init__(self, main_window, name, url=None):
super().__init__()
self.main_window = main_window
self.url = url
self.fetched = False
self.image_file = None
if url:
self.image_file = os.path.join(main_window.image_cache_dir, hashlib.md5(url.encode()).hexdigest() + '.jpg')
name_escaped = html.escape(name)
self.setText(f'<a href="{url}">{name_escaped}</a>')
self.setOpenExternalLinks(True)
if os.path.exists(self.image_file):
self.setToolTip(f'<img src="{self.image_file}" width="200">')
self.fetched = True
else:
self.setText(html.escape(name))
def enterEvent(self, event):
if self.url and not self.fetched:
self.main_window.fetch_poster(self.url, self)
self.fetched = True
super().enterEvent(event)
class AnimeTracker(QMainWindow): class AnimeTracker(QMainWindow):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
@ -113,9 +138,13 @@ class AnimeTracker(QMainWindow):
def load_tabs(self): def load_tabs(self):
self.tab_widget.clear() self.tab_widget.clear()
# Pre-2010 tab
pre_entries = self.backend.get_pre_2010_entries() pre_entries = self.backend.get_pre_2010_entries()
pre_tab = QWidget() pre_tab = QWidget()
self.setup_table(pre_tab, pre_entries, is_pre=True) layout = QVBoxLayout(pre_tab)
if pre_entries:
table = self.create_table_widget(pre_entries, is_pre=True)
layout.addWidget(table)
tab_text = "Pre-2010" tab_text = "Pre-2010"
completed = False completed = False
total = len(pre_entries) total = len(pre_entries)
@ -128,25 +157,32 @@ class AnimeTracker(QMainWindow):
index = self.tab_widget.addTab(pre_tab, tab_text) index = self.tab_widget.addTab(pre_tab, tab_text)
if completed: if completed:
self.tab_widget.tabBar().setTabTextColor(index, QColor('gray')) self.tab_widget.tabBar().setTabTextColor(index, QColor('gray'))
# Years >= 2010
years = self.backend.get_years() years = self.backend.get_years()
for year in years: for year in years:
year_tab = QWidget()
layout = QVBoxLayout(year_tab)
total_entries = 0
comp_entries = 0
for season in ['winter', 'spring', 'summer', 'fall', '']: for season in ['winter', 'spring', 'summer', 'fall', '']:
entries = self.backend.get_entries_for_season(year, season) entries = self.backend.get_entries_for_season(year, season)
if entries: if entries:
tab = QWidget()
self.setup_table(tab, entries, is_pre=False, year=year, season=season)
s_name = season.capitalize() if season else 'Other' s_name = season.capitalize() if season else 'Other'
total = len(entries) label = QLabel(s_name)
comp = sum(1 for e in entries if e[4] == 'completed') layout.addWidget(label)
perc = (comp / total * 100) table = self.create_table_widget(entries, is_pre=False)
tab_text = f"{year} - {s_name} ({perc:.0f}%)" layout.addWidget(table)
completed = (comp == total) total_entries += len(entries)
index = self.tab_widget.addTab(tab, tab_text) comp_entries += sum(1 for e in entries if e[4] == 'completed')
if completed: if total_entries > 0:
self.tab_widget.tabBar().setTabTextColor(index, QColor('gray')) perc = (comp_entries / total_entries * 100) if total_entries > 0 else 0
tab_text = f"{year} ({perc:.0f}%)"
completed = (comp_entries == total_entries)
index = self.tab_widget.addTab(year_tab, tab_text)
if completed:
self.tab_widget.tabBar().setTabTextColor(index, QColor('gray'))
def setup_table(self, tab, entries, is_pre, year=None, season=None): def create_table_widget(self, entries, is_pre):
layout = QVBoxLayout(tab)
col_count = 6 if is_pre else 5 col_count = 6 if is_pre else 5
table = QTableWidget(len(entries), col_count) table = QTableWidget(len(entries), col_count)
headers = ['Year', 'Name', 'Type', 'Status', 'Comment', 'Actions'] if is_pre else ['Name', 'Type', 'Status', 'Comment', 'Actions'] headers = ['Year', 'Name', 'Type', 'Status', 'Comment', 'Actions'] if is_pre else ['Name', 'Type', 'Status', 'Comment', 'Actions']
@ -164,13 +200,7 @@ class AnimeTracker(QMainWindow):
# Name # Name
name = entry[1] name = entry[1]
url = entry[7] url = entry[7]
name_label = QLabel() name_label = HoverLabel(self, name, url)
if url:
name_label.setText(f'<a href="{url}">{name}</a>')
name_label.setOpenExternalLinks(True)
self.fetch_poster(url, name_label)
else:
name_label.setText(name)
table.setCellWidget(row, col, name_label) table.setCellWidget(row, col, name_label)
col += 1 col += 1
# Type # Type
@ -208,7 +238,7 @@ class AnimeTracker(QMainWindow):
widget = table.cellWidget(r, c) widget = table.cellWidget(r, c)
if widget: if widget:
widget.setStyleSheet(f"background-color: {color.name()};") widget.setStyleSheet(f"background-color: {color.name()};")
layout.addWidget(table) return table
def create_actions_widget(self, anime_id, status): def create_actions_widget(self, anime_id, status):
widget = QWidget() widget = QWidget()
@ -239,7 +269,7 @@ class AnimeTracker(QMainWindow):
return widget return widget
def fetch_poster(self, url, label): def fetch_poster(self, url, label):
image_file = os.path.join(self.image_cache_dir, hashlib.md5(url.encode()).hexdigest() + '.jpg') image_file = label.image_file
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
@ -280,10 +310,9 @@ class AnimeTracker(QMainWindow):
default_year = 2009 default_year = 2009
default_season = '' default_season = ''
else: else:
parts = tab_text.split(' - ') parts = tab_text.split(' (')
default_year = int(parts[0]) default_year = int(parts[0])
s_name = parts[1].split(' (')[0].lower() default_season = ''
default_season = '' if s_name == 'other' else s_name
dialog = AnimeDialog(self, None, default_year, default_season) dialog = AnimeDialog(self, None, default_year, default_season)
if dialog.exec_() == QDialog.Accepted: if dialog.exec_() == QDialog.Accepted:
data = dialog.get_data() data = dialog.get_data()
@ -321,18 +350,18 @@ class AnimeTracker(QMainWindow):
return return
tab_text = self.tab_widget.tabText(self.tab_widget.currentIndex()) tab_text = self.tab_widget.tabText(self.tab_widget.currentIndex())
is_pre = 'Pre-2010' in tab_text is_pre = 'Pre-2010' in tab_text
table = self.tab_widget.currentWidget().findChild(QTableWidget) current_widget = self.tab_widget.currentWidget()
if not table: tables = current_widget.findChildren(QTableWidget)
return
name_col = 1 if is_pre else 0 name_col = 1 if is_pre else 0
status_col = 3 if is_pre else 2 status_col = 3 if is_pre else 2
unwatched = [] unwatched = []
for row in range(table.rowCount()): for table in tables:
status = table.item(row, status_col).text() for row in range(table.rowCount()):
if status == 'unwatched': status = table.item(row, status_col).text()
name_text = table.cellWidget(row, name_col).text() if status == 'unwatched':
clean_name = re.sub(r'<[^>]+>', '', name_text) name_text = table.cellWidget(row, name_col).text()
unwatched.append(clean_name) clean_name = re.sub(r'<[^>]+>', '', name_text)
unwatched.append(clean_name)
if unwatched: if unwatched:
random_name = random.choice(unwatched) random_name = random.choice(unwatched)
QMessageBox.information(self, "Random Pick", f"Watch: {random_name}") QMessageBox.information(self, "Random Pick", f"Watch: {random_name}")

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB