feat: aggiunti: loggica random, tema scuro, correzioni mail, miglioramenti generali, cache;
This commit is contained in:
@@ -216,15 +216,29 @@ def get_users_wanting_parking(office_id: str, pool_date: date, db: Session) -> l
|
||||
|
||||
def assign_parking_fairly(office_id: str, pool_date: date, db: Session) -> dict:
|
||||
"""
|
||||
Assign parking spots fairly based on parking ratio.
|
||||
Assign parking spots based on the office's assignment_mode (fairness or random).
|
||||
Creates new DailyParkingAssignment rows only for assigned users.
|
||||
"""
|
||||
if is_closing_day(office_id, pool_date, db):
|
||||
return {"assigned": [], "waitlist": [], "closed": True}
|
||||
|
||||
# Get candidates sorted by fairness
|
||||
# Retrieve office to check assignment mode
|
||||
office = db.query(Office).filter(Office.id == office_id).first()
|
||||
mode = office.assignment_mode if office and getattr(office, 'assignment_mode', None) else "fairness"
|
||||
|
||||
# Get candidates sorted by fairness (guaranteed first, then by ratio)
|
||||
candidates = get_users_wanting_parking(office_id, pool_date, db)
|
||||
|
||||
if mode == "random":
|
||||
import random
|
||||
guaranteed = [c for c in candidates if c["has_guarantee"]]
|
||||
non_guaranteed = [c for c in candidates if not c["has_guarantee"]]
|
||||
|
||||
# Shuffle non-guaranteed users to pick randomly
|
||||
random.shuffle(non_guaranteed)
|
||||
|
||||
candidates = guaranteed + non_guaranteed
|
||||
|
||||
# Get available spots (OfficeSpots not yet in assignments table)
|
||||
free_spots = get_available_spots(office_id, pool_date, db)
|
||||
|
||||
@@ -281,6 +295,10 @@ def release_user_spot(office_id: str, user_id: str, pool_date: date, db: Session
|
||||
if not assignment:
|
||||
return False
|
||||
|
||||
# Get old user name for notification
|
||||
old_user = db.query(User).filter(User.id == user_id).first()
|
||||
old_user_name = old_user.name if old_user else "un collega"
|
||||
|
||||
# Capture spot ID before deletion
|
||||
spot_id = assignment.spot_id
|
||||
|
||||
@@ -305,6 +323,13 @@ def release_user_spot(office_id: str, user_id: str, pool_date: date, db: Session
|
||||
db.add(new_assignment)
|
||||
db.commit()
|
||||
|
||||
# Notify the lucky user
|
||||
from services.notifications import notify_parking_released_to_user
|
||||
top_user = db.query(User).filter(User.id == top_candidate["user_id"]).first()
|
||||
if top_user:
|
||||
spot_name = get_spot_display_name(spot_id, office_id, db)
|
||||
notify_parking_released_to_user(top_user, pool_date, spot_name, old_user_name)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -328,31 +353,6 @@ def handle_presence_change(user_id: str, change_date: date, old_status: Presence
|
||||
# User no longer coming - release their spot (will auto-reassign)
|
||||
release_user_spot(office.id, user_id, change_date, db)
|
||||
|
||||
elif new_status == PresenceStatus.PRESENT:
|
||||
# Check booking window
|
||||
should_assign = True
|
||||
if office.booking_window_enabled:
|
||||
from zoneinfo import ZoneInfo
|
||||
tz = ZoneInfo(config.TIMEZONE)
|
||||
now = datetime.now(tz)
|
||||
|
||||
# Allocation time is Day-1 at cutoff hour
|
||||
cutoff_dt = datetime.combine(change_date - timedelta(days=1), datetime.min.time())
|
||||
cutoff_dt = cutoff_dt.replace(
|
||||
hour=office.booking_window_end_hour,
|
||||
minute=office.booking_window_end_minute,
|
||||
tzinfo=tz
|
||||
)
|
||||
|
||||
# If now is before cutoff, do not assign yet (wait for batch job)
|
||||
if now < cutoff_dt:
|
||||
should_assign = False
|
||||
config.logger.debug(f"Queuing parking request for user {user_id} on {change_date} (Window open until {cutoff_dt})")
|
||||
|
||||
if should_assign:
|
||||
# User coming in - run fair assignment for this date
|
||||
assign_parking_fairly(office.id, change_date, db)
|
||||
|
||||
|
||||
def clear_assignments_for_office_date(office_id: str, pool_date: date, db: Session) -> int:
|
||||
"""
|
||||
@@ -405,7 +405,24 @@ def process_daily_allocations(db: Session):
|
||||
# Cutoff is defined as "Previous Day" (today) at Booking End Hour
|
||||
# If NOW matches the cutoff time, we run allocation for TOMORROW
|
||||
if now.hour == office.booking_window_end_hour and now.minute == office.booking_window_end_minute:
|
||||
|
||||
# Non eseguiamo l'assegnazione se oggi è un giorno di chiusura
|
||||
# (è già stata fatta l'assegnazione per i giorni futuri nell'ultimo giorno lavorativo)
|
||||
if is_closing_day(office.id, now.date(), db):
|
||||
config.logger.info(f"[SCHEDULER] Skipping batch allocation for {office.name} because today ({now.date()}) is a closing day.")
|
||||
continue
|
||||
|
||||
# Troviamo il prossimo giorno lavorativo a partire da "domani"
|
||||
target_date = now.date() + timedelta(days=1)
|
||||
days_ahead = 1
|
||||
while is_closing_day(office.id, target_date, db) and days_ahead <= 30:
|
||||
target_date += timedelta(days=1)
|
||||
days_ahead += 1
|
||||
|
||||
if days_ahead > 30:
|
||||
config.logger.warning(f"[SCHEDULER] Could not find a working day within 30 days for {office.name}.")
|
||||
continue
|
||||
|
||||
config.logger.info(f"[SCHEDULER] CUTOFF REACHED for {office.name} (Cutoff: {office.booking_window_end_hour}:{office.booking_window_end_minute}). Starting Assegnazione Giornaliera parcheggi for {target_date}")
|
||||
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user