#!/usr/bin/env python3 """Event reminders cron — wysyła przypomnienia push/email 24h przed wydarzeniem. Uruchomienie (cron co godzinę): 0 * * * * cd /var/www/nordabiznes && venv/bin/python3 scripts/event_reminders_cron.py Logika: - Szuka wydarzeń, których event_date+time_start mieści się między now()+23h a now()+25h - Które jeszcze nie mają reminder_24h_sent_at ustawionego - Dla każdego: pobiera zapisanych (EventAttendee.status != 'declined') - Dla każdego zapisanego: sprawdza notify_push_event_reminders i notify_email_event_reminders - Wysyła push i/lub email - Oznacza event.reminder_24h_sent_at = now() """ import os import sys import logging from datetime import datetime, timedelta, time sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from dotenv import load_dotenv load_dotenv() logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s') logger = logging.getLogger(__name__) from database import SessionLocal, NordaEvent, EventAttendee, User def _combine_event_datetime(event): """Zwraca datetime startu wydarzenia (data + time_start lub 09:00 jeśli brak).""" t = event.time_start if event.time_start else time(9, 0) return datetime.combine(event.event_date, t) def main(): from email_service import init_email_service init_email_service() from blueprints.push.push_service import send_push from email_service import send_email, _email_v3_wrap now = datetime.now() window_start = now + timedelta(hours=23) window_end = now + timedelta(hours=25) db = SessionLocal() try: candidates = db.query(NordaEvent).filter( NordaEvent.event_date >= window_start.date(), NordaEvent.event_date <= window_end.date(), NordaEvent.reminder_24h_sent_at.is_(None), ).all() fired = 0 for event in candidates: event_dt = _combine_event_datetime(event) if not (window_start <= event_dt <= window_end): continue attendees = db.query(EventAttendee).filter( EventAttendee.event_id == event.id, EventAttendee.status != 'declined', ).all() if not attendees: event.reminder_24h_sent_at = now continue time_str = event.time_start.strftime('%H:%M') if event.time_start else '' date_str = event.event_date.strftime('%d.%m.%Y') url_path = f'/kalendarz/{event.id}' title = f'Jutro wydarzenie: {event.title[:60]}' body = f'{date_str} {time_str} · {event.location or "Miejsce w szczegółach"}' push_count = 0 email_count = 0 for a in attendees: user = db.query(User).filter(User.id == a.user_id).first() if not user or not user.is_active: continue try: if getattr(user, 'notify_push_event_reminders', True) is not False: send_push( user_id=user.id, title=title, body=body, url=url_path, tag=f'event-reminder-{event.id}', ) push_count += 1 except Exception as e: logger.warning(f"push err user={user.id}: {e}") try: if user.email and getattr(user, 'notify_email_event_reminders', True) is not False: subject = f"Przypomnienie: {event.title[:60]} jutro o {time_str}" body_text = f"Przypominamy o wydarzeniu na które jesteś zapisany:\n\n{event.title}\nTermin: {date_str} {time_str}\nMiejsce: {event.location or '-'}\n\nSzczegóły: https://nordabiznes.pl{url_path}" content = ( f'

Cześć {user.name or user.email}!

' f'

Przypominamy o wydarzeniu na które jesteś zapisany:

' f'

{event.title}

' f'

Termin: {date_str} {time_str}
' f'Miejsce: {event.location or "-"}

' f'

Zobacz szczegóły

' ) body_html = _email_v3_wrap('Przypomnienie o wydarzeniu', 'Norda Biznes Partner', content) send_email( to=[user.email], subject=subject, body_text=body_text, body_html=body_html, email_type='event_reminder', user_id=user.id, recipient_name=user.name, notification_type='event_reminders', ) email_count += 1 except Exception as e: logger.warning(f"email err user={user.id}: {e}") event.reminder_24h_sent_at = now fired += 1 logger.info(f"event={event.id} '{event.title}' — push={push_count}, email={email_count}") db.commit() logger.info(f"Done. Fired reminders for {fired} event(s).") finally: db.close() if __name__ == '__main__': main()