""" Notification Helpers ==================== Functions for creating and managing user notifications. """ import logging from database import SessionLocal, UserNotification, User logger = logging.getLogger(__name__) def create_notification(user_id, title, message, notification_type='info', related_type=None, related_id=None, action_url=None): """ Create a notification for a user. Args: user_id: ID of the user to notify title: Notification title message: Notification message/body notification_type: Type of notification (news, system, message, event, alert) related_type: Type of related entity (company_news, event, message, etc.) related_id: ID of the related entity action_url: URL to navigate when notification is clicked Returns: UserNotification object or None on error """ db = SessionLocal() try: notification = UserNotification( user_id=user_id, title=title, message=message, notification_type=notification_type, related_type=related_type, related_id=related_id, action_url=action_url ) db.add(notification) db.commit() db.refresh(notification) logger.info(f"Created notification for user {user_id}: {title}") return notification except Exception as e: logger.error(f"Error creating notification: {e}") db.rollback() return None finally: db.close() def create_news_notification(company_id, news_id, news_title): """ Create notification for company owner when their news is approved. Args: company_id: ID of the company news_id: ID of the approved news news_title: Title of the news """ db = SessionLocal() try: # Find users associated with this company users = db.query(User).filter( User.company_id == company_id, User.is_active == True ).all() for user in users: create_notification( user_id=user.id, title="Nowa aktualnosc o Twojej firmie", message=f"Aktualnosc '{news_title}' zostala zatwierdzona i jest widoczna na profilu firmy.", notification_type='news', related_type='company_news', related_id=news_id, action_url=f"/company/{company_id}" ) finally: db.close() def create_message_notification(user_id, sender_name, message_id): """ Create notification when user receives a private message. Args: user_id: ID of the recipient sender_name: Name of the sender message_id: ID of the message """ create_notification( user_id=user_id, title="Nowa wiadomość prywatna", message=f"Otrzymałeś nową wiadomość od {sender_name}.", notification_type='message', related_type='private_message', related_id=message_id, action_url=f"/wiadomosci/{message_id}" ) def create_event_notification(user_id, event_title, event_id): """ Create notification for upcoming event reminder. Args: user_id: ID of the user to notify event_title: Title of the event event_id: ID of the event """ create_notification( user_id=user_id, title="Przypomnienie o wydarzeniu", message=f"Zbliża się wydarzenie: {event_title}", notification_type='event', related_type='norda_event', related_id=event_id, action_url=f"/kalendarz/{event_id}" ) def notify_all_users_release(version, highlights=None): """ Notify all active users about a new system release. Args: version: Version string (e.g., 'v1.17.0') highlights: Optional list of key changes to include in notification Returns: Number of notifications created """ db = SessionLocal() try: # Get all active users users = db.query(User).filter(User.is_active == True).all() message = f"Wersja {version} jest już dostępna." if highlights: # Include first 2 highlights message += " Nowości: " + ", ".join(highlights[:2]) if len(highlights) > 2: message += f" (+{len(highlights) - 2} więcej)" count = 0 for user in users: result = create_notification( user_id=user.id, title=f"🚀 Nowa wersja systemu {version}", message=message, notification_type='system', related_type='release', related_id=None, action_url='/release-notes' ) if result: count += 1 logger.info(f"Created {count} release notifications for version {version}") return count except Exception as e: logger.error(f"Error creating release notifications: {e}") return 0 finally: db.close() def notify_all_users_announcement(announcement_id, title, category=None): """ Notify all active users about a new announcement. Args: announcement_id: ID of the announcement title: Title of the announcement category: Optional category for context Returns: Number of notifications created """ db = SessionLocal() try: # Get all active users users = db.query(User).filter(User.is_active == True).all() # Category-specific icons category_icons = { 'general': '📢', 'event': '📅', 'business_opportunity': '💼', 'member_news': '👥', 'partnership': '🤝' } icon = category_icons.get(category, '📢') count = 0 for user in users: result = create_notification( user_id=user.id, title=f"{icon} Nowe ogłoszenie w Aktualnościach", message=title[:100] + ('...' if len(title) > 100 else ''), notification_type='news', related_type='announcement', related_id=announcement_id, action_url=f'/ogloszenia/{announcement_id}' ) if result: count += 1 logger.info(f"Created {count} announcement notifications for: {title[:50]}") return count except Exception as e: logger.error(f"Error creating announcement notifications: {e}") return 0 finally: db.close() # ============================================================ # FORUM NOTIFICATIONS # ============================================================ def create_forum_reply_notification(topic_id, topic_title, replier_name, reply_id, subscriber_ids): """ Notify topic subscribers about a new reply. Args: topic_id: ID of the forum topic topic_title: Title of the topic replier_name: Name of the user who replied reply_id: ID of the new reply subscriber_ids: List of user IDs to notify Returns: Number of notifications created """ count = 0 for user_id in subscriber_ids: result = create_notification( user_id=user_id, title="Nowa odpowiedź na forum", message=f"{replier_name} odpowiedział w temacie: {topic_title[:50]}{'...' if len(topic_title) > 50 else ''}", notification_type='message', related_type='forum_reply', related_id=reply_id, action_url=f'/forum/{topic_id}#reply-{reply_id}' ) if result: count += 1 logger.info(f"Created {count} forum reply notifications for topic {topic_id}") return count def send_forum_reply_email(topic_id, topic_title, replier_name, reply_content, subscriber_emails, reply_id=None): """ Send email notifications to forum topic subscribers about a new reply. Args: topic_id: ID of the forum topic topic_title: Title of the topic replier_name: Name of the user who replied reply_content: First 200 chars of the reply content subscriber_emails: List of dicts with 'email' and 'name' keys """ from email_service import send_email base_url = "https://nordabiznes.pl" topic_url = f"{base_url}/forum/{topic_id}" + (f"#reply-{reply_id}" if reply_id else "") unsubscribe_url = f"{base_url}/forum/{topic_id}/unsubscribe" preview = reply_content[:200].strip() if len(reply_content) > 200: preview += "..." count = 0 for subscriber in subscriber_emails: subject = f"Nowa odpowiedz na forum: {topic_title[:60]}" body_text = f"""{replier_name} odpowiedzial w temacie: {topic_title} "{preview}" Zobacz pelna odpowiedz: {topic_url} --- Aby przestac obserwowac ten watek: {unsubscribe_url} Norda Biznes Partner - https://nordabiznes.pl """ from email_service import _email_v3_wrap content = f'''

Cześć {subscriber.get('name', '')}!

{replier_name} odpowiedział w temacie, który obserwujesz:

Temat

{topic_title}

{preview}

Zobacz odpowiedź →
Przestań obserwować ten wątek
''' body_html = _email_v3_wrap('Nowa odpowiedź na forum', 'Norda Biznes Partner', content) try: result = send_email( to=[subscriber['email']], subject=subject, body_text=body_text, body_html=body_html, email_type='forum_notification', recipient_name=subscriber.get('name', '') ) if result: count += 1 except Exception as e: logger.error(f"Failed to send forum reply email to {subscriber['email']}: {e}") logger.info(f"Sent {count}/{len(subscriber_emails)} forum reply emails for topic {topic_id}") return count # ============================================================ # B2B CLASSIFIEDS NOTIFICATIONS # ============================================================ def create_classified_question_notification(classified_id, classified_title, questioner_name, author_id): """Notify classified author that someone asked a question.""" return create_notification( user_id=author_id, title="Nowe pytanie do ogłoszenia B2B", message=f"{questioner_name} zadał pytanie do ogłoszenia: {classified_title[:50]}{'...' if len(classified_title) > 50 else ''}", notification_type='message', related_type='classified_question', related_id=classified_id, action_url=f'/tablica/{classified_id}' ) def create_classified_answer_notification(classified_id, classified_title, answerer_name, questioner_id): """Notify question author that the classified owner answered.""" return create_notification( user_id=questioner_id, title="Odpowiedź na Twoje pytanie B2B", message=f"{answerer_name} odpowiedział na Twoje pytanie w ogłoszeniu: {classified_title[:50]}{'...' if len(classified_title) > 50 else ''}", notification_type='message', related_type='classified_answer', related_id=classified_id, action_url=f'/tablica/{classified_id}' ) def create_classified_interest_notification(classified_id, classified_title, interested_name, author_id): """Notify classified author that someone is interested.""" return create_notification( user_id=author_id, title="Ktoś zainteresował się Twoim ogłoszeniem", message=f"{interested_name} wyraził zainteresowanie ogłoszeniem: {classified_title[:50]}{'...' if len(classified_title) > 50 else ''}", notification_type='message', related_type='classified_interest', related_id=classified_id, action_url=f'/tablica/{classified_id}' ) def send_classified_question_email(classified_id, classified_title, questioner_name, question_content, author_email, author_name, author_id=None): """Send email to classified author about a new question.""" from email_service import send_email, _email_v3_wrap base_url = "https://nordabiznes.pl" classified_url = f"{base_url}/tablica/{classified_id}" preview = question_content[:200].strip() if len(question_content) > 200: preview += "..." subject = f"Nowe pytanie do ogłoszenia: {classified_title[:60]}" body_text = f"""{questioner_name} zadał pytanie do Twojego ogłoszenia: {classified_title} "{preview}" Zobacz i odpowiedz: {classified_url} --- Norda Biznes Partner - https://nordabiznes.pl """ content = f'''

Cześć {author_name}!

{questioner_name} zadał pytanie do Twojego ogłoszenia na tablicy B2B:

Ogłoszenie

{classified_title}

{preview}

Zobacz pytanie i odpowiedz →
''' body_html = _email_v3_wrap('Nowe pytanie do ogłoszenia B2B', 'Norda Biznes Partner', content) try: return send_email( to=[author_email], subject=subject, body_text=body_text, body_html=body_html, email_type='classified_notification', recipient_name=author_name, user_id=author_id, notification_type='classified_question' if author_id else None, ) except Exception as e: logger.error(f"Failed to send classified question email to {author_email}: {e}") return None def send_classified_answer_email(classified_id, classified_title, answerer_name, answer_content, questioner_email, questioner_name, questioner_id=None): """Send email to question author about an answer.""" from email_service import send_email, _email_v3_wrap base_url = "https://nordabiznes.pl" classified_url = f"{base_url}/tablica/{classified_id}" preview = answer_content[:200].strip() if len(answer_content) > 200: preview += "..." subject = f"Odpowiedź na Twoje pytanie: {classified_title[:60]}" body_text = f"""{answerer_name} odpowiedział na Twoje pytanie w ogłoszeniu: {classified_title} "{preview}" Zobacz odpowiedź: {classified_url} --- Norda Biznes Partner - https://nordabiznes.pl """ content = f'''

Cześć {questioner_name}!

{answerer_name} odpowiedział na Twoje pytanie w ogłoszeniu:

Ogłoszenie

{classified_title}

{preview}

Zobacz odpowiedź →
''' body_html = _email_v3_wrap('Odpowiedź na pytanie B2B', 'Norda Biznes Partner', content) try: return send_email( to=[questioner_email], subject=subject, body_text=body_text, body_html=body_html, email_type='classified_notification', recipient_name=questioner_name, user_id=questioner_id, notification_type='classified_answer' if questioner_id else None, ) except Exception as e: logger.error(f"Failed to send classified answer email to {questioner_email}: {e}") return None def create_forum_reaction_notification(user_id, reactor_name, content_type, content_id, topic_id, emoji): """ Notify user when someone reacts to their content. Args: user_id: ID of the content author reactor_name: Name of user who reacted content_type: 'topic' or 'reply' content_id: ID of the content topic_id: ID of the topic emoji: The reaction emoji """ create_notification( user_id=user_id, title=f"Nowa reakcja {emoji}", message=f"{reactor_name} zareagował na Twój{'ą odpowiedź' if content_type == 'reply' else ' temat'}", notification_type='message', related_type=f'forum_{content_type}', related_id=content_id, action_url=f'/forum/{topic_id}{"#reply-" + str(content_id) if content_type == "reply" else ""}' ) def create_forum_solution_notification(user_id, topic_id, topic_title): """ Notify topic author when their question gets a solution. Args: user_id: ID of the topic author topic_id: ID of the topic topic_title: Title of the topic """ create_notification( user_id=user_id, title="Twoje pytanie ma rozwiązanie!", message=f"Odpowiedź w temacie '{topic_title[:40]}' została oznaczona jako rozwiązanie.", notification_type='message', related_type='forum_topic', related_id=topic_id, action_url=f'/forum/{topic_id}' ) def create_forum_report_notification(admin_user_ids, report_id, content_type, reporter_name): """ Notify admins about a new forum report. Args: admin_user_ids: List of admin user IDs report_id: ID of the report content_type: 'topic' or 'reply' reporter_name: Name of the reporter """ for user_id in admin_user_ids: create_notification( user_id=user_id, title="Nowe zgłoszenie na forum", message=f"{reporter_name} zgłosił {'odpowiedź' if content_type == 'reply' else 'temat'}", notification_type='alert', related_type='forum_report', related_id=report_id, action_url='/admin/forum/reports' ) def parse_mentions_and_notify(content, author_id, author_name, topic_id, content_type, content_id): """ Parse @mentions in content and send notifications. Supports formats: - @jan.kowalski (name with dots) - @jan_kowalski (name with underscores) - @jankowalski (name without separators) Args: content: Text content to parse author_id: ID of the content author (won't be notified) author_name: Name of the author topic_id: ID of the topic content_type: 'topic' or 'reply' content_id: ID of the content Returns: List of mentioned user IDs """ import re # Find all @mentions (letters, numbers, dots, underscores, hyphens) mentions = re.findall(r'@([\w.\-]+)', content) if not mentions: return [] db = SessionLocal() try: mentioned_user_ids = [] for mention in set(mentions): # Unique mentions # Try to find user by name (case-insensitive) mention_lower = mention.lower() # Try exact name match user = db.query(User).filter( User.is_active == True, User.id != author_id ).filter( (User.name.ilike(mention)) | (User.name.ilike(mention.replace('.', ' '))) | (User.name.ilike(mention.replace('_', ' '))) | (User.email.ilike(f'{mention}@%')) ).first() if user: mentioned_user_ids.append(user.id) action_url = f'/forum/{topic_id}{"#reply-" + str(content_id) if content_type == "reply" else ""}' create_notification( user_id=user.id, title=f"@{author_name} wspomniał o Tobie", message=f"Zostałeś wspomniany w {'odpowiedzi' if content_type == 'reply' else 'temacie'} na forum", notification_type='message', related_type=f'forum_{content_type}', related_id=content_id, action_url=action_url ) # Send email notification to mentioned user try: from email_service import send_email, _email_v3_wrap base_url = "https://nordabiznes.pl" full_url = base_url + action_url preview = (content[:200] + '...') if len(content) > 200 else content where = 'odpowiedzi' if content_type == 'reply' else 'temacie' subject = f"{author_name} wspomniał o Tobie na forum" body_text = f"""{author_name} wspomniał o Tobie w {where} na forum Norda Biznes. "{preview}" Zobacz: {full_url} --- Norda Biznes Partner - https://nordabiznes.pl """ html_content = f'''

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

{author_name} wspomniał o Tobie w {where} na forum:

{preview}

Zobacz na forum →
''' body_html = _email_v3_wrap('Wspomniano o Tobie', 'Norda Biznes Partner', html_content) send_email( to=[user.email], subject=subject, body_text=body_text, body_html=body_html, email_type='forum_mention', recipient_name=user.name or '' ) except Exception as e: logger.error(f"Failed to send mention email to {user.email}: {e}") return mentioned_user_ids except Exception as e: logger.error(f"Error parsing mentions: {e}") return [] finally: db.close()