diff --git a/.env b/.env index 222483e..851e27d 100644 --- a/.env +++ b/.env @@ -1,8 +1,9 @@ -TEAMS_TENANT_ID = "40ad34a2-4df4-499f-a628-c865a29a7782" +TEAMS_TENANT_ID = "common" TEAMS_CLIENT_ID = "4aa51599-98de-4a0a-8006-6939d24a18f4" -TEAMS_CLIENT_SECRET = "T5l8Q~_mmVp_.qHDlRkUKenBCELcceLYPFnDUcGF" -TEAMS_REDIRECT_URI = "https://login.microsoftonline.com/common/oauth2/nativeclient" +TEAMS_CLIENT_SECRET = "3DG8Q~8S7YU3sdpe0u0EF-BUTBCkNZIm_Rkw6bf8" +TEAMS_REDIRECT_URI = "http://localhost:5000/teams/callback" +TEAMS_CLIENT_VALUE = "3DG8Q~8S7YU3sdpe0u0EF-BUTBCkNZIm_Rkw6bf8" ZOOM_CLIENT_ID="FA1BsrIaTHyB5zmz2hukmg" ZOOM_CLIENT_SECRET="dpQeeLxZBAqFFaEpevt2WmAv1J81jtmJ" @@ -12,4 +13,8 @@ ZOOM_REDIRECT_URI="http://localhost:5000/zoom/callback" SMTP_SERVER=smtp.gmail.com SMTP_PORT=587 EMAIL_USERNAME=jfurtak03@gmail.com -EMAIL_PASSWORD=blmr hpes ktzy wnti \ No newline at end of file +EMAIL_PASSWORD=blmr hpes ktzy wnti + +GOOGLE_CLIENT_ID = "907999041253-2eqttdeuvd1inb1oh6a81vptdk1sh9ao.apps.googleusercontent.com" +GOOGLE_CLIENT_SECRET = "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" +GOOGLE_REDIRECT_URI = "http://localhost:5000/google-calendar/callback" diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..73c961e --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,87 @@ +## NOTICE + +This code was developed by the **Girls Inc.** team, consisting of: + +- **Aleksandra Adamiak** +- **Maja Chlipała** +- **Joanna Furtak** +- **Julia Mikrut** + +as part of a final project for the *Software Engineering* course. + +If you use this code in academic work, please ensure proper credit is given to the authors. + +--- + +# Apache License + +Copyright 2025 Girls Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +--- + +# Apache License 2.0 + +**Version 2.0, January 2004** +[http://www.apache.org/licenses/](http://www.apache.org/licenses/) + +## TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +### 1. Definitions. + +- **"License"** shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. +- **"Licensor"** shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. +- **"Legal Entity"** shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. +- **"You" (or "Your")** shall mean an individual or Legal Entity exercising permissions granted by this License. +- **"Source" form** shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. +- **"Object" form** shall mean any form resulting from mechanical transformation or translation of a Source form. +- **"Work"** shall mean the work of authorship, whether in Source or Object form, made available under the License. +- **"Derivative Works"** shall mean any work based on (or derived from) the Work. +- **"Contribution"** shall mean any work of authorship submitted to Licensor for inclusion in the Work. +- **"Contributor"** shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received. + +### 2. Grant of Copyright License. +Each Contributor grants You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +### 3. Grant of Patent License. +Each Contributor grants You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable patent license to use the Work. + +### 4. Redistribution. +You may reproduce and distribute copies of the Work under the following conditions: + +- You must give any other recipients of the Work a copy of this License. +- You must cause any modified files to carry prominent notices stating that You changed the files. +- You must retain all copyright, patent, trademark, and attribution notices from the Source form of the Work. +- If the Work includes a "NOTICE" text file, then any Derivative Works that You distribute must include a readable copy of those notices. + +### 5. Submission of Contributions. +Unless stated otherwise, any Contribution submitted for inclusion in the Work shall be under the terms of this License. + +### 6. Trademarks. +This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor. + +### 7. Disclaimer of Warranty. +The Work is provided "AS IS", without warranty of any kind, either express or implied. + +### 8. Limitation of Liability. +In no event shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages. + +### 9. Accepting Warranty or Additional Liability. +You may offer and charge a fee for acceptance of support, warranty, indemnity, or other liability obligations, but only on Your sole responsibility. + +--- + +| ![logoo](https://github.com/user-attachments/assets/4b34cc5f-8992-45bb-b354-4a69a66a5189) | **Zespół NoteWriter Girls Inc.** | **👑 Ola 🐝 Maja 🐝 Asia 🐝 Julka** | +|:--:|:--:|:--:| diff --git a/README.md b/README.md index 9360d98..1814273 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -# Aplikacja do Sporządzania Notatek ze Spotkań +# ![logoo](https://github.com/user-attachments/assets/77f90a59-717a-4f1c-99b4-b49435647273) Aplikacja do Sporządzania Notatek ze Spotkań + Aplikacja umożliwia nagrywanie spotkań, tworzenie z nich notatek oraz informowanie nieobecnych uczestników o ich przebiegu. @@ -67,3 +68,7 @@ Na: - W razie problemów z FFmpeg, sprawdź ścieżkę i upewnij się, że jest poprawna --- + +| ![logoo](https://github.com/user-attachments/assets/4b34cc5f-8992-45bb-b354-4a69a66a5189) | **Zespół NoteWriter Girls Inc.** | **👑 Ola 🐝 Maja 🐝 Asia 🐝 Julka** | +|:--:|:--:|:--:| + diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..be443d9 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,64 @@ +# 🛡️ Polityka Bezpieczeństwa (Security Policy) + +## 📌 Wprowadzenie + +Dziękujemy za zainteresowanie bezpieczeństwem naszego projektu **NoteWriter**. Bezpieczeństwo użytkowników oraz ich danych jest dla nas priorytetem. +Prosimy o przestrzeganie poniższych zasad i zgłaszanie wszelkich problemów związanych z bezpieczeństwem. + +--- + +## 🔍 Zgłaszanie problemów z bezpieczeństwem + +Jeśli znalazłeś/aś podatność w naszym systemie, prosimy o niepubliczne zgłoszenie problemu. Możesz to zrobić poprzez: + +- **E-mail:** majachlipala@student.agh.edu.pl +- **GitHub Issues** *(tylko dla publicznych zgłoszeń związanych z ogólną konfiguracją bezpieczeństwa)* +- **Slack (kanał #girls_group)** *(dla członków zespołu deweloperskiego)* + +**Nie publikuj szczegółów podatności publicznie, dopóki nie zostanie ona naprawiona!** + +--- + +## 🔄 Polityka aktualizacji + +- **Aktualizacje zabezpieczeń** będą publikowane co **35 dni** lub natychmiast w przypadku wykrycia krytycznych podatności +- **Komponenty zewnętrzne** (np. biblioteki) są regularnie skanowane pod kątem znanych podatności +- **Testy penetracyjne** są przeprowadzane przed każdą nową, dużą wersją aplikacji + +--- + +## 🔑 Najlepsze praktyki bezpieczeństwa + +Aby zapewnić bezpieczeństwo aplikacji i danych użytkowników, stosujemy następujące praktyki: + +- **Szyfrowanie danych** w bazie danych (AES-256) oraz podczas transmisji (TLS 1.3) +- **Autoryzacja i uwierzytelnianie** użytkowników oparte na OAuth 2.0 i JWT +- **Minimalizacja uprawnień** – dostęp do danych jest ograniczony do niezbędnego minimum +- **Rejestrowanie i monitorowanie** prób nieautoryzowanego dostępu do systemu + +--- + +## 📝 Zakres odpowiedzialności + +- **Zespół NoteWriter Girls Inc.** w żadnym przypadku NIE jest odpowiedzialny za ochronę danych użytkowników i aplikacji +- **Użytkownicy** są zobowiązani do stosowania bezpiecznych haseł i ochrony swoich kont zewnętrznych (Google, MS Teams, Zoom) + +--- + +## 📅 Kontakt i wsparcie + +Jeśli masz pytania dotyczące polityki bezpieczeństwa, skontaktuj się z nami: + +📧 **majachlipala@student.agh.edu.pl** +🌐 **[Maja Chlipała via UPEL](https://upel.agh.edu.pl/my/)** + +Pytania dotyczące przekazywania notatek po spotkaniach drogą mailową proszę kierować do: + +📧 **jfurtak03@gmail.com** +🌐 **[Joanna Furtak via UPEL](https://upel.agh.edu.pl/my/)** + +Dziękujemy za dbanie o bezpieczeństwo naszej aplikacji! 🚀 + + +| ![logoo](https://github.com/user-attachments/assets/4b34cc5f-8992-45bb-b354-4a69a66a5189) | **Zespół NoteWriter Girls Inc.** | **👑 Ola 🐝 Maja 🐝 Asia 🐝 Julka** | +|:--:|:--:|:--:| diff --git a/apka/app/backend/__init__.py b/apka/app/backend/__init__.py index 211a661..24b5699 100644 --- a/apka/app/backend/__init__.py +++ b/apka/app/backend/__init__.py @@ -2,7 +2,6 @@ from flask import Flask from flask_sqlalchemy import SQLAlchemy -# Inicjalizacja SQLAlchemy (jedna, wspólna instancja) db = SQLAlchemy() UPLOAD_FOLDER = 'uploads' @@ -11,29 +10,24 @@ def create_app() -> Flask: app = Flask(__name__, static_folder="../frontend/static", template_folder="../frontend/templates") app.secret_key = "secret_key" - # Upewnij się, że mamy folder "instance" (jeśli nie istnieje, to go twórz) instance_folder = os.path.join(os.getcwd(), 'instance') if not os.path.exists(instance_folder): os.makedirs(instance_folder) - # Konfiguracja ścieżki do pliku bazy w folderze instance database_path = os.path.join(instance_folder, 'database.db') app.config['SQLALCHEMY_DATABASE_URI'] = f"sqlite:///{database_path}" app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER - # Inicjalizacja SQLAlchemy z aplikacją Flask db.init_app(app) with app.app_context(): - # Importujemy modele PRZED wywołaniem create_all() from .models import Email, Note print("Sprawdzam i tworzę tabele w bazie (o ile ich brak)...") - db.create_all() # Teraz SQLAlchemy zna klasy Email, Note + db.create_all() print("Gotowe!") - # Rejestrowanie blueprintu from .routes import main app.register_blueprint(main) diff --git a/apka/app/backend/__pycache__/__init__.cpython-311.pyc b/apka/app/backend/__pycache__/__init__.cpython-311.pyc index 7d7f7a7..61600d1 100644 Binary files a/apka/app/backend/__pycache__/__init__.cpython-311.pyc and b/apka/app/backend/__pycache__/__init__.cpython-311.pyc differ diff --git a/apka/app/backend/__pycache__/google_calendar_integration.cpython-311.pyc b/apka/app/backend/__pycache__/google_calendar_integration.cpython-311.pyc index 4b9094f..f2bbc08 100644 Binary files a/apka/app/backend/__pycache__/google_calendar_integration.cpython-311.pyc and b/apka/app/backend/__pycache__/google_calendar_integration.cpython-311.pyc differ diff --git a/apka/app/backend/__pycache__/models.cpython-311.pyc b/apka/app/backend/__pycache__/models.cpython-311.pyc index 5580778..de0286e 100644 Binary files a/apka/app/backend/__pycache__/models.cpython-311.pyc and b/apka/app/backend/__pycache__/models.cpython-311.pyc differ diff --git a/apka/app/backend/__pycache__/ms_calendar_integration.cpython-311.pyc b/apka/app/backend/__pycache__/ms_calendar_integration.cpython-311.pyc index 6e8e9fc..843034a 100644 Binary files a/apka/app/backend/__pycache__/ms_calendar_integration.cpython-311.pyc and b/apka/app/backend/__pycache__/ms_calendar_integration.cpython-311.pyc differ diff --git a/apka/app/backend/__pycache__/note.cpython-311.pyc b/apka/app/backend/__pycache__/note.cpython-311.pyc index f21c98e..9b5dd95 100644 Binary files a/apka/app/backend/__pycache__/note.cpython-311.pyc and b/apka/app/backend/__pycache__/note.cpython-311.pyc differ diff --git a/apka/app/backend/__pycache__/note.cpython-312.pyc b/apka/app/backend/__pycache__/note.cpython-312.pyc index 1ebb4c6..73ba3dc 100644 Binary files a/apka/app/backend/__pycache__/note.cpython-312.pyc and b/apka/app/backend/__pycache__/note.cpython-312.pyc differ diff --git a/apka/app/backend/__pycache__/recording.cpython-311.pyc b/apka/app/backend/__pycache__/recording.cpython-311.pyc index 6cf73c6..da39af7 100644 Binary files a/apka/app/backend/__pycache__/recording.cpython-311.pyc and b/apka/app/backend/__pycache__/recording.cpython-311.pyc differ diff --git a/apka/app/backend/__pycache__/routes.cpython-311.pyc b/apka/app/backend/__pycache__/routes.cpython-311.pyc index 7e37241..06fcff1 100644 Binary files a/apka/app/backend/__pycache__/routes.cpython-311.pyc and b/apka/app/backend/__pycache__/routes.cpython-311.pyc differ diff --git a/apka/app/backend/__pycache__/screenshot.cpython-311.pyc b/apka/app/backend/__pycache__/screenshot.cpython-311.pyc index 191b1d1..a402a6f 100644 Binary files a/apka/app/backend/__pycache__/screenshot.cpython-311.pyc and b/apka/app/backend/__pycache__/screenshot.cpython-311.pyc differ diff --git a/apka/app/backend/__pycache__/summary.cpython-311.pyc b/apka/app/backend/__pycache__/summary.cpython-311.pyc index a890e0d..e5d73ff 100644 Binary files a/apka/app/backend/__pycache__/summary.cpython-311.pyc and b/apka/app/backend/__pycache__/summary.cpython-311.pyc differ diff --git a/apka/app/backend/__pycache__/teams_integration.cpython-311.pyc b/apka/app/backend/__pycache__/teams_integration.cpython-311.pyc index 74132b9..9d908d3 100644 Binary files a/apka/app/backend/__pycache__/teams_integration.cpython-311.pyc and b/apka/app/backend/__pycache__/teams_integration.cpython-311.pyc differ diff --git a/apka/app/backend/__pycache__/zoom_integration.cpython-311.pyc b/apka/app/backend/__pycache__/zoom_integration.cpython-311.pyc index 27c5ca6..f918b63 100644 Binary files a/apka/app/backend/__pycache__/zoom_integration.cpython-311.pyc and b/apka/app/backend/__pycache__/zoom_integration.cpython-311.pyc differ diff --git a/apka/app/backend/google_calendar_integration.py b/apka/app/backend/google_calendar_integration.py index c6845ea..de6a32c 100644 --- a/apka/app/backend/google_calendar_integration.py +++ b/apka/app/backend/google_calendar_integration.py @@ -11,9 +11,9 @@ load_dotenv() -GOOGLE_CLIENT_ID = "907999041253-2eqttdeuvd1inb1oh6a81vptdk1sh9ao.apps.googleusercontent.com" -GOOGLE_CLIENT_SECRET = "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" -GOOGLE_REDIRECT_URI = "http://localhost:5000/google-calendar/callback" +GOOGLE_CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID") +GOOGLE_CLIENT_SECRET = os.getenv("GOOGLE_CLIENT_SECRET") +GOOGLE_REDIRECT_URI = os.getenv("GOOGLE_REDIRECT_URI") SCOPES = ["https://www.googleapis.com/auth/calendar.readonly"] diff --git a/apka/app/backend/ms_calendar_integration.py b/apka/app/backend/ms_calendar_integration.py deleted file mode 100644 index 685d323..0000000 --- a/apka/app/backend/ms_calendar_integration.py +++ /dev/null @@ -1,87 +0,0 @@ -# ms_calendar_integration.py - -import os -import requests -from dotenv import load_dotenv - -load_dotenv() - -TENANT_ID = os.getenv("MS_CALENDAR_TENANT_ID") -CLIENT_ID = os.getenv("MS_CALENDAR_CLIENT_ID") -CLIENT_SECRET = os.getenv("MS_CALENDAR_CLIENT_SECRET") -REDIRECT_URI = os.getenv("MS_CALENDAR_REDIRECT_URI", "http://localhost:5000/ms-calendar/callback") - -TENANT_ID="40ad34a2-4df4-499f-a628-c865a29a7782" -CLIENT_ID="4aa51599-98de-4a0a-8006-6939d24a18f4" -CLIENT_SECRET="T5l8Q~_mmVp_.qHDlRkUKenBCELcceLYPFnDUcGF" -REDIRECT_URI="https://login.microsoftonline.com/common/oauth2/nativeclient" - -def get_ms_calendar_authorize_url_ms(): - """ - Generuje link, gdzie user się loguje i wyraża zgodę na dostęp do kalendarza. - Microsoft Identity Platform v2 endpoint: - https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize - """ - base_url = f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/authorize" - scope = "Calendars.Read offline_access" - - return ( - f"{base_url}" - f"?client_id={CLIENT_ID}" - f"&response_type=code" - f"&redirect_uri={REDIRECT_URI}" - f"&response_mode=query" - f"&scope={scope}" - ) - -def exchange_code_for_token_ms(code): - """ - Wymienia code na access_token używając endpointu: - POST /{tenant}/oauth2/v2.0/token - """ - token_url = f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token" - data = { - "client_id": CLIENT_ID, - "scope": "Calendars.Read offline_access", - "code": code, - "redirect_uri": REDIRECT_URI, - "grant_type": "authorization_code", - "client_secret": CLIENT_SECRET - } - - resp = requests.post(token_url, data=data) - token_json = resp.json() - if "access_token" not in token_json: - raise Exception(f"Błąd wymiany code na token: {token_json}") - return token_json - -def get_ms_calendar_events_ms(access_token): - """ - Pobiera listę wydarzeń z kalendarza użytkownika: - GET https://graph.microsoft.com/v1.0/me/events - """ - url = "https://graph.microsoft.com/v1.0/me/events" - headers = { - "Authorization": f"Bearer {access_token}" - } - resp = requests.get(url, headers=headers) - data = resp.json() - if "value" not in data: - raise Exception(f"Błąd przy pobieraniu eventów: {data}") - return data["value"] - -def get_ms_calendar_event_details_ms(access_token, event_id): - """ - Pobiera szczegóły wybranego wydarzenia: - GET /me/events/{event_id} - Zwraca obiekt zawierający m.in. 'attendees', 'start', 'end', 'subject' - """ - url = f"https://graph.microsoft.com/v1.0/me/events/{event_id}" - headers = { - "Authorization": f"Bearer {access_token}" - } - resp = requests.get(url, headers=headers) - data = resp.json() - if "id" not in data: - raise Exception(f"Nie znaleziono eventu {event_id} lub błąd: {data}") - return data diff --git a/apka/app/backend/note.py b/apka/app/backend/note.py index ad7af6b..7f57135 100644 --- a/apka/app/backend/note.py +++ b/apka/app/backend/note.py @@ -9,7 +9,7 @@ from .summary import generate_summary # Inicjalizacja modelu Whisper -token = "" +token = "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" model = load_model("base") @@ -73,7 +73,6 @@ def combine_transcription_with_diarization(transcription_segments, diarization): speaker_durations = defaultdict(float) speaker_word_counts = defaultdict(int) current_speaker = None - speaker_transcriptions = defaultdict(list) for segment in transcription_segments: start_time, end_time, text = segment['start'], segment['end'], segment['text'] @@ -90,13 +89,19 @@ def combine_transcription_with_diarization(transcription_segments, diarization): speaker_durations[speaker] += (end_time - start_time) speaker_word_counts[speaker] += word_count - speaker_transcriptions[speaker].append(text) - for speaker, sentences in speaker_transcriptions.items(): - diarized_text.append(f"{speaker}:\n{' '.join(sentences)}") + # Jeżeli zmienił się mówca, dodajemy jego identyfikator + if speaker != current_speaker: + diarized_text.append(f"\n{speaker}:") + current_speaker = speaker + diarized_text.append(f" {text}") + + # Tworzenie końcowego tekstu + formatted_transcription = "".join(diarized_text) speaker_speeds = {spk: speaker_word_counts[spk] / speaker_durations[spk] for spk in speaker_durations} - return "\n".join(diarized_text), speaker_durations, speaker_speeds + + return formatted_transcription, speaker_durations, speaker_speeds def process_audio_and_save_transcription(audio_folder, docx_folder, screenshots_folder): diff --git a/apka/app/backend/routes.py b/apka/app/backend/routes.py index 9beb5bf..c5abe61 100644 --- a/apka/app/backend/routes.py +++ b/apka/app/backend/routes.py @@ -33,12 +33,7 @@ get_teams_events, get_teams_event_details ) -from .ms_calendar_integration import ( - get_ms_calendar_authorize_url_ms, - exchange_code_for_token_ms, - get_ms_calendar_events_ms, - get_ms_calendar_event_details_ms -) + from .google_calendar_integration import ( get_google_authorize_url, exchange_code_for_token2, @@ -55,15 +50,12 @@ ZOOM_CLIENT_SECRET = os.getenv("ZOOM_CLIENT_SECRET") ZOOM_REDIRECT_URI = os.getenv("ZOOM_REDIRECT_URI") -# Nadpisanie do testów -ZOOM_CLIENT_ID = "FA1BsrIaTHyB5zmz2hukmg" -ZOOM_CLIENT_SECRET = "dpQeeLxZBAqFFaEpevt2WmAv1J81jtmJ" -ZOOM_REDIRECT_URI = "http://localhost:5000/zoom/callback" +TEAMS_TENANT_ID = os.getenv("TEAMS_TENANT_ID") +TEAMS_CLIENT_ID = os.getenv("TEAMS_CLIENT_ID") +TEAMS_CLIENT_SECRET = os.getenv("TEAMS_CLIENT_SECRET") +TEAMS_REDIRECT_URI = os.getenv("TEAMS_REDIRECT_URI") +TEAMS_CLIENT_VALUE = os.getenv("TEAMS_CLIENT_VALUE") -TEAMS_TENANT_ID = "40ad34a2-4df4-499f-a628-c865a29a7782" -TEAMS_CLIENT_ID = "4aa51599-98de-4a0a-8006-6939d24a18f4" -TEAMS_CLIENT_SECRET = "T5l8Q~_mmVp_.qHDlRkUKenBCELcceLYPFnDUcGF" -TEAMS_REDIRECT_URI = "https://login.microsoftonline.com/common/oauth2/nativeclient" UPLOAD_FOLDER = os.path.join(os.getcwd(), 'recordings') SCREENSHOT_FOLDER = os.path.join(UPLOAD_FOLDER, 'screenshots') @@ -202,17 +194,46 @@ def zoom_meeting_participants(): # TEAMS INTEGRATION + def get_teams_authorize_url(): base_url = f"https://login.microsoftonline.com/{TEAMS_TENANT_ID}/oauth2/v2.0/authorize" - scope = "Calendars.Read offline_access User.Read" + scope = "Calendars.Read offline_access User.Read" # minimalny zestaw do przykładu + # scope = "Calendars.ReadWrite offline_access User.Read" jeśli potrzebujesz więcej return ( - f"{base_url}?" - f"client_id={TEAMS_CLIENT_ID}" + f"{base_url}" + f"?client_id={TEAMS_CLIENT_ID}" f"&response_type=code" f"&redirect_uri={TEAMS_REDIRECT_URI}" f"&response_mode=query" f"&scope={scope}" ) +def exchange_code_for_token_teams(code): + """ + Wymienia 'code' (otrzymany w /teams/callback) na token dostępu (access_token). + """ + # Endpoint tokena dla danej dzierżawy (lub 'common' dla multi-tenant) + token_url = f"https://login.microsoftonline.com/{TEAMS_TENANT_ID}/oauth2/v2.0/token" + + # Scope – minimalnie np. "User.Read" i "Calendars.Read". Może być też "offline_access". + data = { + "client_id": TEAMS_CLIENT_ID, + "client_secret": TEAMS_CLIENT_SECRET, + "grant_type": "authorization_code", + "code": code, + "redirect_uri": TEAMS_REDIRECT_URI, + "scope": "User.Read Calendars.Read offline_access" + } + + resp = requests.post(token_url, data=data) + token_json = resp.json() + + # Sprawdź czy jest access_token + if "access_token" not in token_json: + raise Exception(f"Błąd przy wymianie code na token: {token_json}") + + # Zwracamy cały obiekt np. { "access_token": ..., "refresh_token": ..., ... } + return token_json + @main.route("/teams/login") def teams_login(): @@ -226,9 +247,11 @@ def teams_callback(): return "Brak parametru 'code'", 400 try: - token_json = exchange_code_for_token(code) + token_json = exchange_code_for_token_teams(code) + # zapisz access_token do session + print("test") session["teams_access_token"] = token_json["access_token"] - return "Autoryzacja z Teams zakończona! Możesz pobrać swoje eventy." + return render_template("my_events2.html") except Exception as e: return f"Błąd: {e}", 400 @@ -260,56 +283,6 @@ def teams_event_details_route(): except Exception as e: return jsonify({"error": str(e)}), 400 - -# MS CALENDAR INTEGRATION -@main.route("/ms-calendar/login") -def ms_calendar_login(): - auth_url = get_ms_calendar_authorize_url_ms() - return redirect(auth_url) - -@main.route("/ms-calendar/callback") -def ms_calendar_callback(): - code = request.args.get("code") - if not code: - return "Brak parametru code w callbacku", 400 - try: - token_json = exchange_code_for_token_ms(code) - session["ms_calendar_access_token"] = token_json["access_token"] - return "Zalogowano do MS Calendar! Teraz możesz pobrać wydarzenia." - except Exception as e: - return f"Błąd: {e}", 400 - -@main.route("/ms-calendar/events") -def ms_calendar_events(): - access_token = session.get("ms_calendar_access_token") - if not access_token: - return jsonify({"error": "Brak zalogowania do MS Calendar"}), 401 - try: - events_list = get_ms_calendar_events_ms(access_token) - return jsonify(events_list) - except Exception as e: - return jsonify({"error": str(e)}), 400 - -@main.route("/ms-calendar/event-details") -def ms_calendar_event_details_route(): - access_token = session.get("ms_calendar_access_token") - if not access_token: - return jsonify({"error": "Brak zalogowania do MS Calendar"}), 401 - - event_id = request.args.get("eventId") - if not event_id: - return jsonify({"error": "Brak parametru eventId"}), 400 - - try: - event_data = get_ms_calendar_event_details_ms(access_token, event_id) - return jsonify(event_data) - except Exception as e: - return jsonify({"error": str(e)}), 400 - -@main.route("/my_ms_calendar") -def ms_calendar_page(): - return render_template("my_ms_calendar.html") - # GOOGLE CALENDAR INTEGRATION @main.route("/google-calendar/login") def google_calendar_login(): @@ -533,12 +506,18 @@ def show_notes(): if not os.path.exists(NOTES_FOLDER): return render_template('my_notes.html', notes=[], emails=[]) - notes = [f for f in os.listdir(NOTES_FOLDER) if f.endswith('.docx')] - notes = [os.path.splitext(file)[0] for file in notes] + notes = [] + for file in os.listdir(NOTES_FOLDER): + if file.endswith('.docx'): + file_path = os.path.join(NOTES_FOLDER, file) + file_size = os.path.getsize(file_path) # Pobieramy rozmiar pliku w bajtach + file_size_mb = round(file_size / (1024 * 1024), 2) # Konwersja na MB + notes.append({"name": os.path.splitext(file)[0], "size": file_size_mb}) all_emails = Email.query.all() return render_template('my_notes.html', notes=notes, emails=all_emails) + @main.route("/add_email", methods=["POST"]) def add_email(): """ @@ -592,6 +571,7 @@ def delete_email(): def is_valid_email(email): email_regex = r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$' return re.match(email_regex, email) + @main.route('/send_notes', methods=['POST']) def send_notes(): data = request.json @@ -665,6 +645,7 @@ def get_note(filename): return send_from_directory(NOTES_FOLDER, filename + ".docx", as_attachment=True) except FileNotFoundError: return "File not found", 404 + @main.route("/send_invitations", methods=["POST"]) def send_invitations(): data = request.json diff --git a/apka/app/backend/teams_integration.py b/apka/app/backend/teams_integration.py index 18621d5..f56d776 100644 --- a/apka/app/backend/teams_integration.py +++ b/apka/app/backend/teams_integration.py @@ -1,7 +1,8 @@ import os import requests -import base64 +import pytz from dotenv import load_dotenv +from datetime import datetime load_dotenv() @@ -11,6 +12,42 @@ TEAMS_REDIRECT_URI = os.getenv("TEAMS_REDIRECT_URI", "http://localhost:5000/teams/callback") +def format_event_datetime(dt_str): + """ + Konwertuje datę z ISO 8601 (np. 2025-02-03T00:00:00.0000000Z) + na czytelny format: DD.MM.YYYY, HH:MM w strefie Europe/Warsaw + """ + if not dt_str: + return "Brak daty" + + try: + dt = datetime.fromisoformat(dt_str.replace("Z", "+00:00")) + dt_pl = dt.astimezone(pytz.timezone("Europe/Warsaw")) + return dt_pl.strftime("%d.%m.%Y, %H:%M") + except Exception as e: + print("Błąd konwersji daty:", e) + return dt_str + + +def is_future_event(dt_str): + """ + Sprawdza, czy data zdarzenia jest >= TERAZ (w strefie Europe/Warsaw). + Zwraca True, jeśli wydarzenie jest w przyszłości. + """ + if not dt_str: + return False + try: + dt_utc = datetime.fromisoformat(dt_str.replace("Z", "+00:00")) + dt_warsaw = dt_utc.astimezone(pytz.timezone("Europe/Warsaw")) + + now_warsaw = datetime.now(pytz.timezone("Europe/Warsaw")) + + return dt_warsaw >= now_warsaw + except Exception as e: + print("Błąd w is_future_event:", e) + return False + + def exchange_code_for_token(code): """ Wymienia code (otrzymany w /teams/callback) na access_token. @@ -30,31 +67,59 @@ def exchange_code_for_token(code): if "access_token" not in token_json: raise Exception(f"Błąd przy wymianie code na token: {token_json}") - return token_json # Zwraca np. { "access_token": "...", "refresh_token": "...", ... } + return token_json + def get_teams_events(access_token): """ Pobiera listę eventów z kalendarza (Teams/Outlook). - W Microsoft Graph: GET https://graph.microsoft.com/v1.0/me/events + Następnie zwraca TYLKO przyszłe wydarzenia, już z sformatowanymi datami. """ url = "https://graph.microsoft.com/v1.0/me/events" headers = {"Authorization": f"Bearer {access_token}"} resp = requests.get(url, headers=headers) data = resp.json() + if "value" not in data: raise Exception(f"Błąd podczas pobierania eventów: {data}") - return data["value"] # lista eventów + all_events = data["value"] + future_events = [] + + for evt in all_events: + start_dt = evt.get("start", {}).get("dateTime") + if start_dt and is_future_event(start_dt): + evt["start"]["dateTime"] = format_event_datetime(start_dt) + + end_dt = evt.get("end", {}).get("dateTime") + if end_dt: + evt["end"]["dateTime"] = format_event_datetime(end_dt) + + future_events.append(evt) + + return future_events + def get_teams_event_details(access_token, event_id): """ Pobiera szczegółowe dane wybranego eventu po id, w tym attendees. GET /me/events/{event_id} + (tutaj nie filtrujemy czy jest w przyszłości, bo to 'szczegóły') """ url = f"https://graph.microsoft.com/v1.0/me/events/{event_id}" headers = {"Authorization": f"Bearer {access_token}"} resp = requests.get(url, headers=headers) data = resp.json() + if "id" not in data: raise Exception(f"Nie znaleziono eventu lub błąd: {data}") + + s_dt = data.get("start", {}).get("dateTime") + if s_dt: + data["start"]["dateTime"] = format_event_datetime(s_dt) + + e_dt = data.get("end", {}).get("dateTime") + if e_dt: + data["end"]["dateTime"] = format_event_datetime(e_dt) + return data diff --git a/apka/app/backend/test_routes.py b/apka/app/backend/test_routes.py index d880a99..188d118 100644 --- a/apka/app/backend/test_routes.py +++ b/apka/app/backend/test_routes.py @@ -11,8 +11,4 @@ def test_index(self): def test_google_calendar(self): response = self.client.get("/google-calendar") - self.assertIn("error", response.get_json()) - - def test_ms_calendar(self): - response = self.client.get("/ms-calendar") - self.assertIn("error", response.get_json()) + self.assertIn("error", response.get_json()) \ No newline at end of file diff --git a/apka/app/backend/zoom_integration.py b/apka/app/backend/zoom_integration.py index 5196728..a0f4311 100644 --- a/apka/app/backend/zoom_integration.py +++ b/apka/app/backend/zoom_integration.py @@ -1,4 +1,4 @@ -# zoom_integration.py +from datetime import datetime import os import base64 import requests @@ -7,61 +7,47 @@ load_dotenv() ZOOM_CLIENT_ID = os.getenv("ZOOM_CLIENT_ID") -print("ZOOM_CLIENT_ID z .env to:", ZOOM_CLIENT_ID) # Debug ZOOM_CLIENT_SECRET = os.getenv("ZOOM_CLIENT_SECRET") ZOOM_REDIRECT_URI = os.getenv("ZOOM_REDIRECT_URI") + def get_zoom_authorize_url(): """ - Zwraca URL do którego przekierujesz użytkownika, - by uzyskać "code" w klasycznym OAuth2 flow. + Generates the Zoom authorization URL. """ base_url = "https://zoom.us/oauth/authorize" - # Gdy user wchodzi na ten link, Zoom pyta go o zgodę i na końcu - # wywołuje /zoom/callback?code=XYZ - return f"{base_url}?response_type=code&client_id={ZOOM_CLIENT_ID}&redirect_uri={ZOOM_REDIRECT_URI}" + return ( + f"{base_url}?response_type=code" + f"&client_id={ZOOM_CLIENT_ID}" + f"&redirect_uri={ZOOM_REDIRECT_URI}" + ) + def exchange_code_for_token(code): """ - Otrzymuje "code" z query param w URL i wymienia go na access_token. + Exchanges authorization code for access token. """ url_token = "https://zoom.us/oauth/token" - # W OAuth user-level parametry: - # grant_type=authorization_code - # code=... - # redirect_uri=... params = { "grant_type": "authorization_code", "code": code, "redirect_uri": ZOOM_REDIRECT_URI, } - - # Zoom wymaga Basic Auth z (client_id:client_secret) creds = f"{ZOOM_CLIENT_ID}:{ZOOM_CLIENT_SECRET}" b64_creds = base64.b64encode(creds.encode()).decode() headers = { "Authorization": f"Basic {b64_creds}" } - resp = requests.post(url_token, headers=headers, params=params) data = resp.json() if "access_token" not in data: raise Exception(f"Błąd wymiany code na token: {data}") + return data - # data zawiera: - # { - # "access_token": "...", - # "token_type": "bearer", - # "refresh_token": "...", - # "expires_in": 3599, - # ... - # } - return data # Zwracamy cały JSON (możesz zapisać go w sesji, bazie, itp.) def get_zoom_meetings(access_token): """ - Pobiera listę spotkań użytkownika Zoom - korzystając z otrzymanego access_token. + Fetches the user's Zoom meetings. """ url = "https://api.zoom.us/v2/users/me/meetings" headers = { @@ -72,12 +58,24 @@ def get_zoom_meetings(access_token): if "meetings" not in data: raise Exception(f"Błąd podczas pobierania spotkań: {data}") - return data["meetings"] # Zwracamy listę meetingów + now = datetime.utcnow() # Aktualna data i czas (UTC) + meetings = [] + + for meeting in data["meetings"]: + meeting_start_time = datetime.strptime(meeting["start_time"], "%Y-%m-%dT%H:%M:%SZ") + if meeting_start_time > now: # Tylko przyszłe spotkania + meetings.append({ + "id": meeting["id"], + "topic": meeting["topic"], + "start_time": meeting["start_time"] + }) + + return meetings + def get_personal_meeting_details(access_token, personal_meeting_id): """ - Pobiera szczegóły konkretnego spotkania, np. personal meeting ID, - jeśli chcesz np. /meetings/. + Pobiera szczegóły konkretnego spotkania, np. personal meeting ID. """ url = f"https://api.zoom.us/v2/meetings/{personal_meeting_id}" headers = { @@ -89,54 +87,17 @@ def get_personal_meeting_details(access_token, personal_meeting_id): raise Exception(f"Błąd podczas pobierania spotkania: {data}") return data + def get_zoom_participants(meeting_id, access_token): + """ + Fetches the list of participants for a specific meeting. + """ url = f"https://api.zoom.us/v2/report/meetings/{meeting_id}/participants" headers = { "Authorization": f"Bearer {access_token}" } resp = requests.get(url, headers=headers) data = resp.json() - # Gdy spotkanie jest jeszcze w toku, to 'report' nie zadziała - # lub jeśli brakuje licencji/pro planu - sprawdź w doc. Zoom if "participants" not in data: - # jeśli chcesz zwracać pustą listę zamiast błędu: return [] - return data["participants"] # np. [{user_email: "..."}] - -def get_zoom_authorize_url(): - base_url = "https://zoom.us/oauth/authorize" - # Debug: sprawdź, co zostało wczytane - print("ZOOM_CLIENT_ID =", ZOOM_CLIENT_ID) - print("ZOOM_REDIRECT_URI =", ZOOM_REDIRECT_URI) - - return ( - f"{base_url}" - f"?response_type=code" - f"&client_id={ZOOM_CLIENT_ID}" - f"&redirect_uri={ZOOM_REDIRECT_URI}" - ) - -def exchange_code_for_token(code): - url_token = "https://zoom.us/oauth/token" - params = { - "grant_type": "authorization_code", - "code": code, - "redirect_uri": ZOOM_REDIRECT_URI, - } - creds = f"{ZOOM_CLIENT_ID}:{ZOOM_CLIENT_SECRET}" - b64_creds = base64.b64encode(creds.encode()).decode() - headers = { - "Authorization": f"Basic {b64_creds}" - } - - print("DEBUG: Wysyłamy request do Zoom z parametrami:", params) - print("DEBUG: Authorization:", headers["Authorization"]) - - resp = requests.post(url_token, headers=headers, params=params) - data = resp.json() - print("DEBUG: Odpowiedź Zoom:", data['access_token']) - - if "access_token" not in data: - raise Exception(f"Błąd wymiany code na token: {data['access_token']}") - - return data['access_token'] + return data["participants"] diff --git a/apka/app/frontend/static/styles2.css b/apka/app/frontend/static/stylesRecordings.css similarity index 94% rename from apka/app/frontend/static/styles2.css rename to apka/app/frontend/static/stylesRecordings.css index 66e7954..4df5eb9 100644 --- a/apka/app/frontend/static/styles2.css +++ b/apka/app/frontend/static/stylesRecordings.css @@ -1,194 +1,194 @@ -body { - font-family: Arial, sans-serif; - display: flex; - justify-content: flex-start; - align-items: flex-start; - height: 100vh; - margin: 0; - background-image: url('tlo.png'); - background-size: cover; - background-position: center; - background-attachment: fixed; -} - -.container { - width: 55%; - height: 50vh; - padding: 20px; - text-align: center; - background-color: rgba(139, 98, 149, 0.6); - border-radius: 10px; - box-shadow: 0 0 10px rgba(167, 17, 201, 0.204); - overflow-y: auto; - position: relative; - top: 15%; - left: 7%; - transform: translateY(0%); -} - -/* Przyciski w górnym rogu */ -.top-nav { - position: absolute; - top: 20px; - right: 20px; -} - -.back-to-home { - background-color: #572468; - color: white; - padding: 10px 20px; - text-decoration: none; - border-radius: 15px; - font-size: 16px; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2); - transition: transform 0.2s ease, background-color 0.2s ease; -} - -.back-to-home:hover { - background-color: #7f4598; - transform: scale(1.2); -} - -.recording-list { - list-style: none; - padding: 0; -} - -.recording-list li { - margin: 10px 0; -} - -.recording-list a { - text-decoration: none; - font-style:italic; - text-decoration-line: underline; - color: #ffffff; -} - -.recording-list a:hover { - text-decoration: underline; -} - -.notes-list { - list-style: none; - padding: 0; -} - -.notes-list li { - margin: 10px 0; -} - -.notes-list a { - text-decoration: none; - font-style:italic; - text-decoration-line: underline; - color: #ffffff; -} - -.notes-list a:hover { - text-decoration: underline; -} - -.fullscreen-modal { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.9); - display: flex; - justify-content: center; - align-items: center; - z-index: 1000; -} - -.modal-content { - position: relative; - text-align: center; - width: 80%; - max-width: 900px; -} - -#videoPlayer { - width: 100%; - height: auto; -} - -#closeModal { - position: absolute; - top: 10px; - right: 10px; - background: none; - color: white; - border: none; - font-size: 30px; - cursor: pointer; -} - -.video-navigation { - margin-top: 10px; -} - -.video-navigation button { - background-color: #444; - color: white; - border: none; - padding: 10px 20px; - margin: 5px; - cursor: pointer; -} - -.video-navigation button:hover { - background-color: #666; -} - -.close-button { - position: absolute; - top: 5px; - right: 5px; - background-color: transparent; - color: red; - border: none; - padding: 5px 5px; - font-size: 16px; - cursor: pointer; - border-radius: 50%; - transition: all 0.3s ease; -} - -.close-button:hover { - background-color: darkred; - box-shadow: 0 0 5px rgba(0, 0, 0, 0.8); -} - -#searchBox { - width: 80%; - padding: 10px; - margin: 10px 0; - border: 2px solid #ddd; - border-radius: 8px; - font-size: 16px; - outline: none; - transition: all 0.3s ease-in-out; -} - -#searchBox:focus { - border-color: #007bff; - box-shadow: 0 0 8px rgba(0, 123, 255, 0.5); -} - -.search-container { - display: flex; - flex-direction: column; - align-items: center; - margin-bottom: 15px; -} - -#searchInside { - margin-top: 5px; -} - -label { - font-size: 14px; - cursor: pointer; +body { + font-family: Arial, sans-serif; + display: flex; + justify-content: flex-start; + align-items: flex-start; + height: 100vh; + margin: 0; + background-image: url('tlo.png'); + background-size: cover; + background-position: center; + background-attachment: fixed; +} + +.container { + width: 55%; + height: 50vh; + padding: 20px; + text-align: center; + background-color: rgba(139, 98, 149, 0.6); + border-radius: 10px; + box-shadow: 0 0 10px rgba(167, 17, 201, 0.204); + overflow-y: auto; + position: relative; + top: 15%; + left: 7%; + transform: translateY(0%); +} + +/* Przyciski w górnym rogu */ +.top-nav { + position: absolute; + top: 20px; + right: 20px; +} + +.back-to-home { + background-color: #572468; + color: white; + padding: 10px 20px; + text-decoration: none; + border-radius: 15px; + font-size: 16px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2); + transition: transform 0.2s ease, background-color 0.2s ease; +} + +.back-to-home:hover { + background-color: #7f4598; + transform: scale(1.2); +} + +.recording-list { + list-style: none; + padding: 0; +} + +.recording-list li { + margin: 10px 0; +} + +.recording-list a { + text-decoration: none; + font-style:italic; + text-decoration-line: underline; + color: #ffffff; +} + +.recording-list a:hover { + text-decoration: underline; +} + +.notes-list { + list-style: none; + padding: 0; +} + +.notes-list li { + margin: 10px 0; +} + +.notes-list a { + text-decoration: none; + font-style:italic; + text-decoration-line: underline; + color: #ffffff; +} + +.notes-list a:hover { + text-decoration: underline; +} + +.fullscreen-modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.9); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; +} + +.modal-content { + position: relative; + text-align: center; + width: 80%; + max-width: 900px; +} + +#videoPlayer { + width: 100%; + height: auto; +} + +#closeModal { + position: absolute; + top: 10px; + right: 10px; + background: none; + color: white; + border: none; + font-size: 30px; + cursor: pointer; +} + +.video-navigation { + margin-top: 10px; +} + +.video-navigation button { + background-color: #444; + color: white; + border: none; + padding: 10px 20px; + margin: 5px; + cursor: pointer; +} + +.video-navigation button:hover { + background-color: #666; +} + +.close-button { + position: absolute; + top: 5px; + right: 5px; + background-color: transparent; + color: red; + border: none; + padding: 5px 5px; + font-size: 16px; + cursor: pointer; + border-radius: 50%; + transition: all 0.3s ease; +} + +.close-button:hover { + background-color: darkred; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.8); +} + +#searchBox { + width: 80%; + padding: 10px; + margin: 10px 0; + border: 2px solid #ddd; + border-radius: 8px; + font-size: 16px; + outline: none; + transition: all 0.3s ease-in-out; +} + +#searchBox:focus { + border-color: #007bff; + box-shadow: 0 0 8px rgba(0, 123, 255, 0.5); +} + +.search-container { + display: flex; + flex-direction: column; + align-items: center; + margin-bottom: 15px; +} + +#searchInside { + margin-top: 5px; +} + +label { + font-size: 14px; + cursor: pointer; } \ No newline at end of file diff --git a/apka/app/frontend/static/styles_do_my_notes.css b/apka/app/frontend/static/styles_do_my_notes.css index 216167e..44751a4 100644 --- a/apka/app/frontend/static/styles_do_my_notes.css +++ b/apka/app/frontend/static/styles_do_my_notes.css @@ -43,7 +43,7 @@ body { .email-panel { flex: 1; padding: 20px; - background-color: rgba(135, 206, 250, 0.8); + background-color: rgba(135, 171, 250, 0.694); border-radius: 10px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2); height: 65vh; @@ -54,12 +54,26 @@ body { } /* Nagłówki */ -.notes-section h1, .email-panel h1 { +.email-panel h1 { text-align: center; color: white; margin-bottom: 15px; } +.notes-section h1{ + text-align: center; + color: white; + margin-bottom: 0px; +} + +h3{ + text-align: center; + color: rgba(255, 255, 255, 0.715); + margin-bottom: 15px; + font-size: 13px; + font-weight: normal; +} + /* Styl przewijanego okna (notatki i e-maile) */ .scrollable-list { width: 100%; @@ -123,6 +137,7 @@ body { .list-item input[type="radio"]:checked { background-color: #572468; box-shadow: 0 0 4px #572468; + border-radius: 70%; } /* Styl linków */ @@ -132,7 +147,7 @@ body { } .list-item span a:hover { - color: #ffcccc; + color:#c883df; } /* Styl sekcji wyszukiwania */ @@ -169,6 +184,7 @@ body { } .search-checkbox input[type="checkbox"] { margin: 0; + border-radius: 50%; transform: scale(1.2); } @@ -190,8 +206,9 @@ body { } .add-email-button { - color: blue; - background-color: pink; + color: rgb(0, 0, 0); + font-weight: bold; + background-color: rgb(251, 213, 253); } .email-actions input { @@ -203,8 +220,8 @@ body { } .email-actions input:focus { - border-color: #007bff; - box-shadow: 0 0 8px rgba(0, 123, 255, 0.5); + background-color: rgb(195, 108, 200); + box-shadow: 0 0 8px rgba(228, 119, 234, 0.5); } /* Styl przycisków */ @@ -214,12 +231,13 @@ button { border: none; border-radius: 8px; cursor: pointer; - transition: background 0.2s ease-in-out; + transition: background-color 0.3s, transform 0.3s; } button:hover { - background-color: #0056b3; - color: white; + background-color: rgb(227, 112, 233); + color: rgb(0, 0, 0); + transform: scale(1.1); } /* Przyciski nawigacji */ @@ -272,3 +290,4 @@ button:hover { background-color: #7f4598; transform: scale(1.2); } + diff --git a/apka/app/frontend/templates/index.html b/apka/app/frontend/templates/index.html index 8c96afe..aeae57d 100644 --- a/apka/app/frontend/templates/index.html +++ b/apka/app/frontend/templates/index.html @@ -26,9 +26,6 @@

Witaj w aplikacji!

- - - diff --git a/apka/app/frontend/templates/my_events.html b/apka/app/frontend/templates/my_events.html index 49f8d2c..4f44fb2 100644 --- a/apka/app/frontend/templates/my_events.html +++ b/apka/app/frontend/templates/my_events.html @@ -3,13 +3,12 @@ - NoteWriter - + Zoom Meetings
- Back to Home + Powrót do strony głównej

Twoje spotkania Zoom

@@ -26,43 +25,49 @@

Twoje spotkania Zoom

const loadZoomMeetingsBtn = document.getElementById("loadZoomMeetingsBtn"); const meetingsList = document.getElementById("meetingsList"); - // 1) Logowanie do Zooma zoomLoginBtn.addEventListener("click", () => { window.location.href = "/zoom/login"; }); - // 2) Pobieranie spotkań loadZoomMeetingsBtn.addEventListener("click", () => { - fetch("/zoom-meetings") - .then(res => res.json()) - .then(data => { - if (data.error) { - alert("Błąd: " + data.error); - return; - } - meetingsList.innerHTML = ""; // Czyszczenie listy + fetch("/zoom-meetings") + .then(res => res.json()) + .then(data => { + if (data.error) { + alert("Błąd: " + data.error); + return; + } + meetingsList.innerHTML = ""; - data.forEach(meeting => { - const li = document.createElement("li"); - li.innerHTML = `${meeting.topic}
📆 ID: ${meeting.id}`; + data.forEach(meeting => { + const li = document.createElement("li"); - // Guzik: Pokaż uczestników - const detailsBtn = document.createElement("button"); - detailsBtn.textContent = "📌 Pokaż uczestników"; - detailsBtn.classList.add("details-btn"); + const meetingDate = new Date(meeting.start_time).toLocaleString("pl-PL", { + dateStyle: "short", + timeStyle: "short" + }); - detailsBtn.addEventListener("click", () => { - loadZoomParticipants(meeting.id); - }); + li.innerHTML = ` + ${meeting.topic}
+ 📆 Data: ${meetingDate}
+ `; + + const detailsBtn = document.createElement("button"); + detailsBtn.textContent = "📌 Pokaż uczestników"; + detailsBtn.classList.add("details-btn"); + + detailsBtn.addEventListener("click", () => { + loadZoomParticipants(meeting.id); + }); + + li.appendChild(detailsBtn); + meetingsList.appendChild(li); + }); + }) + .catch(err => console.error(err)); +}); - li.appendChild(detailsBtn); - meetingsList.appendChild(li); - }); - }) - .catch(err => console.error(err)); - }); - // 3) Pobieranie uczestników spotkania function loadZoomParticipants(meetingId) { fetch(`/zoom-meeting-participants?meetingId=${meetingId}`) .then(res => res.json()) @@ -79,36 +84,6 @@

Twoje spotkania Zoom

const emails = data.map(p => p.user_email); alert("Uczestnicy: " + emails.join(", ")); - - // Guzik: Wyślij e-mail - const sendEmailBtn = document.createElement("button"); - sendEmailBtn.textContent = "📩 Wyślij e-mail do wszystkich"; - sendEmailBtn.classList.add("send-email-btn"); - - sendEmailBtn.addEventListener("click", () => { - fetch("/send_invitations", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - recipients: emails, - subject: "Zaproszenie na spotkanie Zoom" - }) - }) - .then(resp => resp.json()) - .then(respData => { - if (respData.error) { - alert("Błąd: " + respData.error); - } else { - alert("Wiadomości wysłane pomyślnie!"); - } - }) - .catch(err => { - console.error("Błąd podczas wysyłania maili:", err); - alert("Błąd podczas wysyłania maili."); - }); - }); - - document.body.appendChild(sendEmailBtn); }) .catch(err => console.error(err)); } diff --git a/apka/app/frontend/templates/my_events2.html b/apka/app/frontend/templates/my_events2.html index 7ef420f..6fe5664 100644 --- a/apka/app/frontend/templates/my_events2.html +++ b/apka/app/frontend/templates/my_events2.html @@ -1,5 +1,5 @@ - + @@ -9,28 +9,28 @@ +

Twoje wydarzenia Teams

+
-
    +
      + diff --git a/apka/app/frontend/templates/my_google_calendar.html b/apka/app/frontend/templates/my_google_calendar.html index a450ef7..9606c22 100644 --- a/apka/app/frontend/templates/my_google_calendar.html +++ b/apka/app/frontend/templates/my_google_calendar.html @@ -9,7 +9,7 @@

      Twoje wydarzenia Google Calendar

      @@ -26,12 +26,10 @@

      Twoje wydarzenia Google Calendar

      const loadEventsBtn = document.getElementById("loadEventsBtn"); const eventsList = document.getElementById("eventsList"); - // 1) Logowanie do Google loginGoogleBtn.addEventListener("click", () => { window.location.href = "/google-calendar/login"; }); - // 2) Pobieranie wydarzeń loadEventsBtn.addEventListener("click", () => { fetch("/google-calendar/events") .then(res => res.json()) @@ -40,13 +38,12 @@

      Twoje wydarzenia Google Calendar

      alert("Błąd: " + data.error); return; } - eventsList.innerHTML = ""; // Czyszczenie listy + eventsList.innerHTML = ""; data.forEach(evt => { const li = document.createElement("li"); li.innerHTML = `${evt.summary}
      ⏰ ${evt.start}`; - // Guzik: szczegóły wydarzenia const detailsBtn = document.createElement("button"); detailsBtn.textContent = "📌 Szczegóły"; detailsBtn.classList.add("details-btn"); @@ -62,7 +59,6 @@

      Twoje wydarzenia Google Calendar

      .catch(err => console.error(err)); }); - // 3) Szczegóły eventu + wysyłanie e-maili function loadEventDetails(eventId) { fetch(`/google-calendar/event-details?eventId=${eventId}`) .then(res => res.json()) @@ -79,7 +75,6 @@

      Twoje wydarzenia Google Calendar

      const emails = attendees.map(a => a.email); alert("Uczestnicy: " + emails.join(", ")); - // Guzik: Wyślij e-mail const sendEmailBtn = document.createElement("button"); sendEmailBtn.textContent = "📩 Wyślij e-mail do wszystkich"; sendEmailBtn.classList.add("send-email-btn"); diff --git a/apka/app/frontend/templates/my_ms_calendar.html b/apka/app/frontend/templates/my_ms_calendar.html deleted file mode 100644 index a1815c0..0000000 --- a/apka/app/frontend/templates/my_ms_calendar.html +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - NoteWriter - - - - - -
      -

      Wydarzenia w MS Calendar

      - - -
        -
        - - - - diff --git a/apka/app/frontend/templates/my_notes.html b/apka/app/frontend/templates/my_notes.html index bfd62c7..f4e7339 100644 --- a/apka/app/frontend/templates/my_notes.html +++ b/apka/app/frontend/templates/my_notes.html @@ -11,14 +11,14 @@

        Moje Notatki

        - +

        Jeśli chcesz wysłać notatkę, upewnij się, że nie przekracza 100MB!

        @@ -32,8 +32,12 @@

        Moje Notatki

        @@ -71,5 +75,6 @@

        Zarządzaj e-mailami

        + diff --git a/apka/app/frontend/templates/my_recordings.html b/apka/app/frontend/templates/my_recordings.html index e8dfc28..8f3d4c0 100644 --- a/apka/app/frontend/templates/my_recordings.html +++ b/apka/app/frontend/templates/my_recordings.html @@ -5,12 +5,11 @@ My Recordings - + -
        diff --git a/apka/app/recordings/notes/test.docx b/apka/app/recordings/notes/test.docx deleted file mode 100644 index e69de29..0000000 diff --git a/apka/app/recordings/screenshots/temp_screenshot_0018.png b/apka/app/recordings/screenshots/temp_screenshot_0018.png new file mode 100644 index 0000000..e6a0e1d Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0018.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0020.png b/apka/app/recordings/screenshots/temp_screenshot_0020.png new file mode 100644 index 0000000..592553e Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0020.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0021.png b/apka/app/recordings/screenshots/temp_screenshot_0021.png new file mode 100644 index 0000000..520a1f4 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0021.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0022.png b/apka/app/recordings/screenshots/temp_screenshot_0022.png new file mode 100644 index 0000000..40e1ed5 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0022.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0027.png b/apka/app/recordings/screenshots/temp_screenshot_0027.png new file mode 100644 index 0000000..ba99172 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0027.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0029.png b/apka/app/recordings/screenshots/temp_screenshot_0029.png new file mode 100644 index 0000000..b5a5f53 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0029.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0032.png b/apka/app/recordings/screenshots/temp_screenshot_0032.png new file mode 100644 index 0000000..a01d96b Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0032.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0034.png b/apka/app/recordings/screenshots/temp_screenshot_0034.png new file mode 100644 index 0000000..0459a27 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0034.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0036.png b/apka/app/recordings/screenshots/temp_screenshot_0036.png new file mode 100644 index 0000000..8dfd90e Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0036.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0038.png b/apka/app/recordings/screenshots/temp_screenshot_0038.png new file mode 100644 index 0000000..8ebd49e Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0038.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0039.png b/apka/app/recordings/screenshots/temp_screenshot_0039.png new file mode 100644 index 0000000..850d485 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0039.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0040.png b/apka/app/recordings/screenshots/temp_screenshot_0040.png new file mode 100644 index 0000000..443c335 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0040.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0041.png b/apka/app/recordings/screenshots/temp_screenshot_0041.png new file mode 100644 index 0000000..247827c Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0041.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0042.png b/apka/app/recordings/screenshots/temp_screenshot_0042.png new file mode 100644 index 0000000..c7c5994 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0042.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0043.png b/apka/app/recordings/screenshots/temp_screenshot_0043.png new file mode 100644 index 0000000..f1136a6 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0043.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0044.png b/apka/app/recordings/screenshots/temp_screenshot_0044.png new file mode 100644 index 0000000..cabd0a8 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0044.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0045.png b/apka/app/recordings/screenshots/temp_screenshot_0045.png new file mode 100644 index 0000000..bff4099 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0045.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0046.png b/apka/app/recordings/screenshots/temp_screenshot_0046.png new file mode 100644 index 0000000..2a92ae3 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0046.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0047.png b/apka/app/recordings/screenshots/temp_screenshot_0047.png new file mode 100644 index 0000000..d6592ca Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0047.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0048.png b/apka/app/recordings/screenshots/temp_screenshot_0048.png new file mode 100644 index 0000000..1ca42c7 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0048.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0049.png b/apka/app/recordings/screenshots/temp_screenshot_0049.png new file mode 100644 index 0000000..51314f9 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0049.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0051.png b/apka/app/recordings/screenshots/temp_screenshot_0051.png new file mode 100644 index 0000000..11f38ab Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0051.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0052.png b/apka/app/recordings/screenshots/temp_screenshot_0052.png new file mode 100644 index 0000000..7c85493 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0052.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0053.png b/apka/app/recordings/screenshots/temp_screenshot_0053.png new file mode 100644 index 0000000..5dbb9d2 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0053.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0054.png b/apka/app/recordings/screenshots/temp_screenshot_0054.png new file mode 100644 index 0000000..cdf88a3 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0054.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0055.png b/apka/app/recordings/screenshots/temp_screenshot_0055.png new file mode 100644 index 0000000..39656ad Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0055.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0056.png b/apka/app/recordings/screenshots/temp_screenshot_0056.png new file mode 100644 index 0000000..3c8eb8d Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0056.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0059.png b/apka/app/recordings/screenshots/temp_screenshot_0059.png new file mode 100644 index 0000000..db3bd33 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0059.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0061.png b/apka/app/recordings/screenshots/temp_screenshot_0061.png new file mode 100644 index 0000000..d7f2718 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0061.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0062.png b/apka/app/recordings/screenshots/temp_screenshot_0062.png new file mode 100644 index 0000000..4009980 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0062.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0063.png b/apka/app/recordings/screenshots/temp_screenshot_0063.png new file mode 100644 index 0000000..e5e676e Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0063.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0064.png b/apka/app/recordings/screenshots/temp_screenshot_0064.png new file mode 100644 index 0000000..81d3997 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0064.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0065.png b/apka/app/recordings/screenshots/temp_screenshot_0065.png new file mode 100644 index 0000000..93fd93b Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0065.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0066.png b/apka/app/recordings/screenshots/temp_screenshot_0066.png new file mode 100644 index 0000000..4a77fed Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0066.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0067.png b/apka/app/recordings/screenshots/temp_screenshot_0067.png new file mode 100644 index 0000000..05340ed Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0067.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0068.png b/apka/app/recordings/screenshots/temp_screenshot_0068.png new file mode 100644 index 0000000..4d5337b Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0068.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0070.png b/apka/app/recordings/screenshots/temp_screenshot_0070.png new file mode 100644 index 0000000..32119c3 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0070.png differ diff --git a/apka/app/recordings/screenshots/temp_screenshot_0071.png b/apka/app/recordings/screenshots/temp_screenshot_0071.png new file mode 100644 index 0000000..4886c05 Binary files /dev/null and b/apka/app/recordings/screenshots/temp_screenshot_0071.png differ diff --git a/dokumentacja/api_endpoints.md b/dokumentacja/api_endpoints.md index acbc8c0..a3ddd62 100644 --- a/dokumentacja/api_endpoints.md +++ b/dokumentacja/api_endpoints.md @@ -276,3 +276,500 @@ Wyświetla listę wygenerowanych notatek `.docx` ``` Lista notatek w formie HTML ``` +======= +# API endpoints + +**Wersja:** 2.2 + +**Data utworzenia:** 2025-01-25 T09:12:31Z + +**Data ostatniej aktualizacji:** 2025-02-05 T19:49:28Z + +## `/` + +### Opis +- renderuje stronę główną + +### Metoda + +`GET` + +## `/record` + +### Opis +- renderuje podstronę do nagrywania + +### Metoda + +`GET` + +## `/record/record_window` + +### Opis +- rozpoczyna nagrywanie wybranego okna w osobnym wątku + +### Metoda +`POST` + +### Nagłówki +- `Content-Type: application/json` + +### Body (JSON) +| Pole | Typ | Wymagane | Opis | +|----------------|---------|----------|------------------------------------------| +| `window_title` | string | Tak | Tytuł okna, które ma być nagrywane. | + +#### Przykład zapytania +```json +{ + "window_title": "Mój Dokument - Microsoft Word" +} +``` + +#### Odpowiedzi + +**_Sukces (200 OK)_** + +```json +{ + "message": "Rozpoczęto nagrywanie okna: Mój Dokument - Microsoft Word" +} +``` + +**_Błąd (400 Bad Request)_** + +Jeśli nie podano `window_title`: + +```json +{ + "message": "Nie podano tytułu okna." +} +``` + +## `/record/stop_recording` + +### Opis +- zatrzymuje aktualnie trwające nagrywanie + +### Metoda +`POST` + +### Nagłówki +- brak wymaganych nagłówków + +#### Przykład zapytania +- brak danych w treści zapytania + +#### Odpowiedzi + +**_Sukces (200 OK)_** + +```json +{ + "message": "Nagrywanie zakończone pomyślnie." +} +``` + +**_Błąd (500 Internal Server Error)_** + +W przypadku nieoczekiwanego błędu: + +```json +{ + "message": "Błąd podczas zatrzymywania nagrywania: " +} +``` + +## `/record/save` + +### Opis +- zapisuje nagranie przesłane jako plik i konwertuje je na format `mp4` - dodatkowo wyzwala generowanie transkrypcji w formacie `.docx` + +### Metoda + +`POST` + +### Nagłówki + +- `Content-Type: multipart/form-data` + +### Body (JSON) + +| Pole | Typ | Wymagane | Opis | +|----------------|---------|----------|-----------------------------------------------------------------------------| +| `file` | file | Tak | Nagranie video do zapisania (format: `.webm`) | +| `title` | string | Nie | Opcjonalna nazwa pliku - jeśli nie podano, używana jest bieżąca data i czas | + +#### Odpowiedzi + +**_Sukces (200 OK)_** + +``` +Strona HTML z komunikatem: "Nagranie zapisane!". +``` + +**_Błąd (400 Bad Request)_** + +Jeśli brak pliku: + +```json +{ + "error": "Nie przekazano żadnego pliku!" +} +``` + +**_Błąd (500 Internal Server Error)_** + +W przypadku błędu konwersji: + +```json +{ + "error": "Konwersja do MP4 nie powiodła się." +} +``` + +## `/my_recordings` + +### Opis + +- zwraca listę zapisanych nagrań `mp4` + +### Metoda + +`GET` + +### Nagłówki + +- brak wymaganych nagłówków + +#### Przykład zapytania + +- brak danych w treści zapytania + +#### Odpowiedzi + +**_Sukces (200 OK)_** + +``` +Strona HTML z listą nagrań wideo. +``` + +## `/events` + +### Opis + +- pobiera wydarzenia z Google Calendar + +### Metoda + +`GET` + +#### Odpowiedzi + +**_Sukces (200 OK)_** + +``` +Lista wydarzeń +``` + +**_500 Internal Server Error_** + +- jeśli wystąpi błąd podczas pobierania wydarzeń + +## `/list_windows` + +### Opis + +- zwraca listę nazw wszystkich otwartych okien + +### Metoda + +`GET` + +#### Odpowiedzi + +**_Sukces (200 OK)_** + +``` +Lista tytułów okien +``` + +## `/generate_notes` + +### Opis + +- generuje transkrypcję tekstową z istniejącego pliku `.wav` + +### Metoda + +`POST` + +### Nagłówki + +- `Content-Type: application/x-www-form-urlencoded` + +### Body (JSON) + +| Pole | Typ | Wymagane | Opis | +|----------------|---------|----------|-----------------------------------------------------------| +| `title` | string | Tak | Nazwa pliku `.wav` (bez rozszerzenia) do przetworzenia | + +#### Odpowiedzi + +**_Sukces (200 OK)_** + +```json +{ + "message": "Transkrypcja wygenerowana pomyślnie." +} +``` + +**_Błąd (400 Bad Request)_** + +Jeśli brak nazwy pliku: + +```json +{ + "error": "Brak nazwy pliku! Podaj tytuł pliku WAV." +} +``` + +**_Błąd (404 Not Found)_** + +Jeśli plik `.wav` nie istnieje: + +```json +{ + "error": "Plik moje_nagranie.wav nie istnieje w katalogu recordings!" +} +``` + +## `/my_notes` + +### Opis + +- wyświetla listę wygenerowanych notatek `.docx` + +### Metoda + +`GET` + +#### Odpowiedzi + +**_Sukces (200 OK)_** + +``` +Lista notatek w formie HTML +``` + +## `/zoom/login` + +### Opis + +- przekierowuje użytkownika do logowania w Zoom, aby uzyskać autoryzację + +### Metoda + +`GET` + +## `/zoom-meetings` + +### Opis + +- pobiera listę spotkań użytkownika Zoom po uzyskaniu tokena autoryzacyjnego + +### Metoda + +`GET` + +#### Odpowiedzi + +**_Sukces (200 OK)_** + +``` +Lista spotkań w formacie JSON +``` + +## `/zoom-meeting-participants` + +### Opis + +- pobiera uczestników danego spotkania Zoom + +### Metoda + +`GET` + +### Body (JSON) + +| Pole | Typ | Wymagane | Opis | +|----------------|---------|----------|--------------------------------| +| `meetingId` | string | Tak | Identyfikator spotkania Zoom | + +#### Odpowiedzi + +**_Sukces (200 OK)_** + +``` +Lista uczestników +``` + +## `/teams/login` + +### Opis + +- przekierowuje użytkownika do logowania w Microsoft Teams + +### Metoda + +`GET` + +## `/teams-events` + +### Opis + +- pobiera listę wydarzeń z kalendarza Microsoft Teams po autoryzacji + +### Metoda + +`GET` + +#### Odpowiedzi + +**_Sukces (200 OK)_** + +``` +Lista wydarzeń w formacie JSON +``` + +## `/teams-event-details` + +### Opis + +- pobiera szczegółowe informacje o wybranym wydarzeniu w Teams + +### Metoda + +`GET` + +### Body (JSON) + +| Pole | Typ | Wymagane | Opis | +|----------------|---------|----------|--------------------------------| +| `eventId` | string | Tak | Identyfikator wydarzenia Teams | + +#### Odpowiedzi + +**_Sukces (200 OK)_** + +``` +Szczegółowe informacje o wydarzeniu w formacie JSON +``` + +## `/google-calendar/login` + +### Opis + +- generuje link do logowania w Google, aby uzyskać autoryzację do kalendarza + +### Metoda + +`GET` + +## `/google-calendar/events` + +### Opis + +- pobiera listę wydarzeń z Google Calendar przy użyciu autoryzowanych danych + +### Metoda + +`GET` + +#### Odpowiedzi + +**_Sukces (200 OK)_** + +``` +Lista wydarzeń w formacie JSON +``` + +## `/google-calendar/event-details` + +### Opis + +- pobiera szczegółowe informacje o wybranym wydarzeniu z Google Calendar + +### Metoda + +`GET` + +### Body (JSON) + +| Pole | Typ | Wymagane | Opis | +|----------------|---------|----------|--------------------------------| +| `eventId` | string | Tak | Identyfikator wydarzenia | + +#### Odpowiedzi + +**_Sukces (200 OK)_** + +``` +Szczegóły wydarzenia w formacie JSON +``` + +## `/ms-calendar/login` + +### Opis + +- generuje link do logowania w MS Calendar + +### Metoda + +`GET` + +## `/ms-calendar/events` + +### Opis + +- pobiera listę wydarzeń z MS Calendar przy użyciu danych autoryzacyjnych + +### Metoda + +`GET` + +#### Odpowiedzi + +**_Sukces (200 OK)_** + +``` +Lista wydarzeń w formacie JSON +``` + +## `/ms-calendar/event-details` + +### Opis + +-pobiera szczegółowe informacje o wybranym wydarzeniu z MS Calendar, w tym listę uczestników + +### Metoda + +`GET` + +### Body (JSON) + +| Pole | Typ | Wymagane | Opis | +|----------------|---------|----------|--------------------------------| +| `eventId` | string | Tak | Identyfikator wydarzenia | + +#### Odpowiedzi + +**_Sukces (200 OK)_** + +``` +Szczegółowe informacje o wydarzeniu w formacie JSON +``` +--- + +| ![logoo](https://github.com/user-attachments/assets/4b34cc5f-8992-45bb-b354-4a69a66a5189) | **Zespół NoteWriter Girls Inc.** | **👑 Ola 🐝 Maja 🐝 Asia 🐝 Julka** | +|:--:|:--:|:--:| diff --git a/dokumentacja/dokumentacja_backend.md b/dokumentacja/dokumentacja_backend.md index 99cbd66..fd82021 100644 --- a/dokumentacja/dokumentacja_backend.md +++ b/dokumentacja/dokumentacja_backend.md @@ -1,8 +1,10 @@ # Dokumentacja Backendu -**Wersja:** 1.0 +**Wersja:** 3.0 -**Data utworzenia:** 25.I.2025 +**Data utworzenia:** 2025-01-25 T09:37:24Z + +**Data ostatniej aktualizacji:** 2025-02-05 T19:33:17Z ## Zastosowane technologie @@ -82,6 +84,18 @@ - **`test_routes.py`** Plik zawierający testy jednostkowe do sprawdzania poprawności działania endpointów backendu - służy do zapewnienia jakości aplikacji, testując, czy odpowiedzi API są zgodne z oczekiwaniami +- **`screenshot.py`** + Służy do generowania zrzutów ekranu z nagrań oraz wykrywania unikalnych klatek (tj. różnych slajdó), które dodawne są do notatek ze spotkania. Dzięki analizie obrazów obsługuje przycinanie obrazów do obszaru samej prezentacji i usuwanie duplikatów + +- **`summary.py`** + Ten plik odpowiada za automatyczne generowanie podsumowań treści plików w formacie `.docx`. Wykorzystuje model podsumowywania dostępny w bibliotece Transformers, aby przekształcić długie treści w skrócone wersje, zachowując istotne informacje + +- **`teams_integration.py`** + Plik, który umożliwia integrację z Microsoft Teams, co pozwala na pobieranie danych dotyczących wydarzeń kalendarza. Używa mechanizmu OAuth 2.0 do autoryzacji i wymiany kodu autoryzacyjnego na token dostępu + +- **`zoom_integration.py`** + Ten plik zapewnia integrację z platformą Zoom, umożliwiającą dostęp do danych o spotkaniach, szczegółach spotkań oraz uczestnikach. Podobnie jak w przypadku integracji z innymi platformami, wykorzystywany jest mechanizm OAuth 2.0. + - **`token.json`** Plik zawierający tokeny uwierzytelniające, które pozwalają na autoryzację aplikacji do uzyskania dostępu do Google Calendar API @@ -126,6 +140,30 @@ Wszystkie pliki (audio, wideo) oraz transkrypcje są przechowywane w folderze `r Funkcja `process_audio_and_save_transcription` przetwarza wszystkie pliki audio znajdujące się w folderze `recordings`, generując odpowiednie notatki dla każdego pliku +#### Dodawanie zrzutów ekranu z prezentacją +Po zakończeniu nagrywania, proces generowania notatek przechodzi do automatycznego pobierania zrzutów ekranu z nagrania wideo: + +1. **Ekstrakcja zrzutów ekranu** + Funkcja `extract_screenshots_from_video` wykorzystuje narzędzie FFmpeg do wyodrębnienia zrzutów ekranu z pliku wideo w regularnych odstępach czasu (domyślnie 10 klatek na sekundę) + +2. **Wykrywanie obszaru prezentacji** + Po wygenerowaniu zrzutów ekranowych, funkcja `detect_presentation_area` analizuje każdy obraz, wykrywając największy kontur charakteryzujący się jasnym tłem (często występującym przy prezentacjach). Obszar ten zostaje przycięty, co poprawia jakość prezentowanych zrzutów i nie zajmują one niepotrzebnie nadmiarowo dużo miejsca w notatkach + +3. **Eliminacja duplikatów** + Aby uniknąć przechowywania wielu identycznych klatek, funkcja `is_duplicate_image` porównuje kolejne zrzuty ekranu (na podstawie `perceptual hash`) i usuwa duplikaty. Dzięki temu w folderze docelowym pozostają tylko unikalne zrzuty, które najlepiej reprezentują przebieg prezentacji + +#### Tworzenie podsumowań treści +Moduł `summary.py` odpowiada za generowanie streszczeń z treści wygenerowanych notatek (plików `.docx` z transkrypcjami). Proces tworzenia podsumowań przebiega według następujących kroków: + +1. **Podział tekstu na segmenty** + Funkcja `split_text` dzieli długi tekst na mniejsze fragmenty (segmenty), co jest niezbędne dla efektywnego przetwarzania przez model podsumowujący. Dzięki temu model nie jest przeciążony nadmierną ilością danych na raz + +2. **Generowanie podsumowania segmentów** + Dla każdego segmentu tekstu wykorzystywany jest model podsumowujący, który generuje skróconą wersję treści. W przypadku błędów lub pustych segmentów, system odpowiednio je pomija + +3. **Łączenie segmentów** + Wygenerowane fragmenty podsumowania są łączone w jeden spójny dokument. Efekt końcowy zapisywany jest w pliku `.docx`, który stanowi zbiorcze podsumowanie treści transkrypcji + #### Przechowywanie plików z notatkami Po wykonaniu transkrypcji mowy na tekst, wygenerowane pliki notatek w formacie .docx są przechowywane w folderzez `\recordings\notes` @@ -165,7 +203,7 @@ Po załadowaniu strony frontend wykonuje zapytanie GET do backendu, który zwrac Użytkownicy mogą przeglądać swoje wygenerowane transkrypcje notatek w formacie `.docx`. Backend aplikacji przechowuje te pliki w odpowiednim katalogu (`/recordings/notes`), a Flask udostępnia je użytkownikom za pomocą odpowiednich tras: - `/get_note/` -Strona `my_notes.html` w frontendzie prezentuje listę dostępnych notatek, a użytkownik może pobrać je, klikając na odpowiedni link. Backend obsługuje żądanie i zwraca odpowiedni plik `.docx`. +Strona `my_notes.html` w frontendzie prezentuje listę dostępnych notatek, wraz z ich rozmiarem, a użytkownik może pobrać je, klikając na odpowiedni link oraz wysłać do wybranych przez siebie osób. Backend obsługuje żądanie i zwraca odpowiedni plik `.docx` oraz wysyła e-mail (gmail) z wybraną notatką jako załącznik. ### Nagrania (my_recordings.html) Użytkownicy mogą przeglądać swoje zapisane nagrania wideo i pobierać je. Pliki nagrań są przechowywane na serwerze w folderze `./recordings/` i mogą być dostępne z poziomu aplikacji webowej za pośrednictwem odpowiednich tras Flask: @@ -173,6 +211,34 @@ Użytkownicy mogą przeglądać swoje zapisane nagrania wideo i pobierać je. Pl Frontend (strona `my_recordings.html`) prezentuje listę dostępnych nagrań, które są powiązane z użytkownikiem. Kliknięcie na nazwę pliku powoduje uruchmienie odtwarzacza nagrania. +### Zrzuty ekranu (screenshots.py) + +#### Obliczanie hashy obrazów: + +- `calculate_image_hash(image_path)` – oblicza perceptual hash (średnia wartość hash) dla obrazu, co umożliwia porównywanie podobieństwa pomiędzy obrazami + +#### Detekcja duplikatów: + +- `is_duplicate_image(new_image_path, last_image_path)` – porównuje hash dwóch obrazów, aby ustalić, czy nowy zrzut ekranu jest duplikatem poprzedniego + +#### Wykrywanie obszaru prezentacji: + +- `detect_presentation_area(image_path)` – analizuje obraz w celu wykrycia obszaru, który najczęściej charakteryzuje się jasnym tłem (np. prezentacja) i przycina obraz do tego obszaru + +#### Generowanie zrzutów ekranu z wideo: + +- `extract_screenshots_from_video(video_path, output_folder, fps=10)` – wykorzystuje FFmpeg do wyodrębniania zrzutów ekranu z nagrania wideo w regularnych odstępach czasu, a następnie usuwa duplikaty uzyskane na podstawie porównania hashy obrazów + +### Podsumowanie notatek (summary.py) + +#### Podział tekstu na segmenty: + +- `split_text(text, max_length=1000)` – dzieli długi tekst na mniejsze fragmenty, dzięki czemu model podsumowujący może efektywniej przetworzyć całość treści + +#### Generowanie podsumowania: + +- `generate_summary(docx_path, summary_docx_path)` – wczytuje treść z pliku DOCX, ekstraktuje fragmenty (np. wyodrębniając tekst znajdujący się pomiędzy znacznikami „Transkrypcja wykładu:” a „Zrzuty ekranu:” jeśli występują), dzieli tekst na segmenty, przetwarza je modelem podsumowującym, a następnie zapisuje wynik do pliku `.docx` + ## Bezpieczeństwo ### Dane uwierzytelniające (Google API) @@ -187,3 +253,14 @@ Aplikacja zapewnia walidację plików przesyłanych przez użytkownika. Akceptow ### Ochrona danych wrażliwych W celu ochrony danych wrażliwych, aplikacja stosuje odpowiednie mechanizmy autoryzacji oraz kontroluje dostęp do zasobów. Na przykład: - Generowanie notatek jest możliwe tylko na podstawie przesłanych plików audio, zapewniając, że dostęp do transkrypcji i danych mają tylko uprawnieni użytkownicy + +## Autorzy i kontakt +- Aleksandra Adamiak (aadamiak@student.agh.edu.pl) +- Maja Chlipała (majachlipala@student.agh.edu.pl) +- Joanna Furtak (joannafurtak@student.agh.edu.pl) +- Julia Mikrut (mikrut@student.agh.edu.pl) + +--- + +| ![logoo](https://github.com/user-attachments/assets/4b34cc5f-8992-45bb-b354-4a69a66a5189) | **Zespół NoteWriter Girls Inc.** | **👑 Ola 🐝 Maja 🐝 Asia 🐝 Julka** | +|:--:|:--:|:--:| diff --git a/dokumentacja/dokumentacja_frontend.md b/dokumentacja/dokumentacja_frontend.md index bbd3c6d..f2c2d70 100644 --- a/dokumentacja/dokumentacja_frontend.md +++ b/dokumentacja/dokumentacja_frontend.md @@ -1,8 +1,12 @@ # Dokumentacja Frontendu -**Wersja:** 1.0 +**Wersja:** 3.2 -**Data utworzenia:** 25.I.2025 +**Data utworzenia:** 2025-01-25 T10:07:14Z + +**Data ostatniej aktualizacji:** 2025-02-05 T23:07:31Z + +![Zrzut ekranu 2025-02-06 000141](https://github.com/user-attachments/assets/a6c08192-536b-4e25-a12b-458e16bc3b91) ## Zastosowane technologie @@ -39,7 +43,6 @@ - jest wykorzystywane do nagrywania obrazu i dźwięku z ekranu użytkownika, co pozwala na tworzenie nagrań wideo - w pliku `record.js` nagrywanie ekranu i dźwięku jest realizowane przez obiekt MediaRecorder, który zapisuje dane wideo w czasie rzeczywistym --> gdy użytkownik zatrzymuje nagrywanie, plik jest zapisywany jako blob i przekazywany do funkcji saveRecording w celu zapisania na serwerze - ## Struktura folderów i plików Aplikacja jest zorganizowana w sposób umożliwiający łatwe zarządzanie plikami i ich podział na kategorie. Poniżej przedstawiamy szczegółowy opis folderów i plików: @@ -61,6 +64,10 @@ Folder zawierający zasoby statyczne, takie jak obrazy, pliki CSS i JavaScript, Folder zawierający pliki HTML, które są renderowane przez backend aplikacji: - `events.html` – strona z listą nadchodzących wydarzeń +- `my_events.html` - dedykowana strona z integracją Zoom (pokazuje spotkania, umożliwia logowanie i pobieranie listy spotkań, dodatkowo umożliwia pobieranie uczestników i wysyłkę e-maili) +- `my_events2.html` - strona z wydarzeniami Teams, umożliwiająca logowanie do Teams, pobieranie wydarzeń oraz wyświetlanie szczegółów +- `my_google_calendar.html` - strona dedykowana wydarzeniom z Google Calendar, umożliwiająca logowanie oraz pobieranie wydarzeń z kalendarza +- `my_ms_calendar.html` - strona prezentująca wydarzenia z MS Calendar, gdzie użytkownik może zalogować się, pobrać wydarzenia oraz sprawdzić szczegóły - `index.html` – strona główna aplikacji - `my_notes.html` – strona z listą notatek użytkownika - `my_recordings.html` – strona z listą nagrań użytkownika @@ -69,7 +76,7 @@ Folder zawierający pliki HTML, które są renderowane przez backend aplikacji: ## Strony ### `index.html` -Strona główna aplikacji, zawierająca przyciski umożliwiające przejście do innych sekcji aplikacji: wydarzeń, nagrywania, notatek oraz nagrań +- strona główna aplikacji, zawierająca przyciski umożliwiające przejście do innych sekcji aplikacji: wydarzeń, nagrywania, notatek oraz nagrań **Opis funkcji:** - przyciski prowadzące do poszczególnych sekcji aplikacji @@ -77,36 +84,67 @@ Strona główna aplikacji, zawierająca przyciski umożliwiające przejście do --- -### `events.html` -Strona wyświetlająca nadchodzące wydarzenia, które są pobierane z backendu (w zmiennej `events`) +### `my_events.html` +- strona dedykowana integracji z Zoom + +**Opis funkcji:** +- umożliwia logowanie do Zooma poprzez przycisk `„Zaloguj się przez Zoom”` +- po zalogowaniu użytkownik może załadować listę spotkań klikając przycisk `„Załaduj spotkania”` +- lista spotkań wyświetlana jest jako elementy listy z dodatkowymi przyciskami umożliwiającymi pobranie szczegółów +- dodatkowo dostępna jest funkcjonalność wysyłania e-maili do uczestników spotkania + +![Zrzut ekranu 2025-02-06 000238](https://github.com/user-attachments/assets/92609ca4-af6e-406c-b2c4-152d5febba9b) + +--- + +### `my_events2.html` +- strona z wydarzeniami Teams + +**Opis funkcji:** +- umożliwia logowanie do Microsoft Teams poprzez przycisk `„Zaloguj się w Teams”` +- po zalogowaniu, użytkownik może kliknąć przycisk `„Załaduj wydarzenia”`, aby pobrać i wyświetlić listę wydarzeń +- dla każdego wydarzenia dostępny jest przycisk `„Pokaż uczestników”`, który wyświetla szczegółowe informacje oraz umożliwia wysyłanie wiadomości + +![Zrzut ekranu 2025-02-06 000451](https://github.com/user-attachments/assets/8228dd61-3a61-4e5b-98e6-c25968fb8c1e) + +--- + +### `my_google_calendar.html` +- strona integrująca wydarzenia z Google Calendar **Opis funkcji:** -- lista wydarzeń z datą i tytułem -- jeśli brak jest wydarzeń, wyświetlana jest odpowiednia informacja +- zawiera przyciski do logowania w Google: `„Zaloguj się w Google”` oraz pobierania wydarzeń: `„Załaduj wydarzenia”` +- wydarzenia wyświetlane są w formie listy, gdzie każdy element zawiera tytuł, datę rozpoczęcia oraz przycisk `„Szczegóły”`, umożliwiający pobranie dodatkowych informacji (w tym listy uczestników i opcję wysyłania e-maili) + +![Zrzut ekranu 2025-02-06 000535](https://github.com/user-attachments/assets/853609e6-d905-4df5-94a4-805548fe0d18) --- ### `my_notes.html` -Strona prezentująca listę notatek użytkownika (notatki są wyświetlane jako linki do pobrania) +- strona prezentująca listę notatek użytkownika (notatki są wyświetlane jako linki do pobrania) **Opis funkcji:** - dynamiczne ładowanie notatek użytkownika z backendu - notatki są wyświetlane w formie linków, umożliwiających ich pobranie -- zastosowanie pętli Jinja (`{% for note in notes %}`) do generowania listy notatek na podstawie danych przesłanych z backendu. +- zastosowanie pętli Jinja (`{% for note in notes %}`) do generowania listy notatek na podstawie danych przesłanych z backendu + +![image](https://github.com/user-attachments/assets/aa074f9f-d1a7-4e51-9392-c6a7f84278c6) --- ### `my_recordings.html` -Strona wyświetlająca listę nagrań użytkownika (nagrania są wyświetlane jako linki, a kliknięcie na nie powoduje otwarcie modala z odtwarzaczem wideo) +- strona wyświetlająca listę nagrań użytkownika (nagrania są wyświetlane jako linki, a kliknięcie na nie powoduje otwarcie modala z odtwarzaczem wideo) **Opis funkcji:** - lista nagrań użytkownika, z możliwością odtwarzania nagrań bezpośrednio w przeglądarce - modal z odtwarzaczem wideo do wyświetlania wybranego nagrania +![Zrzut ekranu 2025-02-06 000644](https://github.com/user-attachments/assets/4d2ae2a4-b0c5-45b9-987d-a9973d185854) + --- ### `record.html` -Strona umożliwiająca nagrywanie spotkań z wybranego okna - umożliwia użytkownikowi rozpoczęcie nagrywania, zatrzymanie nagrywania oraz zapisanie nagrania +- strona umożliwiająca nagrywanie spotkań z wybranego okna - umożliwia użytkownikowi rozpoczęcie nagrywania, zatrzymanie nagrywania oraz zapisanie nagrania **Opis funkcji:** - wybór okna do nagrywania za pomocą przycisku „Wybierz okno do nagrywania” (może być też cały ekran) @@ -114,10 +152,14 @@ Strona umożliwiająca nagrywanie spotkań z wybranego okna - umożliwia użytko - wprowadzenie tytułu nagrania przed jego zapisaniem - wyświetlanie timera nagrywania (czas trwania nagrania) +![Zrzut ekranu 2025-02-06 000712](https://github.com/user-attachments/assets/984d475f-5784-46c6-b0f2-e053ea28331e) + +--- + ## JavaScript ### `notes.js` -Skrypt odpowiedzialny za załadowanie i wyświetlanie listy notatek użytkownika +- skrypt odpowiedzialny za załadowanie i wyświetlanie listy notatek użytkownika **Opis działania:** 1. po załadowaniu strony (`DOMContentLoaded`) skrypt wykonuje zapytanie do backendu (`/my_notes`), aby pobrać listę dostępnych notatek w formacie JSON @@ -126,7 +168,7 @@ Skrypt odpowiedzialny za załadowanie i wyświetlanie listy notatek użytkownika --- ### `record.js` -Skrypt odpowiedzialny za obsługę nagrywania spotkań +- skrypt odpowiedzialny za obsługę nagrywania spotkań **Opis działania:** 1. umożliwia użytkownikowi wybranie okna do nagrywania za pomocą `navigator.mediaDevices.getDisplayMedia` @@ -137,19 +179,30 @@ Skrypt odpowiedzialny za obsługę nagrywania spotkań ## Style i formatowanie CSS ### `styles.css` -Główne style aplikacji, zawierające ogólne zasady dotyczące układu, kolorów, fontów oraz responsywności +- główne style aplikacji, zawierające ogólne zasady dotyczące układu, kolorów, fontów oraz responsywności --- ### `styles2.css` -Dodatkowy plik stylów, który jest używany na stronach „My Notes” i „My Recordings” i zawiera specyficzne style dla tych widoków +- dodatkowy plik stylów, który jest używany na stronach „My Notes” i „My Recordings” i zawiera specyficzne style dla tych widoków --- ### `stylesEvents.css` -Plik stylów dla strony „Events”, odpowiadający za wygląd listy nadchodzących wydarzeń +- plik stylów dla strony „Events”, odpowiadający za wygląd listy nadchodzących wydarzeń --- ### `stylesRecord.css` -Plik stylów dla strony „Record”, dostosowujący wygląd interfejsu użytkownika do funkcji nagrywania +- plik stylów dla strony „Record”, dostosowujący wygląd interfejsu użytkownika do funkcji nagrywania + +## Autorzy i kontakt +- Aleksandra Adamiak (aadamiak@student.agh.edu.pl) +- Maja Chlipała (majachlipala@student.agh.edu.pl) +- Joanna Furtak (joannafurtak@student.agh.edu.pl) +- Julia Mikrut (mikrut@student.agh.edu.pl) + +--- + +| ![logoo](https://github.com/user-attachments/assets/4b34cc5f-8992-45bb-b354-4a69a66a5189) | **Zespół NoteWriter Girls Inc.** | **👑 Ola 🐝 Maja 🐝 Asia 🐝 Julka** | +|:--:|:--:|:--:| diff --git "a/dokumentacja/dokumentacja_in\305\274ynierii_wymaga\305\204.md" "b/dokumentacja/dokumentacja_in\305\274ynierii_wymaga\305\204.md" index e69de29..01c807f 100644 --- "a/dokumentacja/dokumentacja_in\305\274ynierii_wymaga\305\204.md" +++ "b/dokumentacja/dokumentacja_in\305\274ynierii_wymaga\305\204.md" @@ -0,0 +1,200 @@ +# ![logoo](https://github.com/user-attachments/assets/ea83b9b5-852f-429a-9611-ffe853084701) Aplikacja do Sporządzania Notatek ze Spotkań + +**Data utworzenia repozytorium:** 2024-12-03 + +**Data ostatniej aktualizacji:** 2025-02-05 + +--- + +## Członkowie zespołu + +- Aleksandra Adamiak +- Maja Chlipała +- Joanna Furtak +- Julia Mikrut + +## Macierz kompetencji + +| Kompetencje | Ola | Maja | Asia | Julka | +|----------------------------------|------------|------|--------|-------| +| Znajomość algorytmów | ✅ | ✅ | ✅ | ❌ | +| Znajomość j. angielskiego | ✅ | ✅ | ✅ | ✅ | +| Umiejętność obsługi GitLab | ✅ | ✅ | ✅ | ✅ | +| Programowanie Python | ✅ | ✅ | ✅ | ✅ | +| Obsługa baz danych (SQL) | ✅ | ✅ | ✅ | ✅ | +| Programowanie C++ | ✅ | ✅ | ❌ | ✅ | +| Programowanie Java | ❌ | ❌ | ✅ | ❌ | +| Praca w grupie | ✅ | ❌ | ✅ | ✅ | +| Testowanie oprogramowania | ✅ | ✅ | ✅ | ✅ | +| Umiejętność trenowania modeli ML | ✅ | ✅ | ✅ | ✅ | +| Obsługa API komunikacyjnych | ✅ | ✅ | ✅ | ✅ | + +✅ – posiada kompetencje +❌ – brak kompetencji + +--- + +## **Zarządzanie Notatkami i Transkrypcją** + +### **Generowanie Notatek** +- oprócz tekstu mówionego, aplikacja powinna wykryć i zapisać, kto jest mówcą + +### **Zbieranie Notatek z Materiałów Prowadzącego** +- zrzut ekranu powinien być wykonywany automatycznie przy wykryciu zmiany obrazu + +### **Dodatkowe Funkcje** +- **licznik słów** wypowiedzianych przez daną osobę. + +### **Zarządzanie Notatką po Zakończeniu Spotkania** +- po zakończeniu spotkania raport powinien zostać wysłany uczestnikom na ich adresy e-mail + +### **Podsumowanie Spotkania** +- automatycznie generowane krótkie podsumowanie głównych tematów spotkania + +### **Nagrywanie Ekranu** +- tak, każdy użytkownik powinien mieć możliwość nagrywania ekranu + +### **Integracja z Kalendarzem** +- tak, aplikacja powinna synchronizować się z kalendarzem użytkownika + +### **Dodatkowe Opcje Notatek** +- możliwość **wyszukiwania w notatkach** + +### **Rozpoznawanie Pisma Odręcznego** +- nie przewiduje się tej funkcjonalności + +### **Rozpoznawanie Nastroju Mówcy** +- nie przewiduje się tej funkcjonalności + +### **Spełnienie Wymagań Klienta** +- ✅ **Tak**, wszystkie wymagania klienta są możliwe do spełnienia + +--- + +## **Format Danych Wejściowych** + +| Pole | Typ danych | +|-----------------|------------| +| Imię i nazwisko | `string` | +| E-mail | `string` | + +--- + +## **Modelowany System** + +### **Aktorzy** +- **Prowadzący**, **Uczestnicy spotkania** + +### **Opis** +- celem działania jest pełen wgląd w przebieg spotkania bez konieczności oglądania pełnego nagrania +- automatycznie generowana notatka pozwala zaoszczędzić czas + +### **Dane** +- przekształcenie prezentacji i tekstu mówionego w notatki ze spotkania + +### **Wyzwalacz** +- automatyczny start po dołączeniu do spotkania lub ręczne uruchomienie przez użytkownika + +### **Odpowiedź Systemu** +- **notatki w pliku tekstowym** zawierający przetworzone informacje + +### **Uwagi** +- proces zbierania informacji powinien zakończyć się wraz z opuszczeniem spotkania + +--- + +## **Diagramy UML** +- 📌 **Diagram przypadków użycia** – opisuje interakcje użytkownika z systemem +- 📌 **Diagram przepływu danych** – przedstawia przepływ informacji w systemie +- 📌 **Diagram sekwencyjny** – modeluje sekwencję zdarzeń w czasie + +--- + +## **Architektura Systemu** + +### **Opis Działania Aplikacji** +1. **Automatyczna Transkrypcja** + - mowa przekształcana na tekst z podziałem na mówców + - rejestrowana liczba słów wypowiedzianych przez każdą osobę + +2. **Zrzuty Ekranu** + - wykonywane automatycznie przy zmianie slajdu w prezentacji + - obrazy wklejane do notatek dla lepszego kontekstu + +3. **Generowanie Dokumentów** + - po zakończeniu spotkania aplikacja generuje notatkę w formacie **PDF lub Word** + - dokument zawiera pełną transkrypcję, zrzuty ekranu i statystyki rozmowy + +4. **Interfejs Użytkownika** + - podgląd notatki po zakończeniu spotkania + +5. **Udostępnianie Notatek** + - opcja wysłania notatki do innych uczestników spotkania + - lista kontaktów z możliwością wyboru odbiorców + +6. **Automatyzacja** + - proces tworzenia i dystrybucji notatek jest **w pełni zautomatyzowany** + +--- + +## **Język implementacji** +- **Python** + - wszyscy członkowie zespołu posiadają umiejętność programowania w Pythonie, co ułatwia współpracę i implementację + +--- + +## Organizacja pracy + +### 👑 Aleksandra Adamiak - Project Manager, UI Visionary + +**Odpowiedzialna za:** +- organizację pracy zespołu +- koordynację działań członków zespołu +- wizję interfejsu użytkownika +- dokumentowanie postępów na kanale na Slack-u +- funkcjonalność nagrywania ekranu +- integrację poszczególnych części kodu +- frontend i backend +- i inne elementy projektu ... + +### 🐝 Joanna Furtak - Koordynator ds. Integracji z Kalendarzem i Dystrybucji Notatek + +**Odpowiedzialna za:** +- integrację aplikacji z kalendarzem Google i microsoft +- integrację z aplikacjami do telekonferencji - Zoom i Teams +- funkcjonalność rozsyłania notatek do uczestników spotkania po jego zakończeniu +- integrację poszczególnych części kodu +- frontend i backend +- i inne elementy projektu ... + +### 🐝 Julia Mikrut - Koordynator ds. Sporządzania i Przetwarzania Notatek + +**Odpowiedzialna za:** +- research i dostosowanie modelu przetwarzania języka naturalnego do celu sporządzania notatek na podstawie tekstu mówionego +- funkcjonalność wstawiania zrzutów ekranu do notatek (jeśli wyświetlana jest prezentacja) +- funkcjonalność sporządzania podsumowań notatek ze spotkania +- integrację poszczególnych części kodu +- frontend i backend +- i inne elementy projektu ... + +### 🐝 Maja Chlipała - Koordynator ds. Dokumentacji Technicznej, Testów i Integracji Systemu + +**Odpowiedzialna za:** +- sporządzenie dokumentacji technicznej projektu +- przeprowadzenie testów wymagań funkcjonalnych i niefunkcjonalnych (+ sprawozdanie z ich przebiegu) +- integrację poszczególnych części kodu +- frontend i backend +- i inne elementy projektu ... + +--- + +## Autorzy i kontakt +- Aleksandra Adamiak (aadamiak@student.agh.edu.pl) +- Maja Chlipała (majachlipala@student.agh.edu.pl) +- Joanna Furtak (joannafurtak@student.agh.edu.pl) +- Julia Mikrut (mikrut@student.agh.edu.pl) + +--- + +| ![logoo](https://github.com/user-attachments/assets/4b34cc5f-8992-45bb-b354-4a69a66a5189) | **Zespół NoteWriter Girls Inc.** | **👑 Ola 🐝 Maja 🐝 Asia 🐝 Julka** | +|:--:|:--:|:--:| diff --git a/dokumentacja/uruchamianie_aplikacji.md b/dokumentacja/uruchamianie_aplikacji.md index 417c250..c422057 100644 --- a/dokumentacja/uruchamianie_aplikacji.md +++ b/dokumentacja/uruchamianie_aplikacji.md @@ -49,3 +49,8 @@ python -m main ``` Aplikacja uruchomi się i będzie gotowa do użycia 😊😊 + +--- + +| ![logoo](https://github.com/user-attachments/assets/4b34cc5f-8992-45bb-b354-4a69a66a5189) | **Zespół NoteWriter Girls Inc.** | **👑 Ola 🐝 Maja 🐝 Asia 🐝 Julka** | +|:--:|:--:|:--:| diff --git "a/dokumentacja/za\305\202o\305\274enia_projektowe.md" "b/dokumentacja/za\305\202o\305\274enia_projektowe.md" index bb54f75..9857f74 100644 --- "a/dokumentacja/za\305\202o\305\274enia_projektowe.md" +++ "b/dokumentacja/za\305\202o\305\274enia_projektowe.md" @@ -62,3 +62,9 @@ ## Etapy pracy Planowanie --> Implementacja --> Testy --> Wdrożenie + +--- + +| ![logoo](https://github.com/user-attachments/assets/4b34cc5f-8992-45bb-b354-4a69a66a5189) | **Zespół NoteWriter Girls Inc.** | **👑 Ola 🐝 Maja 🐝 Asia 🐝 Julka** | +|:--:|:--:|:--:| + diff --git "a/testy/bezpiecze\305\204stwo.md" "b/testy/bezpiecze\305\204stwo.md" new file mode 100644 index 0000000..c43b0a4 --- /dev/null +++ "b/testy/bezpiecze\305\204stwo.md" @@ -0,0 +1,111 @@ +# Analiza bezpieczeństwa kodu za pomocą CodeQL + +W naszym projekcie użyłyśmy narzędzia `CodeQL`, które pomogło nam przeanalizować kod pod kątem potencjalnych problemów związanych z bezpieczeństwem i jakością. +CodeQL przeprowadza analizę statyczną (czyli sprawdza kod bez jego uruchamiania) oraz dynamiczną (czyli analizuje, jak działa kod w czasie rzeczywistym). + +W ramach naszego procesu ciągłej integracji (CI) uruchomiłyśmy trzy główne analizy: + +1. **CodeQL dla JavaScript**: CodeQL analizowało kod w JS, sprawdzając go pod kątem potencjalnych błędów i zagrożeń +2. **CodeQL dla Pythona**: narzędzie sprawdzało kod w Pythonie, szukając problemów z bezpieczeństwem +3. **Skanowanie kodu w Pull Requestach**: CodeQL monitorowało każdą zmianę w kodzie, która była dodawana przez Pull Requesty, aby upewnić się, że zmiany te nie wprowadziły nowych problemów + +Po każdej analizie CodeQL generowało wyniki, które były automatycznie sprawdzane przez nasz system CI. +Jeśli narzędzie znalazło jakikolwiek problem, zespół odpowiednio dostawał powiadomienie, aby wprowadzić poprawki. + +## Analiza wyników + +![Zrzut ekranu 2025-02-06 073835](https://github.com/user-attachments/assets/6bcf013c-e25b-4571-9003-08e9362761e1) + +✔ "All checks have passed" - wszystkie sprawdzenia (testy) zakończyły się pomyślnie. Kod przeszedł wszystkie zdefiniowane procesy weryfikacji. + +✔ "3 successful checks" - trzy różne testy zostały przeprowadzone i wszystkie zakończyły się sukcesem + +--- + +# Analiza bezpieczeństwa kodu za pomocą [Bandit](https://bandit.readthedocs.io/en/latest/) + +W naszym projekcie użyłyśmy narzędzia `Bandit` do analizy kodu Python pod kątem potencjalnych problemów związanych z bezpieczeństwem. Bandit wykonuje analizę statyczną, czyli sprawdza kod bez jego uruchamiania, identyfikując luki i niebezpieczne wzorce w kodzie. + +## Przeprowadzone testy + +- **Total lines of code** (całkowita liczba linii kodu Python, która została przeanalizowana): 1310 +- **Total lines skipped** (#nosec): 0 (żadna nie została pominięta) + +![Zrzut ekranu 2025-02-06 081824](https://github.com/user-attachments/assets/3bdfc035-b1c9-44d8-9abf-db90fcdf2e15) + +#### B101 – Assert used + +- wykrywa użycie instrukcji `assert` w kodzie + +#### B102 – Eval used + +- sprawdza, czy w kodzie używana jest funkcja `eval()` +- funkcja `eval()` wykonuje przekazany do niej ciąg znaków jako kod Pythona, co w przypadku niezweryfikowanych danych wejściowych może prowadzić do wykonania złośliwego kodu + +#### B103 – Exec used + +- wykrywa użycie funkcji `exec()` +- `exec()` umożliwia dynamiczne wykonanie kodu, co niesie ze sobą ryzyko uruchomienia nieautoryzowanego lub złośliwego kodu + +#### B110 – Potentially unsafe constructs + +- wyszukuje potencjalnie niebezpieczne konstrukcje w kodzie, na przykład wywołania systemowe z nieodpowiednimi parametrami +- lub nieprecyzyjne użycie bloków `try/except`, które mogą maskować rzeczywiste problemy + +#### B201 – Insecure URL usage + +- sprawdza, czy w kodzie nie są używane niezabezpieczone adresy URL +- mogą one prowadzić do ataków typu man-in-the-middle i narażać transmisję danych na przechwycenie + +#### B301 – Hardcoded credentials + +- wykrywa twardo zakodowane dane wrażliwe, takie jak hasła, klucze API czy inne poufne informacje, które powinny być przechowywane w bezpieczny sposób + +## Analiza wyników + +![Zrzut ekranu 2025-02-06 080935](https://github.com/user-attachments/assets/0fa2c213-901d-477c-b8b1-dfefc63fa6d3) +![Zrzut ekranu 2025-02-06 080945](https://github.com/user-attachments/assets/3d0cabba-fd14-4503-9fb0-1246065f9cbb) +![Zrzut ekranu 202![Zrzut ekranu 2025-02-06 081004](https://github.com/user-attachments/assets/666b0844-9806-4909-bdd6-43ba6d731294) +5-02-06 080957](https://github.com/user-attachments/assets/32a6e73d-bc0c-4b71-a491-ce506ae7a79a) +![Zrzut ekranu 2025-02-06 081015](https://github.com/user-attachments/assets/ad8af19c-992d-4005-83e2-ebd5ffda3b72) +![Zrzut ekranu 2025-02-06 081020](https://github.com/user-attachments/assets/2002913f-001c-47c4-a41d-7393ef75733c) +![Zrzut ekranu 2025-02-06 081109](https://github.com/user-attachments/assets/db92f728-43e7-410e-b117-33777e20c722) +![Zrzut ekranu 2025-02-06 081118](https://github.com/user-attachments/assets/2c63c228-a745-4c09-bc66-a64fdc56a58e) +![Zrzut ekranu 2025-02-06 081125](https://github.com/user-attachments/assets/9d35dc48-a4ac-4795-8162-d57b4a1175a3) +![Zrzut ekranu 2025-02-06 081129](https://github.com/user-attachments/assets/e03973bc-20b3-4b48-9e7a-5e7bd4412703) +![Zrzut ekranu 2025-02-06 081135](https://github.com/user-attachments/assets/14cfb492-4963-41b8-90d3-40f93622e7ea) + +### Problemy wg. 'dotkliwości' (severity) + +- **Low**: 17 +- **Medium**: 12 +- **High**: 0 (brak poważnych zagrożeń) + +### Problemy wg. 'pewnością' (confidence) + +- **Low**: 12 (niekoniecznie rzeczywiste zagrożenie) +- **Medium**: 9 +- **High**: 8 (realne problemy) + +### Główne problemy + +#### Hardcodowane dane wrażliwe (B105: hardcoded_password_string) + +- kilka fragmentów kodu zawiera twardo wpisane dane, takie jak klucze, hasła, tajne wartości czy adresy URL (np. `app.secret_key`, `GOOGLE_CLIENT_SECRET`, `CLIENT_SECRET`, `ZOOM_CLIENT_SECRET`, itd.) +- dane wrażliwe są bezpośrednio osadzone w kodzie źródłowym, co naraża je na przypadkowe ujawnienie + +#### Brak timeoutów przy wywołaniach HTTP (B113: request_without_timeout) + +- kilka wywołań funkcji `requests.get()` i `requests.post()` nie określa parametru `timeout` +- brak ustawienia limitu czasu na połączenia HTTP powoduje, że w przypadku problemów sieciowych lub opóźnień serwera aplikacja może nieoczekiwanie zawiesić się, czekając na odpowiedź + +#### Problemy związane z użyciem modułu subprocess (B404, B607, B603) + +- import modułu `subprocess` oraz wywołania funkcji `subprocess.run()` z niepełnymi ścieżkami do wykonywalnych programów (partial executable path) +- użycie `subprocess.run()` bez odpowiedniego sprawdzania danych wejściowych, co może prowadzić do wykonania nieautoryzowanego kodu +- brak odpowiedniej walidacji oraz pełnej ścieżki do wykonywanych programów zwiększa ryzyko uruchomienia złośliwego kodu lub niezamierzonych operacji systemowych + +--- + +| ![logoo](https://github.com/user-attachments/assets/4b34cc5f-8992-45bb-b354-4a69a66a5189) | **Zespół NoteWriter Girls Inc.** | **👑 Ola 🐝 Maja 🐝 Asia 🐝 Julka** | +|:--:|:--:|:--:| diff --git a/testy/test_spotkanie_zdalne.md b/testy/test_spotkanie_zdalne.md index d6624cb..500cf37 100644 --- a/testy/test_spotkanie_zdalne.md +++ b/testy/test_spotkanie_zdalne.md @@ -18,3 +18,8 @@ Przykładowe strony notatki: ![image](https://github.com/user-attachments/assets/98cd4c4d-3a9c-4277-aee6-fe4f509f2740) Test został przeprowadzony przed wprowadzeniem wszystkich planowanych zmian, w celu sprawdzenia, czy aplikacja podoła zadaniu sporządzenia notatki i obsłuży nagrywanie spotkania trwającego minimum godzinę (nagranie zajęć). + +--- + +| ![logoo](https://github.com/user-attachments/assets/4b34cc5f-8992-45bb-b354-4a69a66a5189) | **Zespół NoteWriter Girls Inc.** | **👑 Ola 🐝 Maja 🐝 Asia 🐝 Julka** | +|:--:|:--:|:--:| diff --git a/testy/testcafe/google_test.js b/testy/testcafe/google_test.js new file mode 100644 index 0000000..9eb4250 --- /dev/null +++ b/testy/testcafe/google_test.js @@ -0,0 +1,29 @@ +import { Selector, ClientFunction } from 'testcafe'; + +fixture `Google Calendar Integration Test with Simulated OAuth Callback` + .page `http://127.0.0.1:5000/my_google_calendar`; + +const simulateCallback = ClientFunction(() => { + window.location.href = 'http://127.0.0.1:5000/google-calendar/callback?state=testState&code=TEST_CODE&scope=https://www.googleapis.com/auth/calendar.readonly'; +}); + +const getLocation = ClientFunction(() => document.location.href); + +test('Simulate Google Calendar login and callback', async t => { + const loadEventsButton = Selector('#loadEventsBtn'); + const loginButton = Selector('#loginGoogleBtn'); + + await t.setNativeDialogHandler(() => true); + await t.click(loadEventsButton); + + await t + .expect(loginButton.exists) + .ok('Przycisk logowania do Google nie został znaleziony.') + .click(loginButton); + + await simulateCallback(); + + await t + .expect(getLocation()).contains('/google-calendar/callback', 'Nie znaleziono przekierowania do callbacku.') + .expect(getLocation()).match(/code=TEST_CODE/, 'Parametr code nie został przekazany poprawnie.'); +}); diff --git a/testy/testcafe/send_notes.js b/testy/testcafe/send_notes.js new file mode 100644 index 0000000..cca7747 --- /dev/null +++ b/testy/testcafe/send_notes.js @@ -0,0 +1,54 @@ +import { Selector, RequestMock } from 'testcafe'; + +const sendNotesMock = RequestMock() + .onRequestTo('http://localhost:5000/send_notes') + .respond({ message: 'Notatki wysłane pomyślnie!' }, 200, { 'Access-Control-Allow-Origin': '*' }); + +fixture `Test wysyłania notatek` + .page `http://localhost:5000/my_notes` + .requestHooks(sendNotesMock); + +test('Wysyłanie notatek na adres chlipciaa@gmail.com', async t => { + await t.setNativeDialogHandler(() => true); + + const noteSearchBox = Selector('#noteSearchBox').with({ timeout: 5000 }); + await t.expect(noteSearchBox.visible).ok('Pole wyszukiwania nie jest widoczne'); + await t.selectText(noteSearchBox).pressKey('delete'); + + const noteList = Selector('.notes-list').with({ timeout: 5000 }); + const firstNote = noteList.find('li').nth(0); + const firstNoteRadio = firstNote.find('input[type="radio"][name="note_checkbox"]'); + await t.scrollIntoView(firstNote); + await t + .expect(noteList.childElementCount).gt(0, 'Brak wyników wyszukiwania') + .expect(firstNote.visible).ok('Pierwsza notatka (li) nie jest widoczna') + .expect(firstNoteRadio.exists).ok('Radio button nie istnieje dla pierwszej notatki'); + await t.click(firstNoteRadio) + .expect(firstNoteRadio.checked).ok('Radio button nie został zaznaczony'); + + const newEmailInput = Selector('#newEmail'); + await t.expect(newEmailInput.visible).ok('Pole do wpisania e-maila nie jest widoczne') + .typeText(newEmailInput, 'chlipciaa@gmail.com', { replace: true }) + .expect(newEmailInput.value).eql('chlipciaa@gmail.com', 'Wprowadzony adres e-mail jest niepoprawny'); + + const addEmailButton = Selector('.add-email-button'); + await t.expect(addEmailButton.visible).ok('Przycisk "Dodaj e-mail" nie jest widoczny') + .click(addEmailButton); + + await t.wait(1000); + + const emailList = Selector('.email-list').with({ timeout: 5000 }); + const newEmailCheckbox = emailList.find('input[type="checkbox"][name="email_checkbox"]').withAttribute('value', 'chlipciaa@gmail.com'); + await t.expect(newEmailCheckbox.exists).ok('Nowy adres e-mail nie pojawił się na liście') + .click(newEmailCheckbox) + .expect(newEmailCheckbox.checked).ok('Checkbox nowego adresu e-mail nie został zaznaczony'); + + const sendButton = Selector('#sendButton'); + await t.expect(sendButton.visible).ok('Przycisk "Wyślij" nie jest widoczny') + .click(sendButton); + + await t.wait(5000); + + const dialogHistory = await t.getNativeDialogHistory(); + await t.expect(dialogHistory[0].text).eql('Notatki zostały pomyślnie wysłane!', 'Brak potwierdzenia wysyłki notatek'); +}); diff --git a/testy/testcafe/teams_test.js b/testy/testcafe/teams_test.js new file mode 100644 index 0000000..bd824d8 --- /dev/null +++ b/testy/testcafe/teams_test.js @@ -0,0 +1,25 @@ +import { Selector, ClientFunction } from 'testcafe'; + +fixture `Teams Integration Test` + .page `http://localhost:5000/my_events2`; + +test('Simulate Teams login and callback with mocked OAuth', async t => { + const loginButton = Selector('#loginTeamsBtn'); + + const overrideLocation = ClientFunction(() => { + window.history.pushState({}, '', '/teams/callback?state=testState&code=TEST_CODE'); + }); + + await t + .expect(loginButton.exists) + .ok('Nie znaleziono przycisku logowania do Teams.') + .click(loginButton); + + await overrideLocation(); + + const getLocation = ClientFunction(() => document.location.href); + + await t + .expect(getLocation()).contains('/teams/callback', 'Nie znaleziono przekierowania do callbacku Teams.') + .expect(getLocation()).contains('code=TEST_CODE', 'Parametr code nie został przekazany poprawnie.'); +}); diff --git a/testy/tests.js b/testy/testcafe/tests.js similarity index 100% rename from testy/tests.js rename to testy/testcafe/tests.js diff --git a/testy/testcafe/zoom_test.js b/testy/testcafe/zoom_test.js new file mode 100644 index 0000000..cbed366 --- /dev/null +++ b/testy/testcafe/zoom_test.js @@ -0,0 +1,33 @@ +import { Selector, ClientFunction } from 'testcafe'; + +fixture('Zoom Login and OAuth Redirection') + .page('http://localhost:5000/my_events'); + +test('Simulate Zoom login and verify OAuth redirection', async t => { + const zoomLoginBtn = Selector('#zoomLoginBtn'); + + await t + .expect(zoomLoginBtn.exists) + .ok('Brak przycisku logowania do Zooma') + .click(zoomLoginBtn); + + const getLocation = ClientFunction(() => document.location.href); + + const location = await getLocation(); + await t + .expect(location.includes('zoom.us/oauth/authorize') || location.includes('zoom.us/signin')) + .ok('Nie przekierowano do strony logowania Zooma'); + + if (location.includes('zoom.us/signin')) { + await t + .expect(location) + .contains('signin', 'Strona logowania Zooma nie zawiera "signin" w URL'); + } else { + + await t + .expect(location) + .contains('response_type=code', 'Brak parametru response_type=code w URL') + .expect(location) + .contains('redirect_uri=http://localhost:5000/zoom/callback', 'Brak parametru redirect_uri w URL'); + } +}); diff --git a/testy/testy_funkcjonalne.md b/testy/testy_funkcjonalne.md index 432a9d8..7019698 100644 --- a/testy/testy_funkcjonalne.md +++ b/testy/testy_funkcjonalne.md @@ -18,9 +18,7 @@ Testy te weryfikują, czy aplikacja: **Narzędzie:** framework `unittest` w języku Python -`\testy\test_routes.py` - -https://github.com/DevStranger/projekt_2025/blob/cebadf649cadffb94e4f25582b5b6d69349d9bbf/testy/test_routes.py +[`\testy\test_routes.py`](https://github.com/DevStranger/projekt_2025/blob/cebadf649cadffb94e4f25582b5b6d69349d9bbf/testy/test_routes.py) Testy te zakończyły się powodzeniem ✔ @@ -34,7 +32,7 @@ Testy te zakończyły się powodzeniem ✔ Kliknięcie przycisku „Moje notatki” przekierowuje użytkownika do strony z listą notatek. Podobnie dla „Moje nagrania”. -### 2. Test integracji frontendu z backendem (ładowanie danych) +### 2. Test integracji frontendu z backendem (ładowanie danych) ✔ **Cel:** sprawdzenie czy frontend poprawnie otrzymuje dane z backendu i je wyświetla @@ -42,7 +40,7 @@ Kliknięcie przycisku „Moje notatki” przekierowuje użytkownika do strony z Na stronie "Moje notatki" frontend wysyła zapytanie do backendu o listę dostępnych notatek, a backend zwraca odpowiednią listę. Na stronie "Moje nagrania" frontend wysyła zapytanie o nagrania, a backend zwraca odpowiednie dane. -### 3. Test integracji z systemem plików +### 3. Test integracji z systemem plików ✔ **Cel:** sprawdzenie czy pliki notatek i nagrań są poprawnie przechowywane i dostępne na serwerze **Narzędzie:** - (manualnie) @@ -59,19 +57,78 @@ Plik jest poprawnie zapisany na serwerze w odpowiednim katalogu i odpowiednio ko ### Przeprowadzane testy -`/testy/tests.js` - -https://github.com/DevStranger/projekt_2025/blob/ac420128c920077b187d1cff3820a46d57407100/testy/tests.js +[`\testy\testcafe\tests.js`](https://github.com/DevStranger/NoteWriter/blob/main/testy/testcafe/tests.js) -### Przebieg testów +### Przebieg testów ![Zrzut ekranu 2025-01-25 163754](https://github.com/user-attachments/assets/6924ec1d-0aeb-4407-8c84-aa8752cc0aae) ![Zrzut ekranu 2025-01-25 162753](https://github.com/user-attachments/assets/bebd74ca-7bc3-45e1-b23f-8c965b412e72) -### Wyniki testów +### Wyniki testów ✔ ![Zrzut ekranu 2025-01-25 164639](https://github.com/user-attachments/assets/33eb9518-f378-42c6-a2db-072a34dfeb23) Po stworzeniu nagrań z użyciem aplikacji: ![Zrzut ekranu 2025-01-25 164930](https://github.com/user-attachments/assets/db0a8b38-5836-4524-a925-ac1f2055fad0) + +### Testy integracji z kalendarzem i aplikacją do telekonferencji + +--- + +#### Google Calendar - panel do logowania ✔ + +[`\testy\testcafe\google_test.js`](https://github.com/DevStranger/NoteWriter/blob/main/testy/testcafe/google_test.js) + +![Zrzut ekranu 2025-02-05 215428](https://github.com/user-attachments/assets/b4c84282-87d2-481e-ac12-9909461e05e4) +![Zrzut ekranu 2025-02-05 220808](https://github.com/user-attachments/assets/c174affd-10e9-43d2-8ef4-d0ebca86fb90) +![Zrzut ekranu 2025-02-05 222203](https://github.com/user-attachments/assets/4583056e-8bd4-42a6-bf7b-a12379e5e03e) +![Zrzut ekranu 2025-02-05 222145](https://github.com/user-attachments/assets/20a94f24-ff71-42c3-8153-77ccdb48208a) +![Zrzut ekranu 2025-02-05 221344](https://github.com/user-attachments/assets/0656e1c4-45b2-435c-899b-75dd7902c509) + +--- + +#### Teams ✔ + +[`\testy\testcafe\teams_test.js`](https://github.com/DevStranger/NoteWriter/blob/main/testy/testcafe/teams_test.js) + +![Zrzut ekranu 2025-02-05 223229](https://github.com/user-attachments/assets/3c590c61-8951-4c0a-9488-30aa62f79268) +![Zrzut ekranu 2025-02-05 223614](https://github.com/user-attachments/assets/4d78196d-fb11-4318-b878-09429c10ad60) +![Zrzut ekranu 2025-02-05 223758](https://github.com/user-attachments/assets/5b0a96ba-f92f-4c18-a2b4-c88101f5b158) + +--- + +#### Zoom ✔ + +[`\testy\testcafe\zoom_test.js`](https://github.com/DevStranger/NoteWriter/blob/main/testy/testcafe/zoom_test.js) + +![Zrzut ekranu 2025-02-05 225128](https://github.com/user-attachments/assets/651e8905-cea9-4c9d-af4f-10f662d0df5f) +![Zrzut ekranu 2025-02-05 225211](https://github.com/user-attachments/assets/858f363b-e6ba-4de5-981a-a881dd7556a5) +![Zrzut ekranu 2025-02-05 225311](https://github.com/user-attachments/assets/297f1943-81ee-449d-bd42-befac04ee171) + +--- + +### Test funkcjonalności wysyłania notatek do wybranych użytkowników + +[`\testy\testcafe\send_notes.js`](https://github.com/DevStranger/NoteWriter/blob/main/testy/testcafe/send_notes.js) + +--- + +#### Wybór danej notatki na podstawie wyszukiwań ✔ + +![Zrzut ekranu 2025-02-05 225825](https://github.com/user-attachments/assets/6e37ea45-f7d7-44cb-a508-7b90fad0ae73) + +--- + +#### Dodawanie nowego adresu e-mail i wysyłanie notatek ✔ + +![Zrzut ekranu 2025-02-05 233731](https://github.com/user-attachments/assets/a855805f-67e0-4c78-8d31-51889e0800f1) +![Zrzut ekranu 2025-02-05 233319](https://github.com/user-attachments/assets/76bb0f19-d514-4fae-9802-14a1e39574f3) +![Zrzut ekranu 2025-02-05 234026](https://github.com/user-attachments/assets/7f33867d-439b-4eb8-91a1-4975f0467fe8) + +--- + +| ![logoo](https://github.com/user-attachments/assets/4b34cc5f-8992-45bb-b354-4a69a66a5189) | **Zespół NoteWriter Girls Inc.** | **👑 Ola 🐝 Maja 🐝 Asia 🐝 Julka** | +|:--:|:--:|:--:| + + diff --git a/testy/testy_niefunkcjonalne.md b/testy/testy_niefunkcjonalne.md index 6567fd7..54e2f61 100644 --- a/testy/testy_niefunkcjonalne.md +++ b/testy/testy_niefunkcjonalne.md @@ -286,3 +286,9 @@ Wysłanie zapytania HTTP do serwera, powoduje chwilowe wystąpienie błędów ### Opera ✔ ![Zrzut ekranu 2025-01-25 160928](https://github.com/user-attachments/assets/f861874c-9f3d-4e7e-a378-0be38a8ce400) + +--- + +| ![logoo](https://github.com/user-attachments/assets/4b34cc5f-8992-45bb-b354-4a69a66a5189) | **Zespół NoteWriter Girls Inc.** | **👑 Ola 🐝 Maja 🐝 Asia 🐝 Julka** | +|:--:|:--:|:--:| +