362 lines
13 KiB
Python
362 lines
13 KiB
Python
import json
|
|
import os
|
|
from datetime import datetime
|
|
from typing import Dict, List, Optional
|
|
|
|
class DataManager:
|
|
def __init__(self, participants_file="participants.json", logs_file="scan_logs.json"):
|
|
self.participants_file = participants_file
|
|
self.logs_file = logs_file
|
|
self.init_files()
|
|
|
|
def init_files(self):
|
|
"""Initialize JSON files if they don't exist"""
|
|
if not os.path.exists(self.participants_file):
|
|
self.save_participants([])
|
|
if not os.path.exists(self.logs_file):
|
|
self.save_logs([])
|
|
|
|
def load_participants(self) -> List[Dict]:
|
|
"""Load participants from JSON file"""
|
|
try:
|
|
with open(self.participants_file, 'r') as f:
|
|
return json.load(f)
|
|
except Exception as e:
|
|
print(f"Error loading participants: {e}")
|
|
return []
|
|
|
|
def save_participants(self, participants: List[Dict]):
|
|
"""Save participants to JSON file"""
|
|
try:
|
|
with open(self.participants_file, 'w') as f:
|
|
json.dump(participants, f, indent=2)
|
|
except Exception as e:
|
|
print(f"Error saving participants: {e}")
|
|
|
|
def load_logs(self) -> List[Dict]:
|
|
"""Load scan logs from JSON file"""
|
|
try:
|
|
with open(self.logs_file, 'r') as f:
|
|
return json.load(f)
|
|
except Exception as e:
|
|
print(f"Error loading logs: {e}")
|
|
return []
|
|
|
|
def save_logs(self, logs: List[Dict]):
|
|
"""Save scan logs to JSON file"""
|
|
try:
|
|
with open(self.logs_file, 'w') as f:
|
|
json.dump(logs, f, indent=2)
|
|
except Exception as e:
|
|
print(f"Error saving logs: {e}")
|
|
|
|
def add_participant(self, name: str, card_uid: str = None, expiration_date: str = None, key_uid: str = None) -> bool:
|
|
"""Add a new participant"""
|
|
try:
|
|
participants = self.load_participants()
|
|
|
|
# Check if card UID already exists
|
|
if card_uid and any(p.get('card_uid') == card_uid for p in participants):
|
|
return False
|
|
|
|
# Check if key UID already exists
|
|
if key_uid and any(p.get('key_uid') == key_uid for p in participants):
|
|
return False
|
|
|
|
new_participant = {
|
|
'id': len(participants) + 1,
|
|
'name': name,
|
|
'card_uid': card_uid,
|
|
'key_uid': key_uid,
|
|
'expiration_date': expiration_date,
|
|
'date_added': datetime.now().isoformat()
|
|
}
|
|
participants.append(new_participant)
|
|
self.save_participants(participants)
|
|
return True
|
|
except Exception as e:
|
|
print(f"Error adding participant: {e}")
|
|
return False
|
|
|
|
def get_participant_by_card(self, card_uid: str) -> Optional[Dict]:
|
|
"""Get participant by card UID or key UID"""
|
|
try:
|
|
participants = self.load_participants()
|
|
for p in participants:
|
|
if p.get('card_uid') == card_uid or p.get('key_uid') == card_uid:
|
|
return p
|
|
return None
|
|
except Exception as e:
|
|
print(f"Error getting participant: {e}")
|
|
return None
|
|
|
|
def is_card_expired(self, participant: Dict) -> bool:
|
|
"""Check if participant's card is expired"""
|
|
try:
|
|
expiration_date = participant.get('expiration_date')
|
|
if not expiration_date:
|
|
return False
|
|
|
|
exp_date = datetime.fromisoformat(expiration_date.replace('Z', '+00:00'))
|
|
current_date = datetime.now()
|
|
return current_date > exp_date
|
|
except Exception as e:
|
|
print(f"Error checking expiration: {e}")
|
|
return False
|
|
|
|
def get_all_participants(self) -> List[Dict]:
|
|
"""Get all participants"""
|
|
return self.load_participants()
|
|
|
|
def update_participant(self, participant_id: int, name: str, card_uid: str = None, expiration_date: str = None, key_uid: str = None) -> bool:
|
|
"""Update participant information"""
|
|
try:
|
|
participants = self.load_participants()
|
|
|
|
# Find the participant being updated
|
|
participant_to_update = None
|
|
for p in participants:
|
|
if p['id'] == participant_id:
|
|
participant_to_update = p
|
|
break
|
|
|
|
if not participant_to_update:
|
|
return False
|
|
|
|
# Check if new card_uid is already used by another participant (skip if same as current)
|
|
if card_uid and card_uid != participant_to_update.get('card_uid'):
|
|
for p in participants:
|
|
if p['id'] != participant_id and p.get('card_uid') == card_uid:
|
|
return False
|
|
|
|
# Check if new key_uid is already used by another participant (skip if same as current)
|
|
if key_uid and key_uid != participant_to_update.get('key_uid'):
|
|
for p in participants:
|
|
if p['id'] != participant_id and p.get('key_uid') == key_uid:
|
|
return False
|
|
|
|
# Update name (always)
|
|
participant_to_update['name'] = name
|
|
|
|
# Update UIDs and expiration only if explicitly provided (not None)
|
|
# card_uid and key_uid can be explicitly set to empty string to clear them
|
|
if card_uid is not None:
|
|
participant_to_update['card_uid'] = card_uid if card_uid else None
|
|
if key_uid is not None:
|
|
participant_to_update['key_uid'] = key_uid if key_uid else None
|
|
if expiration_date is not None:
|
|
participant_to_update['expiration_date'] = expiration_date if expiration_date else None
|
|
|
|
self.save_participants(participants)
|
|
return True
|
|
except Exception as e:
|
|
print(f"Error updating participant: {e}")
|
|
return False
|
|
|
|
def delete_participant(self, participant_id: int) -> bool:
|
|
"""Delete participant"""
|
|
try:
|
|
participants = self.load_participants()
|
|
participants = [p for p in participants if p['id'] != participant_id]
|
|
self.save_participants(participants)
|
|
return True
|
|
except Exception as e:
|
|
print(f"Error deleting participant: {e}")
|
|
return False
|
|
|
|
def log_scan(self, scanned_uid: str, result: str, name: str = "Unknown", reason: str = None, card_uid: str = None, key_uid: str = None):
|
|
"""Log a card/key scan"""
|
|
try:
|
|
logs = self.load_logs()
|
|
log_entry = {
|
|
'id': len(logs) + 1,
|
|
'card_uid': card_uid,
|
|
'key_uid': key_uid,
|
|
'name': name,
|
|
'scan_result': result,
|
|
'reason': reason,
|
|
'timestamp': datetime.now().isoformat()
|
|
}
|
|
logs.append(log_entry)
|
|
self.save_logs(logs)
|
|
except Exception as e:
|
|
print(f"Error logging scan: {e}")
|
|
|
|
def get_scan_history(self, limit: int = 100) -> List[Dict]:
|
|
"""Get scan history (most recent first)"""
|
|
try:
|
|
logs = self.load_logs()
|
|
return logs[-limit:][::-1] # Return last N items in reverse order
|
|
except Exception as e:
|
|
print(f"Error getting scan history: {e}")
|
|
return []
|
|
|
|
def get_statistics(self) -> Dict:
|
|
"""Get statistics from data"""
|
|
try:
|
|
participants = self.load_participants()
|
|
logs = self.load_logs()
|
|
|
|
pass_count = sum(1 for l in logs if l['scan_result'] == 'PASS')
|
|
fail_count = sum(1 for l in logs if l['scan_result'] == 'FAIL')
|
|
|
|
return {
|
|
'total_participants': len(participants),
|
|
'total_passes': pass_count,
|
|
'total_fails': fail_count,
|
|
'total_scans': len(logs)
|
|
}
|
|
except Exception as e:
|
|
print(f"Error getting statistics: {e}")
|
|
return {}
|
|
|
|
def log_unregistered_user(self, name: str, timestamp: str = None) -> bool:
|
|
"""Log an unregistered user manually (admin only)"""
|
|
try:
|
|
logs = self.load_logs()
|
|
log_entry = {
|
|
'id': len(logs) + 1,
|
|
'card_uid': None,
|
|
'key_uid': None,
|
|
'name': name,
|
|
'scan_result': 'MANUAL',
|
|
'reason': 'Unregistered',
|
|
'timestamp': timestamp if timestamp else datetime.now().isoformat()
|
|
}
|
|
logs.append(log_entry)
|
|
self.save_logs(logs)
|
|
return True
|
|
except Exception as e:
|
|
print(f"Error logging unregistered user: {e}")
|
|
return False
|
|
|
|
# ===== STORE =====
|
|
|
|
def load_store(self) -> Dict:
|
|
try:
|
|
store_file = self.participants_file.replace('participants.json', 'store.json')
|
|
with open(store_file, 'r') as f:
|
|
return json.load(f)
|
|
except Exception:
|
|
return {'balance': 0.0, 'items': [], 'payments': []}
|
|
|
|
def save_store(self, store: Dict):
|
|
try:
|
|
store_file = self.participants_file.replace('participants.json', 'store.json')
|
|
with open(store_file, 'w') as f:
|
|
json.dump(store, f, indent=2)
|
|
except Exception as e:
|
|
print(f"Error saving store: {e}")
|
|
|
|
def get_store_items(self) -> List[Dict]:
|
|
return self.load_store().get('items', [])
|
|
|
|
def get_balance(self) -> float:
|
|
return self.load_store().get('balance', 0.0)
|
|
|
|
def set_balance(self, amount: float) -> bool:
|
|
try:
|
|
store = self.load_store()
|
|
store['balance'] = round(amount, 2)
|
|
self.save_store(store)
|
|
return True
|
|
except Exception as e:
|
|
print(f"Error setting balance: {e}")
|
|
return False
|
|
|
|
def add_store_item(self, name: str, price: float, stock: int, image: str = None) -> bool:
|
|
try:
|
|
store = self.load_store()
|
|
items = store.get('items', [])
|
|
new_id = max((i['id'] for i in items), default=0) + 1
|
|
items.append({'id': new_id, 'name': name, 'price': round(price, 2), 'stock': stock, 'image': image})
|
|
store['items'] = items
|
|
self.save_store(store)
|
|
return True
|
|
except Exception as e:
|
|
print(f"Error adding item: {e}")
|
|
return False
|
|
|
|
def update_store_item(self, item_id: int, name: str, price: float, stock: int, image: str = None) -> bool:
|
|
try:
|
|
store = self.load_store()
|
|
items = store.get('items', [])
|
|
for item in items:
|
|
if item['id'] == item_id:
|
|
item['name'] = name
|
|
item['price'] = round(price, 2)
|
|
item['stock'] = stock
|
|
item['image'] = image
|
|
store['items'] = items
|
|
self.save_store(store)
|
|
return True
|
|
return False
|
|
except Exception as e:
|
|
print(f"Error updating item: {e}")
|
|
return False
|
|
|
|
def delete_store_item(self, item_id: int) -> bool:
|
|
try:
|
|
store = self.load_store()
|
|
items = store.get('items', [])
|
|
new_items = [i for i in items if i['id'] != item_id]
|
|
if len(new_items) == len(items):
|
|
return False
|
|
store['items'] = new_items
|
|
self.save_store(store)
|
|
return True
|
|
except Exception as e:
|
|
print(f"Error deleting item: {e}")
|
|
return False
|
|
|
|
def log_payment(self, items: list, total: float, balance_before: float, balance_after: float, buyer: str = '') -> bool:
|
|
try:
|
|
store = self.load_store()
|
|
payments = store.get('payments', [])
|
|
new_id = max((p['id'] for p in payments), default=0) + 1
|
|
payments.append({
|
|
'id': new_id,
|
|
'timestamp': datetime.now().isoformat(),
|
|
'buyer': buyer,
|
|
'items': items,
|
|
'total': round(total, 2),
|
|
'balance_before': round(balance_before, 2),
|
|
'balance_after': round(balance_after, 2)
|
|
})
|
|
store['payments'] = payments
|
|
self.save_store(store)
|
|
return True
|
|
except Exception as e:
|
|
print(f"Error logging payment: {e}")
|
|
return False
|
|
|
|
def get_payments(self) -> list:
|
|
return self.load_store().get('payments', [])
|
|
|
|
def clear_payments(self) -> bool:
|
|
try:
|
|
store = self.load_store()
|
|
store['payments'] = []
|
|
self.save_store(store)
|
|
return True
|
|
except Exception as e:
|
|
print(f"Error clearing payments: {e}")
|
|
return False
|
|
|
|
def delete_log(self, log_id: int) -> bool:
|
|
"""Delete a log entry by ID (admin only)"""
|
|
try:
|
|
logs = self.load_logs()
|
|
original_length = len(logs)
|
|
logs = [log for log in logs if log['id'] != log_id]
|
|
|
|
if len(logs) == original_length:
|
|
# Log ID not found
|
|
return False
|
|
|
|
self.save_logs(logs)
|
|
return True
|
|
except Exception as e:
|
|
print(f"Error deleting log: {e}")
|
|
return False
|