132 lines
5.4 KiB
Python
132 lines
5.4 KiB
Python
"""
|
|
Statistics module for BrickTracker
|
|
Provides statistics and analytics functionality
|
|
"""
|
|
|
|
import logging
|
|
from typing import Any
|
|
|
|
from .sql import BrickSQL
|
|
from .theme_list import BrickThemeList
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class BrickStatistics:
|
|
"""Main statistics class providing overview and detailed statistics"""
|
|
|
|
def __init__(self):
|
|
self.sql = BrickSQL()
|
|
|
|
def get_overview(self) -> dict[str, Any]:
|
|
"""Get overview statistics"""
|
|
result = self.sql.fetchone('statistics/overview')
|
|
if result:
|
|
return dict(result)
|
|
return {}
|
|
|
|
def get_theme_statistics(self) -> list[dict[str, Any]]:
|
|
"""Get statistics grouped by theme with theme names"""
|
|
results = self.sql.fetchall('statistics/themes')
|
|
|
|
# Load theme list to get theme names
|
|
theme_list = BrickThemeList()
|
|
|
|
statistics = []
|
|
for row in results:
|
|
stat = dict(row)
|
|
# Add theme name from theme list
|
|
theme = theme_list.get(stat['theme_id'])
|
|
stat['theme_name'] = theme.name if theme else f"Theme {stat['theme_id']}"
|
|
statistics.append(stat)
|
|
|
|
return statistics
|
|
|
|
def get_storage_statistics(self) -> list[dict[str, Any]]:
|
|
"""Get statistics grouped by storage location"""
|
|
results = self.sql.fetchall('statistics/storage')
|
|
return [dict(row) for row in results]
|
|
|
|
def get_purchase_location_statistics(self) -> list[dict[str, Any]]:
|
|
"""Get statistics grouped by purchase location"""
|
|
results = self.sql.fetchall('statistics/purchase_locations')
|
|
return [dict(row) for row in results]
|
|
|
|
def get_financial_summary(self) -> dict[str, Any]:
|
|
"""Get financial summary from overview statistics"""
|
|
overview = self.get_overview()
|
|
return {
|
|
'total_cost': overview.get('total_cost') or 0,
|
|
'average_cost': overview.get('average_cost') or 0,
|
|
'minimum_cost': overview.get('minimum_cost') or 0,
|
|
'maximum_cost': overview.get('maximum_cost') or 0,
|
|
'sets_with_price': overview.get('sets_with_price') or 0,
|
|
'total_sets': overview.get('total_sets') or 0,
|
|
'percentage_with_price': round(
|
|
((overview.get('sets_with_price') or 0) / max((overview.get('total_sets') or 0), 1)) * 100, 1
|
|
)
|
|
}
|
|
|
|
def get_collection_summary(self) -> dict[str, Any]:
|
|
"""Get collection summary from overview statistics"""
|
|
overview = self.get_overview()
|
|
return {
|
|
'total_sets': overview.get('total_sets') or 0,
|
|
'unique_sets': overview.get('unique_sets') or 0,
|
|
'total_parts_count': overview.get('total_parts_count') or 0,
|
|
'unique_parts': overview.get('unique_parts') or 0,
|
|
'total_minifigures_count': overview.get('total_minifigures_count') or 0,
|
|
'unique_minifigures': overview.get('unique_minifigures') or 0,
|
|
'total_missing_parts': overview.get('total_missing_parts') or 0,
|
|
'total_damaged_parts': overview.get('total_damaged_parts') or 0,
|
|
'storage_locations_used': overview.get('storage_locations_used') or 0,
|
|
'purchase_locations_used': overview.get('purchase_locations_used') or 0
|
|
}
|
|
|
|
def get_sets_by_year_statistics(self) -> list[dict[str, Any]]:
|
|
"""Get statistics grouped by LEGO set release year"""
|
|
results = self.sql.fetchall('statistics/sets_by_year')
|
|
return [dict(row) for row in results]
|
|
|
|
def get_purchases_by_year_statistics(self) -> list[dict[str, Any]]:
|
|
"""Get statistics grouped by purchase year"""
|
|
results = self.sql.fetchall('statistics/purchases_by_year')
|
|
return [dict(row) for row in results]
|
|
|
|
def get_year_summary(self) -> dict[str, Any]:
|
|
"""Get year-based summary statistics"""
|
|
sets_by_year = self.get_sets_by_year_statistics()
|
|
purchases_by_year = self.get_purchases_by_year_statistics()
|
|
|
|
# Calculate summary metrics
|
|
years_represented = len(sets_by_year)
|
|
years_with_purchases = len(purchases_by_year)
|
|
|
|
# Find peak year for collection (by set count)
|
|
peak_collection_year = None
|
|
max_sets_in_year = 0
|
|
if sets_by_year:
|
|
peak_year_data = max(sets_by_year, key=lambda x: x.get('total_sets') or 0)
|
|
peak_collection_year = peak_year_data.get('year')
|
|
max_sets_in_year = peak_year_data.get('total_sets') or 0
|
|
|
|
# Find peak spending year
|
|
peak_spending_year = None
|
|
max_spending = 0
|
|
if purchases_by_year:
|
|
spending_years = [y for y in purchases_by_year if y.get('total_spent')]
|
|
if spending_years:
|
|
peak_spending_data = max(spending_years, key=lambda x: x.get('total_spent') or 0)
|
|
peak_spending_year = peak_spending_data.get('purchase_year')
|
|
max_spending = peak_spending_data.get('total_spent') or 0
|
|
|
|
return {
|
|
'years_represented': years_represented,
|
|
'years_with_purchases': years_with_purchases,
|
|
'peak_collection_year': peak_collection_year,
|
|
'max_sets_in_year': max_sets_in_year,
|
|
'peak_spending_year': peak_spending_year,
|
|
'max_spending': max_spending,
|
|
'oldest_set_year': min([y['year'] for y in sets_by_year]) if sets_by_year else None,
|
|
'newest_set_year': max([y['year'] for y in sets_by_year]) if sets_by_year else None
|
|
} |