Untitled
unknown
plain_text
a month ago
12 kB
3
Indexable
import json import os import time from datetime import datetime from typing import Dict, Any, List, Optional from filelock import FileLock class StatsManager: _instance = None def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) cls._instance._initialized = False return cls._instance def __init__(self): if self._initialized: return # Define file paths self.stats_file = 'sorting_stats.json' self.impact_file = 'environmental_impact.json' self.time_file = 'time_tracking.json' self.fill_file = 'fill_history.json' self.achievements_file = 'achievements.json' # Initialize file locks self.locks = {} for file in [self.stats_file, self.impact_file, self.time_file, self.fill_file, self.achievements_file]: self.locks[file] = FileLock(f"{file}.lock") # Initialize all files with default structures self._initialize_files() self._initialized = True def _initialize_files(self): """Initialize all tracking files with default structures""" # Default sorting stats structure default_stats = { "total_items_sorted": 0, "items_per_bin": { "bin1": 0, "bin2": 0, "bin3": 0, "bin4": 0 }, "sort_events": [], "bin_emptying_counts": { "bin1": 0, "bin2": 0, "bin3": 0, "bin4": 0 }, "classification_counts": { "api": 0, "local": 0 } } # Default environmental impact structure default_impact = { "co2_saved": 0.0, "trees_saved": 0.0, "recycled_weights": { "paper": 0.0, "plastic": 0.0, "organic": 0.0 } } # Default time tracking structure default_time = { "hours_sorted": [], "last_sort_timestamp": "", "current_streak": { "start_date": "", "days": 0 }, "weekend_streak": { "count": 0, "last_weekend": "" }, "seasonal_counts": { "summer": 0, "winter": 0 }, "daily_sorts": {}, "weekly_sorts": {}, "monthly_sorts": {} } # Default fill history structure default_fill = { "bin1": {"history": [], "emptying_events": []}, "bin2": {"history": [], "emptying_events": []}, "bin3": {"history": [], "emptying_events": []}, "bin4": {"history": [], "emptying_events": []} } # Default achievements structure default_achievements = { "achievements": { "beginner_sorter": { "id": "beginner_sorter", "name": "Beginner Sorter", "description": "Sort your first item", "status": "locked", "progress": 0, "unlock_date": None }, # Add all other achievements here } } # Initialize all files if they don't exist file_defaults = { self.stats_file: default_stats, self.impact_file: default_impact, self.time_file: default_time, self.fill_file: default_fill, self.achievements_file: default_achievements } for filename, default_data in file_defaults.items(): if not os.path.exists(filename): self._safe_write(filename, default_data) def _safe_read(self, filename: str) -> Dict: """Safely read JSON file with lock""" with self.locks[filename]: try: with open(filename, 'r') as f: return json.load(f) except FileNotFoundError: return {} def _safe_write(self, filename: str, data: Dict): """Safely write to JSON file with lock""" with self.locks[filename]: # Write to temporary file first temp_file = f"{filename}.tmp" with open(temp_file, 'w') as f: json.dump(data, f, indent=2) # Rename temporary file to actual file os.replace(temp_file, filename) def record_sort_event(self, bin_id: str, fill_level_before: float, fill_level_after: float, classification_method: str): """Record a sorting event with all related statistics""" current_time = datetime.now() # Update sorting stats stats = self._safe_read(self.stats_file) stats['total_items_sorted'] += 1 stats['items_per_bin'][bin_id] += 1 stats['classification_counts'][classification_method] += 1 # Add sort event event = { 'timestamp': current_time.isoformat(), 'bin_id': bin_id, 'fill_level_before': fill_level_before, 'fill_level_after': fill_level_after, 'classification_method': classification_method } stats['sort_events'].append(event) self._safe_write(self.stats_file, stats) # Update time tracking self._update_time_tracking(current_time, bin_id) # Update fill history self._update_fill_history(bin_id, fill_level_before, fill_level_after, current_time) # Update environmental impact self._update_environmental_impact(bin_id) # Check achievements self._check_achievements(bin_id, current_time) def record_bin_empty(self, bin_id: str, level_before: float): """Record when a bin is emptied""" current_time = datetime.now() # Update emptying counts stats = self._safe_read(self.stats_file) stats['bin_emptying_counts'][bin_id] += 1 self._safe_write(self.stats_file, stats) # Update fill history fill_history = self._safe_read(self.fill_file) emptying_event = { "timestamp": current_time.isoformat(), "level_before": level_before, "level_after": 0.0 } fill_history[bin_id]["emptying_events"].append(emptying_event) self._safe_write(self.fill_file, fill_history) def _update_time_tracking(self, current_time: datetime, bin_id: str): """Update all time-based tracking metrics""" time_data = self._safe_read(self.time_file) # Update hours sorted current_hour = current_time.hour if str(current_hour) not in time_data['hours_sorted']: time_data['hours_sorted'].append(str(current_hour)) # Update streak tracking last_sort = datetime.fromisoformat(time_data['last_sort_timestamp']) if time_data['last_sort_timestamp'] else None if not last_sort or (current_time.date() - last_sort.date()).days > 1: time_data['current_streak']['start_date'] = current_time.isoformat() time_data['current_streak']['days'] = 1 elif (current_time.date() - last_sort.date()).days == 1: time_data['current_streak']['days'] += 1 # Update last sort timestamp time_data['last_sort_timestamp'] = current_time.isoformat() # Update seasonal tracking month = current_time.month if 6 <= month <= 8: # Summer months time_data['seasonal_counts']['summer'] += 1 elif month <= 2 or month == 12: # Winter months time_data['seasonal_counts']['winter'] += 1 self._safe_write(self.time_file, time_data) def _update_fill_history(self, bin_id: str, level_before: float, level_after: float, timestamp: datetime): """Update fill level history""" fill_data = self._safe_read(self.fill_file) event = { "timestamp": timestamp.isoformat(), "level": level_after, "event_type": "sort" } fill_data[bin_id]["history"].append(event) self._safe_write(self.fill_file, fill_data) def _update_environmental_impact(self, bin_id: str): """Update environmental impact calculations""" impact_data = self._safe_read(self.impact_file) # Example calculations (adjust these based on your actual impact metrics) if bin_id == "bin3": # Paper bin impact_data['trees_saved'] += 0.1 impact_data['co2_saved'] += 2.5 elif bin_id == "bin2": # Recycling bin impact_data['co2_saved'] += 1.8 self._safe_write(self.impact_file, impact_data) def _check_achievements(self, bin_id: str, current_time: datetime): """Check and update achievements""" achievements = self._safe_read(self.achievements_file) stats = self._safe_read(self.stats_file) # Check sorting milestones total_sorted = stats['total_items_sorted'] milestone_achievements = { "beginner_sorter": 1, "dedicated_recycler": 100, "waste_warrior": 1000, "sorting_legend": 10000 } for achievement_id, required_count in milestone_achievements.items(): if (total_sorted >= required_count and achievements['achievements'][achievement_id]['status'] == 'locked'): achievements['achievements'][achievement_id].update({ 'status': 'completed', 'progress': 100, 'unlock_date': current_time.isoformat() }) # Update bin specialization progress items_in_bin = stats['items_per_bin'][bin_id] bin_achievement_map = { "bin1": "organic_expert", "bin2": "recycling_rockstar", "bin3": "paper_champion", "bin4": "residual_master" } if bin_id in bin_achievement_map: achievement_id = bin_achievement_map[bin_id] if items_in_bin >= 100 and achievements['achievements'][achievement_id]['status'] == 'locked': achievements['achievements'][achievement_id].update({ 'status': 'completed', 'progress': 100, 'unlock_date': current_time.isoformat() }) self._safe_write(self.achievements_file, achievements) def get_statistics(self) -> Dict: """Get all statistics for display""" stats = self._safe_read(self.stats_file) impact = self._safe_read(self.impact_file) time_data = self._safe_read(self.time_file) return { "total_items": stats['total_items_sorted'], "items_per_bin": stats['items_per_bin'], "environmental_impact": { "co2_saved": impact['co2_saved'], "trees_saved": impact['trees_saved'] }, "current_streak": time_data['current_streak'], "seasonal_counts": time_data['seasonal_counts'], "classification_methods": stats['classification_counts'] } def get_achievements(self) -> Dict: """Get all achievements and their status""" return self._safe_read(self.achievements_file)
Editor is loading...
Leave a Comment