From 08d6c0b069af7a45e798701ea7284a4d356ab32e Mon Sep 17 00:00:00 2001 From: Maciej Pienczyn Date: Tue, 13 Jan 2026 11:48:08 +0100 Subject: [PATCH] feat: Add 'test' category for forum topics to separate test content - Add 'test' to ForumTopic.CATEGORIES with Polish label 'Testowy' - Add gray styling for test topics (badge + card opacity) - Add scripts to list and mark test topics --- database.py | 5 +- docs/meetings/2026-01-12-artur-teams.md | 69 ++++++++++++++++ scripts/list_forum_topics.py | 40 ++++++++++ scripts/mark_test_topics.py | 45 +++++++++++ scripts/register_whatsapp_attendees.py | 102 ++++++++++++++++++++++++ templates/forum/index.html | 13 ++- 6 files changed, 271 insertions(+), 3 deletions(-) create mode 100644 docs/meetings/2026-01-12-artur-teams.md create mode 100644 scripts/list_forum_topics.py create mode 100644 scripts/mark_test_topics.py create mode 100644 scripts/register_whatsapp_attendees.py diff --git a/database.py b/database.py index 59b1e52..973c1ee 100644 --- a/database.py +++ b/database.py @@ -864,14 +864,15 @@ class ForumTopic(Base): updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now) # Constants for validation - CATEGORIES = ['feature_request', 'bug', 'question', 'announcement'] + CATEGORIES = ['feature_request', 'bug', 'question', 'announcement', 'test'] STATUSES = ['new', 'in_progress', 'resolved', 'rejected'] CATEGORY_LABELS = { 'feature_request': 'Propozycja funkcji', 'bug': 'Błąd', 'question': 'Pytanie', - 'announcement': 'Ogłoszenie' + 'announcement': 'Ogłoszenie', + 'test': 'Testowy' } STATUS_LABELS = { diff --git a/docs/meetings/2026-01-12-artur-teams.md b/docs/meetings/2026-01-12-artur-teams.md new file mode 100644 index 0000000..fe2c674 --- /dev/null +++ b/docs/meetings/2026-01-12-artur-teams.md @@ -0,0 +1,69 @@ +# Notatka ze spotkania z Arturem + +**Data:** 12 stycznia 2026 +**Platforma:** Microsoft Teams + +--- + +## Kluczowe ustalenia + +### 1. Wizytówka Google jako narzędzie SEO +- Wizytówka Google Business Profile to jedno z narzędzi SEO +- **Do zrobienia:** Pozycjonowanie na local content +- Współpraca: SEO Partners i Pixlab +- Horyzont czasowy: **2 lata** + +### 2. Strona WWW +- Skopiować całą stronę, którą zrobił Pixlab +- Dodać: **PKiD** +- Dodać: **Data powstania** + +### 3. Model monetyzacji - dostęp do platformy + +| Poziom | Cena/mies. | Dostęp | +|--------|------------|--------| +| **Członkowie Izby** | 1 zł | Pełny dostęp do bazy (płacą już ~200 zł składki) | +| **Podstawowy** | 49,99 zł | Podstawowe informacje, baza gospodarcza | +| **Premium** | 99 zł | Dostęp do wszystkiego + dodatkowe funkcjonalności | + +- **Współdzielenie kosztów** między uczestnikami + +### 4. Reprezentacja firm i treści +- Treści informacyjne "trochę jak MSN.com" +- Do ustalenia szczegóły funkcjonalności dla poziomu Premium + +### 5. Agent AI dla portalu - wizja + +**Cel:** +- Agent AI, który wie wszystko o organizacjach członkowskich +- Pomaga w kooperacji między firmami +- **Transfer informacji** do innych agentów AI (ChatGPT, Claude, itp.) + +**Strategia wdrożenia:** +1. **Najpierw:** Zbudować odpowiedni content dla firm członkowskich +2. **Później:** Poprzez odpowiednie wtyczki/plugins dystrybuować informacje do platform AI + +### 6. Case study - Waterm i ChatGPT (historia od Bartka) + +> *"Klient (Białorusin) pytany skąd trafił do Waterm odpowiedział: 'W tym ChatGPT to jesteście jedyną najlepszą firmą w Wejherowie. Tylko o Watermie mówią.'"* + +**Refleksja Bartka:** +- "Nie wiem, na ile to co robię... powinienem modlić się, żeby konkurencja tego nie robiła jak najdłużej" +- "Ale i tak tej roboty nie jestem w stanie przerobić" +- **Wizja:** "Jeśli wszyscy zadbamy o podniesienie standardu, to ludziom będzie się żyło lepiej" + +--- + +## Do zrobienia (action items) +- [ ] Pozycjonowanie na local content +- [ ] Skopiować strukturę strony Pixlab +- [ ] Zaprojektować poziomy dostępu (1 zł / 49,99 zł / 99 zł) +- [ ] Zbudować content dla firm członkowskich +- [ ] Zaplanować integrację z platformami AI (plugins) +- [ ] Usiąść i poukładać puste elementy + +--- + +## Kluczowy insight + +**Waterm jest już widoczny w ChatGPT jako "najlepsza firma w Wejherowie"** - to dowód, że obecność w AI działa. Cel: osiągnąć to samo dla wszystkich firm Norda Biznes. diff --git a/scripts/list_forum_topics.py b/scripts/list_forum_topics.py new file mode 100644 index 0000000..71abd03 --- /dev/null +++ b/scripts/list_forum_topics.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +"""Lista wszystkich wątków forum z autorami.""" + +import os +import sys +from dotenv import load_dotenv + +# Load .env first +load_dotenv() + +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from database import SessionLocal, ForumTopic, ForumReply, User + +def main(): + db = SessionLocal() + + threads = db.query(ForumTopic).order_by(ForumTopic.created_at).all() + print("=== WĄTKI FORUM ===") + print() + + for t in threads: + author = db.query(User).filter(User.id == t.author_id).first() + author_name = author.name if author else 'Unknown' + author_email = author.email if author else '' + replies_count = db.query(ForumReply).filter(ForumReply.topic_id == t.id).count() + date_str = t.created_at.strftime('%Y-%m-%d') + + print(f"ID: {t.id}") + print(f" Tytuł: {t.title}") + print(f" Autor: {author_name} ({author_email})") + print(f" Data: {date_str}") + print(f" Odpowiedzi: {replies_count}") + print(f" Przypięty: {t.is_pinned}") + print() + + db.close() + +if __name__ == "__main__": + main() diff --git a/scripts/mark_test_topics.py b/scripts/mark_test_topics.py new file mode 100644 index 0000000..3f652f5 --- /dev/null +++ b/scripts/mark_test_topics.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +"""Oznacz wszystkie obecne wątki forum jako testowe.""" + +import os +import sys +from dotenv import load_dotenv + +# Load .env first +load_dotenv() + +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from database import SessionLocal, ForumTopic, ForumReply + +def main(): + db = SessionLocal() + + # Pobierz wszystkie wątki + topics = db.query(ForumTopic).all() + + print(f"Znaleziono {len(topics)} wątków do oznaczenia jako testowe...") + print() + + for topic in topics: + old_category = topic.category + topic.category = 'test' + print(f" ID {topic.id}: {topic.title[:50]}... ({old_category} → test)") + + # Oznacz też odpowiedzi jako AI-generated (jeśli są testowe) + replies = db.query(ForumReply).all() + for reply in replies: + reply.is_ai_generated = True + + print() + print(f"Oznaczono {len(topics)} wątków jako 'test'") + print(f"Oznaczono {len(replies)} odpowiedzi jako AI-generated") + + db.commit() + db.close() + + print() + print("Gotowe!") + +if __name__ == "__main__": + main() diff --git a/scripts/register_whatsapp_attendees.py b/scripts/register_whatsapp_attendees.py new file mode 100644 index 0000000..bfee49d --- /dev/null +++ b/scripts/register_whatsapp_attendees.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +""" +Rejestracja uczestników z WhatsApp na wydarzenie Chwila dla Biznesu +Tworzy konta użytkowników (bez powiadomień) i zapisuje na wydarzenie. +""" + +import os +import sys +import secrets +from datetime import datetime + +# Dodaj ścieżkę do modułów +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from database import SessionLocal, Company, User, EventAttendee +from werkzeug.security import generate_password_hash + +def main(): + db = SessionLocal() + + # Dane do utworzenia (imię z WhatsApp, firma, email) + attendees_data = [ + ("Marcin Bulczak", "Renk Hurtownie", "sekretariat@renk.pl"), + ("Agnieszka (Aga)", "Your Welcome", "agnieszka@yourewelcome.pl"), + ("Bartosz (Bartek)", "TERMO", "biuro@termocenter.pl"), + ("Daniel", "Stalpunkt", "biuro.pomorze@stalpunkt.com"), + ("Paweł Cioban", "Kancelaria Ostrowski i Wspólnicy", "p.cioban@ostrowski-legal.net"), + ("Sławomir Grula", "Usługi Ogólnobudowlane Sławomir Grula", "slawek1055@onet.pl"), + ("Artur Czajkowski (Arturo)", "Family Art", "artczaj@gmail.com"), + ] + + event_id = 28 + + print("Tworzę konta użytkowników...") + print("=" * 60) + + try: + for name, company_name, email in attendees_data: + # Sprawdź czy użytkownik już istnieje + existing_user = db.query(User).filter(User.email == email).first() + if existing_user: + print(f"→ {name}: konto już istnieje (ID {existing_user.id})") + user_id = existing_user.id + else: + # Znajdź firmę + company = db.query(Company).filter(Company.name == company_name).first() + + # Utwórz użytkownika + temp_password = secrets.token_urlsafe(16) + user = User( + email=email, + name=name, + password_hash=generate_password_hash(temp_password), + is_active=True, + is_admin=False, + company_id=company.id if company else None, + created_at=datetime.now() + ) + db.add(user) + db.flush() + print(f"✓ {name}: utworzono konto (ID {user.id}) - {company_name}") + user_id = user.id + + # Zapisz na wydarzenie + existing_attendee = db.query(EventAttendee).filter( + EventAttendee.event_id == event_id, + EventAttendee.user_id == user_id + ).first() + + if existing_attendee: + print(f" → już zapisany na wydarzenie") + else: + attendee = EventAttendee( + event_id=event_id, + user_id=user_id, + registered_at=datetime.now() + ) + db.add(attendee) + print(f" → zapisano na wydarzenie 28") + + db.commit() + + # Podsumowanie + print() + print("=" * 60) + attendees = db.query(EventAttendee).filter(EventAttendee.event_id == event_id).all() + print(f"Uczestnicy wydarzenia 28 ({len(attendees)} osób):") + for a in attendees: + user = db.query(User).filter(User.id == a.user_id).first() + company = db.query(Company).filter(Company.id == user.company_id).first() if user.company_id else None + company_name = company.name if company else "-" + print(f" • {user.name} ({company_name})") + + except Exception as e: + db.rollback() + print(f"Błąd: {e}") + raise + finally: + db.close() + +if __name__ == "__main__": + main() diff --git a/templates/forum/index.html b/templates/forum/index.html index 027ccb4..93190d5 100755 --- a/templates/forum/index.html +++ b/templates/forum/index.html @@ -162,6 +162,17 @@ border-color: #fcd34d; } + .badge-test { + background: #f3f4f6; + color: #6b7280; + border-color: #d1d5db; + } + + .topic-card.test-topic { + opacity: 0.6; + border-left: 4px solid #9ca3af; + } + /* Status badges */ .badge-status { font-size: var(--font-size-xs); @@ -349,7 +360,7 @@ {% if topics %}
{% for topic in topics %} -