Untitled
unknown
plain_text
10 months ago
12 kB
6
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