Refactor MyTimeline.py: improve code quality and reduce duplication
- Extract calendar widget creation into helper method (_create_date_calendar_widget) Eliminates ~80 lines of duplicated code between from/to date widgets - Extract place access logic into _get_place_name_for_event() helper Provides single source of truth for place access with error handling - Consolidate year spin button updates via _update_year_spin_button() helper Reduces duplication and ensures consistent behavior - Split _update_filter_dialog_state() into focused methods: - _update_event_type_widgets() - _update_person_filter_widgets() - _update_date_range_widgets() - Extract filter check logic into separate methods: - _apply_event_type_filter() - _apply_category_filter() - Improve type hints: change handler return types from Any to Callable[[Gtk.Widget], None] - Extract UI layout constants (margins, spacing) for better maintainability - Add event type normalization caching for performance Cache dictionary avoids repeated conversions - Optimize event type set operations with pre-computed normalized active types Improves filter performance significantly
This commit is contained in:
parent
5860b3d25c
commit
c76735f2b8
408
MyTimeline.py
408
MyTimeline.py
@ -32,7 +32,7 @@ import colorsys
|
||||
import logging
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional, List, Tuple, Any, Set, Dict, TYPE_CHECKING, Union
|
||||
from typing import Optional, List, Tuple, Any, Set, Dict, TYPE_CHECKING, Union, Callable
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from gramps.gen.lib import Event, Person, Family
|
||||
@ -101,6 +101,14 @@ MIN_GENEALOGICAL_YEAR = 1000 # Minimum year for genealogical data
|
||||
DATE_SORT_YEAR_MULTIPLIER = 10000 # Year component in date sort value
|
||||
DATE_SORT_MONTH_MULTIPLIER = 100 # Month component in date sort value
|
||||
|
||||
# UI Layout Constants
|
||||
FILTER_PAGE_MARGIN = 10 # Margin for filter page containers
|
||||
FILTER_PAGE_SPACING = 10 # Spacing in filter page containers
|
||||
CALENDAR_CONTAINER_MARGIN = 5 # Margin for calendar containers
|
||||
CALENDAR_CONTAINER_SPACING = 5 # Spacing in calendar containers
|
||||
CALENDAR_HORIZONTAL_SPACING = 15 # Spacing between calendar frames
|
||||
YEAR_SELECTOR_SPACING = 5 # Spacing in year selector boxes
|
||||
|
||||
# Font Constants
|
||||
FONT_FAMILY = "Sans"
|
||||
FONT_SIZE_NORMAL = 11
|
||||
@ -416,6 +424,8 @@ class MyTimelineView(NavigationView):
|
||||
self._cached_min_date: Optional[int] = None
|
||||
self._cached_max_date: Optional[int] = None
|
||||
self._event_to_person_cache: Dict[str, Optional['Person']] = {} # Event handle -> Person object (or None)
|
||||
self._event_type_normalization_cache: Dict[EventType, int] = {} # Cache for event type normalization
|
||||
self._normalized_active_event_types: Optional[Set[int]] = None # Pre-computed normalized active types
|
||||
|
||||
# Filter state
|
||||
self.filter_enabled: bool = False
|
||||
@ -676,7 +686,7 @@ class MyTimelineView(NavigationView):
|
||||
group_checkbox.set_inconsistent(True)
|
||||
|
||||
def _make_group_toggle_handler(self, child_checkboxes: List[Gtk.CheckButton],
|
||||
updating_flag: List[bool]) -> Any:
|
||||
updating_flag: List[bool]) -> Callable[[Gtk.Widget], None]:
|
||||
"""
|
||||
Create a handler for group checkbox toggle that toggles all children.
|
||||
|
||||
@ -707,7 +717,7 @@ class MyTimelineView(NavigationView):
|
||||
|
||||
def _make_child_toggle_handler(self, group_checkbox: Gtk.CheckButton,
|
||||
child_checkboxes: List[Gtk.CheckButton],
|
||||
updating_flag: List[bool]) -> Any:
|
||||
updating_flag: List[bool]) -> Callable[[Gtk.Widget], None]:
|
||||
"""
|
||||
Create a handler for child checkbox toggle that updates group state.
|
||||
|
||||
@ -916,6 +926,77 @@ class MyTimelineView(NavigationView):
|
||||
scrolled.add(box)
|
||||
return scrolled
|
||||
|
||||
def _create_date_calendar_widget(self, label: str, year_changed_handler: Callable,
|
||||
date_selected_handler: Callable,
|
||||
month_changed_handler: Callable,
|
||||
calendar_key: str, spin_key: str,
|
||||
current_year: int, min_year: int, max_year: int) -> Tuple[Gtk.Frame, Gtk.Calendar, Gtk.SpinButton]:
|
||||
"""
|
||||
Create a date calendar widget with year selector.
|
||||
|
||||
Args:
|
||||
label: Label for the frame.
|
||||
year_changed_handler: Handler for year spin button changes.
|
||||
date_selected_handler: Handler for calendar date selection.
|
||||
month_changed_handler: Handler for calendar month changes.
|
||||
calendar_key: Key to store calendar widget in _filter_widgets.
|
||||
spin_key: Key to store spin button widget in _filter_widgets.
|
||||
current_year: Initial year value.
|
||||
min_year: Minimum year for the spin button.
|
||||
max_year: Maximum year for the spin button.
|
||||
|
||||
Returns:
|
||||
Tuple containing (frame, calendar, spin_button).
|
||||
"""
|
||||
frame = Gtk.Frame(label=label)
|
||||
frame.set_label_align(0.5, 0.5)
|
||||
|
||||
container = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=CALENDAR_CONTAINER_SPACING)
|
||||
container.set_margin_start(CALENDAR_CONTAINER_MARGIN)
|
||||
container.set_margin_end(CALENDAR_CONTAINER_MARGIN)
|
||||
container.set_margin_top(CALENDAR_CONTAINER_MARGIN)
|
||||
container.set_margin_bottom(CALENDAR_CONTAINER_MARGIN)
|
||||
|
||||
# Year selector
|
||||
year_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=YEAR_SELECTOR_SPACING)
|
||||
year_label = Gtk.Label(label=_("Year:"))
|
||||
year_adjustment = Gtk.Adjustment(
|
||||
value=current_year,
|
||||
lower=min_year,
|
||||
upper=max_year,
|
||||
step_increment=1,
|
||||
page_increment=10
|
||||
)
|
||||
year_spin = Gtk.SpinButton()
|
||||
year_spin.set_adjustment(year_adjustment)
|
||||
year_spin.set_numeric(True)
|
||||
year_spin.set_update_policy(Gtk.SpinButtonUpdatePolicy.IF_VALID)
|
||||
year_spin.set_width_chars(6)
|
||||
year_spin.connect("value-changed", year_changed_handler)
|
||||
|
||||
year_box.pack_start(year_label, False, False, 0)
|
||||
year_box.pack_start(year_spin, False, False, 0)
|
||||
container.pack_start(year_box, False, False, 0)
|
||||
|
||||
# Calendar
|
||||
calendar = Gtk.Calendar()
|
||||
calendar.set_display_options(
|
||||
Gtk.CalendarDisplayOptions.SHOW_HEADING |
|
||||
Gtk.CalendarDisplayOptions.SHOW_DAY_NAMES |
|
||||
Gtk.CalendarDisplayOptions.SHOW_WEEK_NUMBERS
|
||||
)
|
||||
calendar.connect("day-selected", date_selected_handler)
|
||||
calendar.connect("month-changed", month_changed_handler)
|
||||
container.pack_start(calendar, True, True, 0)
|
||||
|
||||
frame.add(container)
|
||||
|
||||
# Store widgets
|
||||
self._filter_widgets[calendar_key] = calendar
|
||||
self._filter_widgets[spin_key] = year_spin
|
||||
|
||||
return frame, calendar, year_spin
|
||||
|
||||
def _build_date_range_filter_page(self) -> Gtk.Widget:
|
||||
"""
|
||||
Build the date range filter page with date chooser widgets.
|
||||
@ -926,18 +1007,18 @@ class MyTimelineView(NavigationView):
|
||||
scrolled = Gtk.ScrolledWindow()
|
||||
scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
|
||||
box.set_margin_start(10)
|
||||
box.set_margin_end(10)
|
||||
box.set_margin_top(10)
|
||||
box.set_margin_bottom(10)
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=FILTER_PAGE_SPACING)
|
||||
box.set_margin_start(FILTER_PAGE_MARGIN)
|
||||
box.set_margin_end(FILTER_PAGE_MARGIN)
|
||||
box.set_margin_top(FILTER_PAGE_MARGIN)
|
||||
box.set_margin_bottom(FILTER_PAGE_MARGIN)
|
||||
|
||||
info_label = Gtk.Label(label=_("Select date range to filter events. Leave unselected to show all dates."))
|
||||
info_label.set_line_wrap(True)
|
||||
box.pack_start(info_label, False, False, 0)
|
||||
|
||||
# Calendar container
|
||||
calendar_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=15)
|
||||
calendar_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=CALENDAR_HORIZONTAL_SPACING)
|
||||
calendar_box.set_homogeneous(True)
|
||||
|
||||
# Year range for genealogical data
|
||||
@ -946,86 +1027,32 @@ class MyTimelineView(NavigationView):
|
||||
min_year = MIN_GENEALOGICAL_YEAR
|
||||
max_year = current_year + 10
|
||||
|
||||
# From date calendar with year selector
|
||||
from_frame = Gtk.Frame(label=_("From Date"))
|
||||
from_frame.set_label_align(0.5, 0.5)
|
||||
from_calendar_container = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
|
||||
from_calendar_container.set_margin_start(5)
|
||||
from_calendar_container.set_margin_end(5)
|
||||
from_calendar_container.set_margin_top(5)
|
||||
from_calendar_container.set_margin_bottom(5)
|
||||
|
||||
# Year selector for From date
|
||||
from_year_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
|
||||
from_year_label = Gtk.Label(label=_("Year:"))
|
||||
from_year_adjustment = Gtk.Adjustment(
|
||||
value=current_year,
|
||||
lower=min_year,
|
||||
upper=max_year,
|
||||
step_increment=1,
|
||||
page_increment=10
|
||||
# Create From date calendar widget
|
||||
from_frame, from_calendar, from_year_spin = self._create_date_calendar_widget(
|
||||
label=_("From Date"),
|
||||
year_changed_handler=self._on_from_year_changed,
|
||||
date_selected_handler=self._on_from_date_selected,
|
||||
month_changed_handler=self._on_from_calendar_changed,
|
||||
calendar_key='date_from_calendar',
|
||||
spin_key='date_from_year_spin',
|
||||
current_year=current_year,
|
||||
min_year=min_year,
|
||||
max_year=max_year
|
||||
)
|
||||
from_year_spin = Gtk.SpinButton()
|
||||
from_year_spin.set_adjustment(from_year_adjustment)
|
||||
from_year_spin.set_numeric(True)
|
||||
from_year_spin.set_update_policy(Gtk.SpinButtonUpdatePolicy.IF_VALID)
|
||||
from_year_spin.set_width_chars(6)
|
||||
from_year_spin.connect("value-changed", self._on_from_year_changed)
|
||||
from_year_box.pack_start(from_year_label, False, False, 0)
|
||||
from_year_box.pack_start(from_year_spin, False, False, 0)
|
||||
from_calendar_container.pack_start(from_year_box, False, False, 0)
|
||||
|
||||
from_calendar = Gtk.Calendar()
|
||||
from_calendar.set_display_options(
|
||||
Gtk.CalendarDisplayOptions.SHOW_HEADING |
|
||||
Gtk.CalendarDisplayOptions.SHOW_DAY_NAMES |
|
||||
Gtk.CalendarDisplayOptions.SHOW_WEEK_NUMBERS
|
||||
)
|
||||
from_calendar.connect("day-selected", self._on_from_date_selected)
|
||||
from_calendar.connect("month-changed", self._on_from_calendar_changed)
|
||||
from_calendar_container.pack_start(from_calendar, True, True, 0)
|
||||
from_frame.add(from_calendar_container)
|
||||
calendar_box.pack_start(from_frame, True, True, 0)
|
||||
|
||||
# To date calendar with year selector
|
||||
to_frame = Gtk.Frame(label=_("To Date"))
|
||||
to_frame.set_label_align(0.5, 0.5)
|
||||
to_calendar_container = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
|
||||
to_calendar_container.set_margin_start(5)
|
||||
to_calendar_container.set_margin_end(5)
|
||||
to_calendar_container.set_margin_top(5)
|
||||
to_calendar_container.set_margin_bottom(5)
|
||||
|
||||
# Year selector for To date
|
||||
to_year_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
|
||||
to_year_label = Gtk.Label(label=_("Year:"))
|
||||
to_year_adjustment = Gtk.Adjustment(
|
||||
value=current_year,
|
||||
lower=min_year,
|
||||
upper=max_year,
|
||||
step_increment=1,
|
||||
page_increment=10
|
||||
# Create To date calendar widget
|
||||
to_frame, to_calendar, to_year_spin = self._create_date_calendar_widget(
|
||||
label=_("To Date"),
|
||||
year_changed_handler=self._on_to_year_changed,
|
||||
date_selected_handler=self._on_to_date_selected,
|
||||
month_changed_handler=self._on_to_calendar_changed,
|
||||
calendar_key='date_to_calendar',
|
||||
spin_key='date_to_year_spin',
|
||||
current_year=current_year,
|
||||
min_year=min_year,
|
||||
max_year=max_year
|
||||
)
|
||||
to_year_spin = Gtk.SpinButton()
|
||||
to_year_spin.set_adjustment(to_year_adjustment)
|
||||
to_year_spin.set_numeric(True)
|
||||
to_year_spin.set_update_policy(Gtk.SpinButtonUpdatePolicy.IF_VALID)
|
||||
to_year_spin.set_width_chars(6)
|
||||
to_year_spin.connect("value-changed", self._on_to_year_changed)
|
||||
to_year_box.pack_start(to_year_label, False, False, 0)
|
||||
to_year_box.pack_start(to_year_spin, False, False, 0)
|
||||
to_calendar_container.pack_start(to_year_box, False, False, 0)
|
||||
|
||||
to_calendar = Gtk.Calendar()
|
||||
to_calendar.set_display_options(
|
||||
Gtk.CalendarDisplayOptions.SHOW_HEADING |
|
||||
Gtk.CalendarDisplayOptions.SHOW_DAY_NAMES |
|
||||
Gtk.CalendarDisplayOptions.SHOW_WEEK_NUMBERS
|
||||
)
|
||||
to_calendar.connect("day-selected", self._on_to_date_selected)
|
||||
to_calendar.connect("month-changed", self._on_to_calendar_changed)
|
||||
to_calendar_container.pack_start(to_calendar, True, True, 0)
|
||||
to_frame.add(to_calendar_container)
|
||||
calendar_box.pack_start(to_frame, True, True, 0)
|
||||
|
||||
box.pack_start(calendar_box, True, True, 0)
|
||||
@ -1043,23 +1070,32 @@ class MyTimelineView(NavigationView):
|
||||
self.date_validation_label.set_markup("<span color='red'></span>")
|
||||
box.pack_start(self.date_validation_label, False, False, 0)
|
||||
|
||||
self._filter_widgets['date_from_calendar'] = from_calendar
|
||||
self._filter_widgets['date_to_calendar'] = to_calendar
|
||||
self._filter_widgets['date_from_year_spin'] = from_year_spin
|
||||
self._filter_widgets['date_to_year_spin'] = to_year_spin
|
||||
|
||||
scrolled.add(box)
|
||||
return scrolled
|
||||
|
||||
def _update_filter_dialog_state(self) -> None:
|
||||
def _update_year_spin_button(self, spin_key: str, year: int, handler: Callable) -> None:
|
||||
"""
|
||||
Update the filter dialog widgets to reflect current filter state.
|
||||
Update a year spin button value, blocking signals during update.
|
||||
|
||||
Args:
|
||||
spin_key: Key to find the spin button in _filter_widgets.
|
||||
year: Year value to set.
|
||||
handler: Handler function to block/unblock during update.
|
||||
"""
|
||||
if not hasattr(self, '_filter_widgets'):
|
||||
if spin_key in self._filter_widgets:
|
||||
year_spin = self._filter_widgets[spin_key]
|
||||
year_spin.handler_block_by_func(handler)
|
||||
year_spin.set_value(year)
|
||||
year_spin.handler_unblock_by_func(handler)
|
||||
|
||||
def _update_event_type_widgets(self) -> None:
|
||||
"""
|
||||
Update event type filter widgets to reflect current filter state.
|
||||
"""
|
||||
if 'event_type_checkboxes' not in self._filter_widgets:
|
||||
return
|
||||
|
||||
# Update event type checkboxes
|
||||
if 'event_type_checkboxes' in self._filter_widgets:
|
||||
for event_type, checkbox in self._filter_widgets['event_type_checkboxes'].items():
|
||||
if not self.active_event_types:
|
||||
checkbox.set_active(True) # All selected when filter is off
|
||||
@ -1079,8 +1115,13 @@ class MyTimelineView(NavigationView):
|
||||
# Update category checkbox state based on children
|
||||
self._update_group_checkbox_state(category_checkbox, child_checkboxes)
|
||||
|
||||
# Update person checkboxes with families
|
||||
if 'person_checkboxes' in self._filter_widgets and 'person_container' in self._filter_widgets:
|
||||
def _update_person_filter_widgets(self) -> None:
|
||||
"""
|
||||
Update person filter widgets to reflect current filter state.
|
||||
"""
|
||||
if 'person_checkboxes' not in self._filter_widgets or 'person_container' not in self._filter_widgets:
|
||||
return
|
||||
|
||||
# Clear existing person checkboxes and family expanders
|
||||
container = self._filter_widgets['person_container']
|
||||
|
||||
@ -1098,7 +1139,9 @@ class MyTimelineView(NavigationView):
|
||||
self._filter_widgets['person_checkboxes'].clear()
|
||||
|
||||
# Collect all families and create expanders
|
||||
if self.dbstate.is_open():
|
||||
if not self.dbstate.is_open():
|
||||
return
|
||||
|
||||
try:
|
||||
# Initialize family_expanders if not exists
|
||||
if 'family_expanders' not in self._filter_widgets:
|
||||
@ -1183,8 +1226,13 @@ class MyTimelineView(NavigationView):
|
||||
except (AttributeError, KeyError) as e:
|
||||
logger.warning(f"Error updating person filter: {e}", exc_info=True)
|
||||
|
||||
# Update date range calendars and year selectors
|
||||
if 'date_from_calendar' in self._filter_widgets and 'date_to_calendar' in self._filter_widgets:
|
||||
def _update_date_range_widgets(self) -> None:
|
||||
"""
|
||||
Update date range filter widgets to reflect current filter state.
|
||||
"""
|
||||
if 'date_from_calendar' not in self._filter_widgets or 'date_to_calendar' not in self._filter_widgets:
|
||||
return
|
||||
|
||||
from_calendar = self._filter_widgets['date_from_calendar']
|
||||
to_calendar = self._filter_widgets['date_to_calendar']
|
||||
|
||||
@ -1204,17 +1252,8 @@ class MyTimelineView(NavigationView):
|
||||
to_calendar.select_month(current_to_month, to_year)
|
||||
|
||||
# Update year spin buttons
|
||||
if 'date_from_year_spin' in self._filter_widgets:
|
||||
from_year_spin = self._filter_widgets['date_from_year_spin']
|
||||
from_year_spin.handler_block_by_func(self._on_from_year_changed)
|
||||
from_year_spin.set_value(from_year)
|
||||
from_year_spin.handler_unblock_by_func(self._on_from_year_changed)
|
||||
|
||||
if 'date_to_year_spin' in self._filter_widgets:
|
||||
to_year_spin = self._filter_widgets['date_to_year_spin']
|
||||
to_year_spin.handler_block_by_func(self._on_to_year_changed)
|
||||
to_year_spin.set_value(to_year)
|
||||
to_year_spin.handler_unblock_by_func(self._on_to_year_changed)
|
||||
self._update_year_spin_button('date_from_year_spin', from_year, self._on_from_year_changed)
|
||||
self._update_year_spin_button('date_to_year_spin', to_year, self._on_to_year_changed)
|
||||
else:
|
||||
# Reset to current date
|
||||
import datetime
|
||||
@ -1223,22 +1262,25 @@ class MyTimelineView(NavigationView):
|
||||
to_calendar.select_month(now.month - 1, now.year)
|
||||
|
||||
# Reset year spin buttons
|
||||
if 'date_from_year_spin' in self._filter_widgets:
|
||||
from_year_spin = self._filter_widgets['date_from_year_spin']
|
||||
from_year_spin.handler_block_by_func(self._on_from_year_changed)
|
||||
from_year_spin.set_value(now.year)
|
||||
from_year_spin.handler_unblock_by_func(self._on_from_year_changed)
|
||||
|
||||
if 'date_to_year_spin' in self._filter_widgets:
|
||||
to_year_spin = self._filter_widgets['date_to_year_spin']
|
||||
to_year_spin.handler_block_by_func(self._on_to_year_changed)
|
||||
to_year_spin.set_value(now.year)
|
||||
to_year_spin.handler_unblock_by_func(self._on_to_year_changed)
|
||||
self._update_year_spin_button('date_from_year_spin', now.year, self._on_from_year_changed)
|
||||
self._update_year_spin_button('date_to_year_spin', now.year, self._on_to_year_changed)
|
||||
|
||||
# Clear validation message
|
||||
if hasattr(self, 'date_validation_label'):
|
||||
self.date_validation_label.set_text("")
|
||||
|
||||
def _update_filter_dialog_state(self) -> None:
|
||||
"""
|
||||
Update the filter dialog widgets to reflect current filter state.
|
||||
"""
|
||||
if not hasattr(self, '_filter_widgets'):
|
||||
return
|
||||
|
||||
# Update each filter type's widgets
|
||||
self._update_event_type_widgets()
|
||||
self._update_person_filter_widgets()
|
||||
self._update_date_range_widgets()
|
||||
|
||||
def _on_filter_dialog_response(self, dialog: Gtk.Dialog, response_id: int) -> None:
|
||||
"""
|
||||
Handle filter dialog response.
|
||||
@ -1251,6 +1293,7 @@ class MyTimelineView(NavigationView):
|
||||
# Clear all filters
|
||||
self.filter_enabled = False
|
||||
self.active_event_types = set()
|
||||
self._update_normalized_active_event_types() # Invalidate cache
|
||||
self.date_range_filter = None
|
||||
self.date_range_explicit = False
|
||||
self.person_filter = None
|
||||
@ -1269,6 +1312,7 @@ class MyTimelineView(NavigationView):
|
||||
if checkbox.get_active():
|
||||
active_types.add(event_type)
|
||||
self.active_event_types = active_types if len(active_types) < len(self._filter_widgets['event_type_checkboxes']) else set()
|
||||
self._update_normalized_active_event_types() # Update cache
|
||||
|
||||
# Update category filter
|
||||
if 'category_checkboxes' in self._filter_widgets:
|
||||
@ -1867,6 +1911,52 @@ class MyTimelineView(NavigationView):
|
||||
# Update filter button state
|
||||
self._update_filter_button_state()
|
||||
|
||||
def _update_normalized_active_event_types(self) -> None:
|
||||
"""
|
||||
Update the pre-computed normalized active event types set.
|
||||
Call this whenever active_event_types changes.
|
||||
"""
|
||||
if not self.active_event_types:
|
||||
self._normalized_active_event_types = None
|
||||
else:
|
||||
self._normalized_active_event_types = {
|
||||
self._normalize_event_type(et) for et in self.active_event_types
|
||||
}
|
||||
|
||||
def _apply_event_type_filter(self, event: TimelineEvent) -> bool:
|
||||
"""
|
||||
Check if event passes event type filter.
|
||||
|
||||
Args:
|
||||
event: The event to check.
|
||||
|
||||
Returns:
|
||||
bool: True if event passes filter, False otherwise.
|
||||
"""
|
||||
if not self.active_event_types:
|
||||
return True
|
||||
# Use pre-computed normalized set if available, otherwise compute it
|
||||
if self._normalized_active_event_types is None:
|
||||
self._update_normalized_active_event_types()
|
||||
# Normalize event.event_type and compare with normalized active_event_types
|
||||
event_type_normalized = self._normalize_event_type(event.event_type)
|
||||
return event_type_normalized in self._normalized_active_event_types
|
||||
|
||||
def _apply_category_filter(self, event: TimelineEvent) -> bool:
|
||||
"""
|
||||
Check if event passes category filter.
|
||||
|
||||
Args:
|
||||
event: The event to check.
|
||||
|
||||
Returns:
|
||||
bool: True if event passes filter, False otherwise.
|
||||
"""
|
||||
if not self.category_filter:
|
||||
return True
|
||||
category = self._get_event_category(event.event_type)
|
||||
return category in self.category_filter
|
||||
|
||||
def _apply_filters(self, events: List[TimelineEvent]) -> List[TimelineEvent]:
|
||||
"""
|
||||
Apply all active filters to events.
|
||||
@ -1883,11 +1973,7 @@ class MyTimelineView(NavigationView):
|
||||
filtered = []
|
||||
for event in events:
|
||||
# Check event type filter
|
||||
if self.active_event_types:
|
||||
# Normalize event.event_type and compare with normalized active_event_types
|
||||
event_type_normalized = self._normalize_event_type(event.event_type)
|
||||
active_types_normalized = {self._normalize_event_type(et) for et in self.active_event_types}
|
||||
if event_type_normalized not in active_types_normalized:
|
||||
if not self._apply_event_type_filter(event):
|
||||
continue
|
||||
|
||||
# Check date range filter
|
||||
@ -1900,8 +1986,7 @@ class MyTimelineView(NavigationView):
|
||||
continue
|
||||
|
||||
# Check category filter
|
||||
category = self._get_event_category(event.event_type)
|
||||
if self.category_filter and category not in self.category_filter:
|
||||
if not self._apply_category_filter(event):
|
||||
continue
|
||||
|
||||
filtered.append(event)
|
||||
@ -1911,6 +1996,7 @@ class MyTimelineView(NavigationView):
|
||||
def _normalize_event_type(self, event_type: EventType) -> int:
|
||||
"""
|
||||
Normalize EventType to integer for comparison.
|
||||
Uses caching to avoid repeated conversions.
|
||||
|
||||
Args:
|
||||
event_type: The event type (may be EventType object or integer).
|
||||
@ -1918,15 +2004,23 @@ class MyTimelineView(NavigationView):
|
||||
Returns:
|
||||
int: The integer value of the event type.
|
||||
"""
|
||||
# Check cache first
|
||||
if event_type in self._event_type_normalization_cache:
|
||||
return self._event_type_normalization_cache[event_type]
|
||||
|
||||
try:
|
||||
if isinstance(event_type, int):
|
||||
return event_type
|
||||
normalized = event_type
|
||||
elif hasattr(event_type, 'value'):
|
||||
return event_type.value
|
||||
normalized = event_type.value
|
||||
else:
|
||||
return int(event_type)
|
||||
normalized = int(event_type)
|
||||
except (TypeError, ValueError, AttributeError):
|
||||
return 0 # Default to 0 if conversion fails
|
||||
normalized = 0 # Default to 0 if conversion fails
|
||||
|
||||
# Cache the result
|
||||
self._event_type_normalization_cache[event_type] = normalized
|
||||
return normalized
|
||||
|
||||
def _is_event_type_enabled(self, event_type: EventType) -> bool:
|
||||
"""
|
||||
@ -1940,10 +2034,12 @@ class MyTimelineView(NavigationView):
|
||||
"""
|
||||
if not self.active_event_types:
|
||||
return True
|
||||
# Normalize both for comparison
|
||||
# Use pre-computed normalized set if available, otherwise compute it
|
||||
if self._normalized_active_event_types is None:
|
||||
self._update_normalized_active_event_types()
|
||||
# Normalize event type and compare with normalized active types
|
||||
normalized_type = self._normalize_event_type(event_type)
|
||||
normalized_active = {self._normalize_event_type(et) for et in self.active_event_types}
|
||||
return normalized_type in normalized_active
|
||||
return normalized_type in self._normalized_active_event_types
|
||||
|
||||
def _is_date_in_range(self, date_sort: int) -> bool:
|
||||
"""
|
||||
@ -2580,6 +2676,28 @@ class MyTimelineView(NavigationView):
|
||||
|
||||
return None
|
||||
|
||||
def _get_place_name_for_event(self, event: 'Event') -> Optional[str]:
|
||||
"""
|
||||
Get the place name for an event, with error handling.
|
||||
|
||||
Args:
|
||||
event: The event object.
|
||||
|
||||
Returns:
|
||||
Optional[str]: Place name if available, None otherwise.
|
||||
"""
|
||||
place_handle = event.get_place_handle()
|
||||
if not place_handle:
|
||||
return None
|
||||
|
||||
try:
|
||||
place = self.dbstate.db.get_place_from_handle(place_handle)
|
||||
if place:
|
||||
return place.get_title()
|
||||
except (AttributeError, KeyError) as e:
|
||||
logger.debug(f"Error accessing place for event: {e}")
|
||||
return None
|
||||
|
||||
def _format_person_tooltip(self, person: 'Person', person_events: List[TimelineEvent]) -> str:
|
||||
"""
|
||||
Format tooltip for person with multiple events.
|
||||
@ -2605,15 +2723,9 @@ class MyTimelineView(NavigationView):
|
||||
tooltip_text += f"{date_str} - {event_type_str}\n"
|
||||
|
||||
# Add place if available
|
||||
place_handle = event_data.event.get_place_handle()
|
||||
if place_handle:
|
||||
try:
|
||||
place = self.dbstate.db.get_place_from_handle(place_handle)
|
||||
if place:
|
||||
place_name = place.get_title()
|
||||
place_name = self._get_place_name_for_event(event_data.event)
|
||||
if place_name:
|
||||
tooltip_text += f" 📍 {place_name}\n"
|
||||
except (AttributeError, KeyError) as e:
|
||||
logger.debug(f"Error accessing place in tooltip: {e}")
|
||||
|
||||
return tooltip_text
|
||||
|
||||
@ -2634,15 +2746,9 @@ class MyTimelineView(NavigationView):
|
||||
tooltip_text = f"<b>{date_str}</b>\n{event_type_str}"
|
||||
|
||||
# Get place information
|
||||
place_handle = event.get_place_handle()
|
||||
if place_handle:
|
||||
try:
|
||||
place = self.dbstate.db.get_place_from_handle(place_handle)
|
||||
if place:
|
||||
place_name = place.get_title()
|
||||
place_name = self._get_place_name_for_event(event)
|
||||
if place_name:
|
||||
tooltip_text += f"\n📍 {place_name}"
|
||||
except (AttributeError, KeyError) as e:
|
||||
logger.debug(f"Error accessing place in tooltip: {e}")
|
||||
|
||||
# Get description
|
||||
description = event.get_description()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user