4 Commits

Author SHA1 Message Date
Bernd
c0a37c4b45 updating readme 2025-08-05 16:51:58 +05:00
Bernd
86e7b72323 fixed season default value when editing anime 2025-08-05 16:36:43 +05:00
Bernd
8900b7b11d preserving sroll position fix 2025-08-05 16:22:23 +05:00
Bernd
8be501dd46 fixing season combo box logic 2025-07-31 20:16:10 +05:00
2 changed files with 65 additions and 12 deletions

View File

@@ -28,10 +28,20 @@ class AnimeDialog(QDialog):
self.name_edit.setMaxLength(255) # Prevent overly long inputs self.name_edit.setMaxLength(255) # Prevent overly long inputs
layout.addRow("Name", self.name_edit) layout.addRow("Name", self.name_edit)
self.year_spin = QSpinBox() self.year_spin = QSpinBox()
self.year_spin.setRange(1900, 2100) self.year_spin.setRange(1960, 2073)
layout.addRow("Year", self.year_spin) layout.addRow("Year", self.year_spin)
self.season_combo = QComboBox() self.season_combo = QComboBox()
self.season_combo.addItems(['', 'winter', 'spring', 'summer', 'fall']) self.season_combo.addItems(['Winter', 'Spring', 'Summer', 'Fall', 'Other'])
# Map display names to database values
self.season_map = {
'Winter': 'winter',
'Spring': 'spring',
'Summer': 'summer',
'Fall': 'fall',
'Other': ''
}
# Reverse map for setting current selection
self.reverse_season_map = {v: k for k, v in self.season_map.items()}
layout.addRow("Season", self.season_combo) layout.addRow("Season", self.season_combo)
self.type_combo = QComboBox() self.type_combo = QComboBox()
self.type_combo.addItems(['TV', 'Movie', 'OVA', 'Special', 'Short TV', 'Other']) self.type_combo.addItems(['TV', 'Movie', 'OVA', 'Special', 'Short TV', 'Other'])
@@ -53,7 +63,8 @@ class AnimeDialog(QDialog):
# Unescape for display in input fields # Unescape for display in input fields
self.name_edit.setText(html.unescape(entry[1])) self.name_edit.setText(html.unescape(entry[1]))
self.year_spin.setValue(entry[2]) self.year_spin.setValue(entry[2])
self.season_combo.setCurrentText(entry[3]) self.season_combo.setCurrentText((entry[3] or "Other").capitalize())
print("entry[3] = " + entry[3])
self.status_combo.setCurrentText(entry[4]) self.status_combo.setCurrentText(entry[4])
self.type_combo.setCurrentText(entry[5] or '') self.type_combo.setCurrentText(entry[5] or '')
self.comment_edit.setPlainText(html.unescape(entry[6] or '')) self.comment_edit.setPlainText(html.unescape(entry[6] or ''))
@@ -62,23 +73,28 @@ class AnimeDialog(QDialog):
if default_year is not None: if default_year is not None:
self.year_spin.setValue(default_year) self.year_spin.setValue(default_year)
if default_season is not None: if default_season is not None:
self.season_combo.setCurrentText(default_season) # Map database season value to display name for default
display_season = self.reverse_season_map.get(default_season, 'Other')
self.year_spin.valueChanged.connect(self.update_season) self.year_spin.valueChanged.connect(self.update_season)
self.update_season(self.year_spin.value()) self.update_season(self.year_spin.value())
def update_season(self, year): def update_season(self, year):
if year < 2010: if year < 2010:
self.season_combo.setEnabled(False) self.season_combo.setEnabled(False)
self.season_combo.setCurrentText('') self.season_combo.setCurrentText('Other')
else: else:
self.season_combo.setEnabled(True) self.season_combo.setEnabled(True)
def get_data(self): def get_data(self):
# Convert display season name back to database value
display_season = self.season_combo.currentText().strip()
db_season = self.season_map.get(display_season, '')
# Sanitize inputs by escaping special characters # Sanitize inputs by escaping special characters
return { return {
'name': html.escape(self.name_edit.text().strip()), 'name': html.escape(self.name_edit.text().strip()),
'year': self.year_spin.value(), 'year': self.year_spin.value(),
'season': html.escape(self.season_combo.currentText().strip()), 'season': html.escape(db_season), # Use mapped database value
'status': html.escape(self.status_combo.currentText().strip()), 'status': html.escape(self.status_combo.currentText().strip()),
'type': html.escape(self.type_combo.currentText().strip()), 'type': html.escape(self.type_combo.currentText().strip()),
'comment': html.escape(self.comment_edit.toPlainText().strip()), 'comment': html.escape(self.comment_edit.toPlainText().strip()),
@@ -183,6 +199,17 @@ class AnimeTracker(QMainWindow):
self.set_current_tab_by_identifier(last_tab) self.set_current_tab_by_identifier(last_tab)
self.tab_widget.setFocus() self.tab_widget.setFocus()
def get_current_scroll_pos(self):
widget = self.tab_widget.currentWidget()
if widget:
return widget.verticalScrollBar().value()
return 0
def set_current_scroll_pos(self, pos):
widget = self.tab_widget.currentWidget()
if widget:
widget.verticalScrollBar().setValue(pos)
def filter_tables(self, text): def filter_tables(self, text):
self.search_text = text.strip().lower() self.search_text = text.strip().lower()
for table in self.tables: for table in self.tables:
@@ -628,9 +655,10 @@ class AnimeTracker(QMainWindow):
parts = tab_text.split(' (') parts = tab_text.split(' (')
default_year = int(parts[0]) default_year = int(parts[0])
default_season = '' default_season = ''
current_id = self.get_current_tab_identifier()
current_scroll = self.get_current_scroll_pos()
dialog = AnimeDialog(self, None, default_year, self.last_used_season) dialog = AnimeDialog(self, None, default_year, self.last_used_season)
if dialog.exec_() == QDialog.Accepted: if dialog.exec_() == QDialog.Accepted:
current_id = self.get_current_tab_identifier()
data = dialog.get_data() data = dialog.get_data()
if data['season']: if data['season']:
self.last_used_season = data['season'] self.last_used_season = data['season']
@@ -639,36 +667,50 @@ class AnimeTracker(QMainWindow):
QMessageBox.warning(self, "Error", "Anime name cannot be empty.") QMessageBox.warning(self, "Error", "Anime name cannot be empty.")
return return
self.backend.add_anime(data) self.backend.add_anime(data)
new_year = data['year']
new_id = "pre" if new_year < 2010 else new_year
self.load_tabs() self.load_tabs()
self.set_current_tab_by_identifier(current_id) self.set_current_tab_by_identifier(new_id)
if self.get_current_tab_identifier() == current_id:
self.set_current_scroll_pos(current_scroll)
def edit_anime(self, anime_id): def edit_anime(self, anime_id):
entry = self.backend.get_anime_by_id(anime_id) entry = self.backend.get_anime_by_id(anime_id)
if entry: if entry:
current_id = self.get_current_tab_identifier() current_id = self.get_current_tab_identifier()
current_scroll = self.get_current_scroll_pos()
dialog = AnimeDialog(self, entry) dialog = AnimeDialog(self, entry)
if dialog.exec_() == QDialog.Accepted: if dialog.exec_() == QDialog.Accepted:
current_id = self.get_current_tab_identifier()
data = dialog.get_data() data = dialog.get_data()
if not data['name']: if not data['name']:
QMessageBox.warning(self, "Error", "Anime name cannot be empty.") QMessageBox.warning(self, "Error", "Anime name cannot be empty.")
return return
self.backend.edit_anime(anime_id, data) self.backend.edit_anime(anime_id, data)
new_year = data['year']
new_id = "pre" if new_year < 2010 else new_year
self.load_tabs() self.load_tabs()
self.set_current_tab_by_identifier(current_id) self.set_current_tab_by_identifier(new_id)
if self.get_current_tab_identifier() == current_id:
self.set_current_scroll_pos(current_scroll)
def delete_anime(self, anime_id): def delete_anime(self, anime_id):
if QMessageBox.question(self, "Confirm Delete", "Are you sure you want to delete this entry?") == QMessageBox.Yes: if QMessageBox.question(self, "Confirm Delete", "Are you sure you want to delete this entry?") == QMessageBox.Yes:
current_id = self.get_current_tab_identifier() current_id = self.get_current_tab_identifier()
current_scroll = self.get_current_scroll_pos()
self.backend.delete_anime(anime_id) self.backend.delete_anime(anime_id)
self.load_tabs() self.load_tabs()
self.set_current_tab_by_identifier(current_id) self.set_current_tab_by_identifier(current_id)
if self.get_current_tab_identifier() == current_id:
self.set_current_scroll_pos(current_scroll)
def change_status(self, anime_id, new_status): def change_status(self, anime_id, new_status):
current_id = self.get_current_tab_identifier() current_id = self.get_current_tab_identifier()
current_scroll = self.get_current_scroll_pos()
self.backend.change_status(anime_id, new_status) self.backend.change_status(anime_id, new_status)
self.load_tabs() self.load_tabs()
self.set_current_tab_by_identifier(current_id) self.set_current_tab_by_identifier(current_id)
if self.get_current_tab_identifier() == current_id:
self.set_current_scroll_pos(current_scroll)
def add_new_year(self): def add_new_year(self):
current_year = datetime.now().year current_year = datetime.now().year
@@ -689,8 +731,13 @@ class AnimeTracker(QMainWindow):
QMessageBox.warning(self, "Error", f"Year {year} does not exist.") QMessageBox.warning(self, "Error", f"Year {year} does not exist.")
return return
if QMessageBox.question(self, "Confirm Delete", f"Are you sure you want to delete all entries for {year}?") == QMessageBox.Yes: if QMessageBox.question(self, "Confirm Delete", f"Are you sure you want to delete all entries for {year}?") == QMessageBox.Yes:
current_id = self.get_current_tab_identifier()
current_scroll = self.get_current_scroll_pos()
self.backend.delete_year(year) self.backend.delete_year(year)
self.load_tabs() self.load_tabs()
self.set_current_tab_by_identifier(current_id)
if self.get_current_tab_identifier() == current_id:
self.set_current_scroll_pos(current_scroll)
def random_pick(self): def random_pick(self):
if self.tab_widget.currentIndex() == -1: if self.tab_widget.currentIndex() == -1:
@@ -755,9 +802,12 @@ class AnimeTracker(QMainWindow):
file_name, _ = QFileDialog.getOpenFileName(self, "Import CSV", "", "CSV Files (*.csv)") file_name, _ = QFileDialog.getOpenFileName(self, "Import CSV", "", "CSV Files (*.csv)")
if file_name: if file_name:
current_id = self.get_current_tab_identifier() current_id = self.get_current_tab_identifier()
current_scroll = self.get_current_scroll_pos()
self.backend.import_from_csv(file_name) self.backend.import_from_csv(file_name)
self.load_tabs() self.load_tabs()
self.set_current_tab_by_identifier(current_id) self.set_current_tab_by_identifier(current_id)
if self.get_current_tab_identifier() == current_id:
self.set_current_scroll_pos(current_scroll)
def export_csv(self): def export_csv(self):
file_name, _ = QFileDialog.getSaveFileName(self, "Export CSV", "anime_backlog.csv", "CSV Files (*.csv)") file_name, _ = QFileDialog.getSaveFileName(self, "Export CSV", "anime_backlog.csv", "CSV Files (*.csv)")

View File

@@ -10,5 +10,8 @@
Then run a binary: Then run a binary:
`./dist/AnimeTracker/AnimeTracker` `./dist/AnimeTracker/AnimeTracker`
## How to update the binary file
cp ~/Documents/programs/python/anime-tracker/dist/AnimeTracker/AnimeTracker ~/Applications/AnimeTracker/AnimeTracker
## How to run this app without building: ## How to run this app without building:
`python frontend.py` `python frontend.py`