Compare commits
10 Commits
e4156ca096
...
2874fe35d1
| Author | SHA1 | Date | |
|---|---|---|---|
| 2874fe35d1 | |||
| 9976a95b97 | |||
| 1b6d76583c | |||
| 891a9664d8 | |||
| 19f38a4c15 | |||
| 1e2c9617d5 | |||
| a4e12d4dbb | |||
| 652c88cd99 | |||
| 1efe978728 | |||
| e6c6afc4e4 |
359
CODE_ANALYSIS.md
359
CODE_ANALYSIS.md
@ -1,359 +0,0 @@
|
|||||||
# MyTimeline Plugin - Code Analysis & Enhancement Recommendations
|
|
||||||
|
|
||||||
## Executive Summary
|
|
||||||
|
|
||||||
The MyTimeline plugin is well-structured and functional, but there are several opportunities for improvement in code quality, performance, maintainability, and user experience.
|
|
||||||
|
|
||||||
**Total Lines:** 1,312
|
|
||||||
**Methods:** 31
|
|
||||||
**Complexity:** Medium to High (some methods exceed 100 lines)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Code Duplication Issues
|
|
||||||
|
|
||||||
### 1.1 Collision Detection Logic (HIGH PRIORITY)
|
|
||||||
**Location:** `detect_label_overlaps()` (lines 516-561) and `find_event_at_position()` (lines 719-761)
|
|
||||||
|
|
||||||
**Problem:** The same collision detection algorithm is duplicated in two places, making maintenance difficult.
|
|
||||||
|
|
||||||
**Impact:**
|
|
||||||
- Bug fixes need to be applied twice
|
|
||||||
- Inconsistencies can occur between click detection and visual rendering
|
|
||||||
- ~45 lines of duplicated code
|
|
||||||
|
|
||||||
**Recommendation:**
|
|
||||||
```python
|
|
||||||
def _calculate_adjusted_positions(self, events_with_y_pos, timeline_y_start, timeline_y_end, context=None):
|
|
||||||
"""Calculate adjusted Y positions with collision detection. Reusable by both drawing and click detection."""
|
|
||||||
# Extract common logic here
|
|
||||||
pass
|
|
||||||
```
|
|
||||||
|
|
||||||
### 1.2 Label Text Generation (MEDIUM PRIORITY)
|
|
||||||
**Location:** Multiple places (lines 532-540, 734-742, 1096-1143)
|
|
||||||
|
|
||||||
**Problem:** Label text generation logic is repeated in several methods.
|
|
||||||
|
|
||||||
**Recommendation:**
|
|
||||||
```python
|
|
||||||
def _get_event_label_text(self, event, person, event_type, is_expanded=False):
|
|
||||||
"""Generate label text for an event. Centralized logic."""
|
|
||||||
date_str = get_date(event)
|
|
||||||
event_type_str = str(event_type)
|
|
||||||
if person:
|
|
||||||
person_name = name_displayer.display(person)
|
|
||||||
if is_expanded:
|
|
||||||
# Expanded format
|
|
||||||
else:
|
|
||||||
return f"{date_str} - {event_type_str} - {person_name}"
|
|
||||||
else:
|
|
||||||
# Family event format
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Performance Issues
|
|
||||||
|
|
||||||
### 2.1 Recalculating Positions on Every Mouse Event (HIGH PRIORITY)
|
|
||||||
**Location:** `find_event_at_position()` (line 686)
|
|
||||||
|
|
||||||
**Problem:** Every mouse move/click recalculates all Y positions and collision detection, which is expensive.
|
|
||||||
|
|
||||||
**Impact:**
|
|
||||||
- Laggy mouse interaction with many events
|
|
||||||
- Unnecessary CPU usage
|
|
||||||
- Poor user experience
|
|
||||||
|
|
||||||
**Recommendation:**
|
|
||||||
- Cache adjusted positions after `collect_events()` or `on_draw()`
|
|
||||||
- Only recalculate when events change or zoom changes
|
|
||||||
- Store `self.adjusted_events_cache` and invalidate on updates
|
|
||||||
|
|
||||||
```python
|
|
||||||
def collect_events(self):
|
|
||||||
# ... existing code ...
|
|
||||||
self._adjusted_events_cache = None # Invalidate cache
|
|
||||||
|
|
||||||
def _get_adjusted_events(self, context, timeline_y_start, timeline_y_end):
|
|
||||||
"""Get adjusted events, using cache if available."""
|
|
||||||
if self._adjusted_events_cache is None:
|
|
||||||
# Calculate and cache
|
|
||||||
return self._adjusted_events_cache
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.2 Temporary Cairo Surface Creation (MEDIUM PRIORITY)
|
|
||||||
**Location:** `find_event_at_position()` (lines 721-723)
|
|
||||||
|
|
||||||
**Problem:** Creates a new Cairo surface on every call, which is expensive.
|
|
||||||
|
|
||||||
**Recommendation:**
|
|
||||||
- Reuse a cached surface or context
|
|
||||||
- Or use Pango layout without Cairo context for text measurement
|
|
||||||
|
|
||||||
### 2.3 Redundant Event Collection (LOW PRIORITY)
|
|
||||||
**Location:** `on_zoom_in()`, `on_zoom_out()`, `on_zoom_reset()` (lines 563-587)
|
|
||||||
|
|
||||||
**Problem:** `collect_events()` is called on every zoom change, but events don't change, only layout does.
|
|
||||||
|
|
||||||
**Recommendation:**
|
|
||||||
- Only recalculate timeline height, not re-collect events
|
|
||||||
- Separate height calculation from event collection
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Code Organization & Maintainability
|
|
||||||
|
|
||||||
### 3.1 Long Methods (MEDIUM PRIORITY)
|
|
||||||
**Methods exceeding 50 lines:**
|
|
||||||
- `on_draw()`: 108 lines
|
|
||||||
- `draw_event_label()`: 106 lines
|
|
||||||
- `show_tooltip()`: 98 lines
|
|
||||||
- `find_event_at_position()`: 94 lines
|
|
||||||
|
|
||||||
**Recommendation:** Break down into smaller, focused methods:
|
|
||||||
```python
|
|
||||||
def on_draw(self, widget, context):
|
|
||||||
"""Main draw method - delegates to helpers."""
|
|
||||||
self._draw_background(context)
|
|
||||||
self._draw_timeline_axis(context)
|
|
||||||
events_with_y_pos = self._calculate_and_draw_events(context)
|
|
||||||
self._draw_connections(context, events_with_y_pos)
|
|
||||||
self._draw_year_markers(context)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.2 Magic Numbers (MEDIUM PRIORITY)
|
|
||||||
**Location:** Throughout codebase
|
|
||||||
|
|
||||||
**Problem:** Hard-coded values make code harder to maintain:
|
|
||||||
- `600` (clickable width)
|
|
||||||
- `30` (min spacing)
|
|
||||||
- `16` (padding)
|
|
||||||
- `25` (label offset)
|
|
||||||
- `10` (marker area padding)
|
|
||||||
|
|
||||||
**Recommendation:** Move to constants:
|
|
||||||
```python
|
|
||||||
CLICKABLE_AREA_WIDTH = 600
|
|
||||||
MIN_LABEL_SPACING = 30
|
|
||||||
LABEL_PADDING = 16
|
|
||||||
LABEL_X_OFFSET = 25
|
|
||||||
MARKER_CLICK_PADDING = 10
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.3 Tuple-Based Data Structure (LOW PRIORITY)
|
|
||||||
**Location:** `self.events` (line 231)
|
|
||||||
|
|
||||||
**Problem:** Using tuples with index access (`prev_data[6]`) is error-prone and hard to read.
|
|
||||||
|
|
||||||
**Recommendation:** Use dataclass or named tuple:
|
|
||||||
```python
|
|
||||||
from dataclasses import dataclass
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class TimelineEvent:
|
|
||||||
date_sort: int
|
|
||||||
date_obj: Date
|
|
||||||
event: Event
|
|
||||||
person: Person | None
|
|
||||||
event_type: EventType
|
|
||||||
expanded: bool
|
|
||||||
y_pos: float
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Error Handling
|
|
||||||
|
|
||||||
### 4.1 Bare Exception Handlers (MEDIUM PRIORITY)
|
|
||||||
**Location:** 26 instances of bare `except:` clauses
|
|
||||||
|
|
||||||
**Problem:** Catching all exceptions without logging makes debugging difficult.
|
|
||||||
|
|
||||||
**Recommendation:**
|
|
||||||
```python
|
|
||||||
# Instead of:
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Use:
|
|
||||||
except Exception as e:
|
|
||||||
# Log error for debugging
|
|
||||||
import logging
|
|
||||||
logging.warning(f"Error processing event: {e}", exc_info=True)
|
|
||||||
pass
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.2 Database Access Errors (LOW PRIORITY)
|
|
||||||
**Location:** `collect_events()`, `person_updated()`, etc.
|
|
||||||
|
|
||||||
**Problem:** Database errors are silently ignored.
|
|
||||||
|
|
||||||
**Recommendation:** Add specific exception handling for database errors.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Code Quality Improvements
|
|
||||||
|
|
||||||
### 5.1 Type Hints (LOW PRIORITY)
|
|
||||||
**Recommendation:** Add type hints for better IDE support and documentation:
|
|
||||||
```python
|
|
||||||
def collect_events(self) -> None:
|
|
||||||
def find_event_at_position(self, x: float, y: float) -> int | None:
|
|
||||||
def draw_event_marker(self, context: cairo.Context, x: float, y: float,
|
|
||||||
event_type: EventType, is_hovered: bool = False,
|
|
||||||
is_selected: bool = False) -> None:
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5.2 Docstrings (LOW PRIORITY)
|
|
||||||
**Status:** Most methods have docstrings, but some could be more detailed.
|
|
||||||
|
|
||||||
**Recommendation:** Add parameter and return value documentation.
|
|
||||||
|
|
||||||
### 5.3 Constants Organization (LOW PRIORITY)
|
|
||||||
**Location:** Lines 63-72
|
|
||||||
|
|
||||||
**Recommendation:** Group related constants:
|
|
||||||
```python
|
|
||||||
# Timeline Layout
|
|
||||||
TIMELINE_MARGIN_LEFT = 150
|
|
||||||
TIMELINE_MARGIN_RIGHT = 50
|
|
||||||
TIMELINE_MARGIN_TOP = 50
|
|
||||||
TIMELINE_MARGIN_BOTTOM = 50
|
|
||||||
TIMELINE_LINE_WIDTH = 3
|
|
||||||
|
|
||||||
# Event Display
|
|
||||||
EVENT_MARKER_SIZE = 10
|
|
||||||
EVENT_SPACING = 80
|
|
||||||
LABEL_X_OFFSET = 25
|
|
||||||
MIN_LABEL_SPACING = 30
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. User Experience Enhancements
|
|
||||||
|
|
||||||
### 6.1 Keyboard Navigation (NEW FEATURE)
|
|
||||||
**Recommendation:** Add keyboard shortcuts:
|
|
||||||
- Arrow keys to navigate between events
|
|
||||||
- Enter to select person
|
|
||||||
- Space to expand/collapse event
|
|
||||||
|
|
||||||
### 6.2 Event Filtering (NEW FEATURE)
|
|
||||||
**Recommendation:** Add filter options:
|
|
||||||
- Filter by event type
|
|
||||||
- Filter by person
|
|
||||||
- Date range filtering
|
|
||||||
|
|
||||||
### 6.3 Export Functionality (NEW FEATURE)
|
|
||||||
**Recommendation:** Add export options:
|
|
||||||
- Export timeline as image (PNG/SVG)
|
|
||||||
- Export event list as CSV
|
|
||||||
|
|
||||||
### 6.4 Configuration Options (NEW FEATURE)
|
|
||||||
**Recommendation:** Add user preferences:
|
|
||||||
- Customizable colors for event types
|
|
||||||
- Adjustable spacing and margins
|
|
||||||
- Font size preferences
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. Testing & Reliability
|
|
||||||
|
|
||||||
### 7.1 Unit Tests (NEW)
|
|
||||||
**Recommendation:** Add unit tests for:
|
|
||||||
- Event collection logic
|
|
||||||
- Collision detection algorithm
|
|
||||||
- Date calculations
|
|
||||||
- Coordinate transformations
|
|
||||||
|
|
||||||
### 7.2 Edge Case Handling (MEDIUM PRIORITY)
|
|
||||||
**Issues to address:**
|
|
||||||
- Very large families (100+ events)
|
|
||||||
- Events with no dates
|
|
||||||
- Events spanning very long time periods
|
|
||||||
- Zoom at extreme levels
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. Memory Optimization
|
|
||||||
|
|
||||||
### 8.1 Event Data Caching (LOW PRIORITY)
|
|
||||||
**Recommendation:** Cache formatted strings and calculated values to avoid repeated computation.
|
|
||||||
|
|
||||||
### 8.2 Tooltip Window Management (LOW PRIORITY)
|
|
||||||
**Location:** `show_tooltip()` (line 780)
|
|
||||||
|
|
||||||
**Recommendation:** Reuse tooltip window instead of creating new one each time.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Priority Summary
|
|
||||||
|
|
||||||
### High Priority (Do First)
|
|
||||||
1. ✅ Extract collision detection to shared method
|
|
||||||
2. ✅ Cache adjusted positions to improve mouse interaction performance
|
|
||||||
3. ✅ Replace bare `except:` with specific exception handling
|
|
||||||
|
|
||||||
### Medium Priority (Do Soon)
|
|
||||||
1. Extract label text generation to shared method
|
|
||||||
2. Break down long methods into smaller functions
|
|
||||||
3. Replace magic numbers with named constants
|
|
||||||
4. Optimize zoom operations (don't re-collect events)
|
|
||||||
|
|
||||||
### Low Priority (Nice to Have)
|
|
||||||
1. Add type hints
|
|
||||||
2. Use dataclasses for event data structure
|
|
||||||
3. Add keyboard navigation
|
|
||||||
4. Add event filtering
|
|
||||||
5. Add unit tests
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Estimated Impact
|
|
||||||
|
|
||||||
**Performance Improvements:**
|
|
||||||
- Mouse interaction: 50-70% faster with caching
|
|
||||||
- Zoom operations: 30-40% faster by avoiding event re-collection
|
|
||||||
|
|
||||||
**Code Quality:**
|
|
||||||
- Reduced duplication: ~100 lines of code
|
|
||||||
- Improved maintainability: Easier to modify and extend
|
|
||||||
- Better error handling: Easier debugging
|
|
||||||
|
|
||||||
**User Experience:**
|
|
||||||
- Smoother interactions
|
|
||||||
- More features (keyboard nav, filtering, export)
|
|
||||||
- Better customization options
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Implementation Order
|
|
||||||
|
|
||||||
1. **Phase 1 (Quick Wins):**
|
|
||||||
- Extract collision detection method
|
|
||||||
- Add constants for magic numbers
|
|
||||||
- Cache adjusted positions
|
|
||||||
|
|
||||||
2. **Phase 2 (Refactoring):**
|
|
||||||
- Extract label text generation
|
|
||||||
- Break down long methods
|
|
||||||
- Improve error handling
|
|
||||||
|
|
||||||
3. **Phase 3 (Enhancements):**
|
|
||||||
- Add type hints
|
|
||||||
- Use dataclasses
|
|
||||||
- Add new features (keyboard nav, filtering)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
The MyTimeline plugin is functional and well-designed, but would benefit significantly from:
|
|
||||||
- Reducing code duplication
|
|
||||||
- Improving performance through caching
|
|
||||||
- Better code organization
|
|
||||||
- Enhanced error handling
|
|
||||||
|
|
||||||
These improvements would make the codebase more maintainable, performant, and extensible.
|
|
||||||
|
|
||||||
2387
MyTimeline.py
2387
MyTimeline.py
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user