Sprint 2 ‐ Deliverable - GapFinder-Group21/GapFinderBackend GitHub Wiki
Sprint 2 - Deliverable
Table of Contents
- Business Questions
- Analytics Pipeline
- Architectural Design
- Implemented Features
- Implemented Views
- Ethics Video
- Repository Evidence and Collaboration
- Dashboard BQ's
Team Responsibilities Overview
| Member | BQ | Architectural pattern (contribution) | Design pattern (responsible) | View(s) |
|---|---|---|---|---|
| Miguel Cabrera | BQ 5 | Event-driven | Observer | Flutter: Register, Interests, Schedule setup, Google calendar, Gap detail, Match, Searching match, Match found, Waiting, Open table detail |
| Joel Niño | BQ 11 | Client-Server (security and deployment) | Strategy | Flutter: Welcome, Login, Main screen, Friends, Match invitation, It's a match, Google connect |
| Sofía Arias | BQ 7 | Client-Server (REST resources) | Builder | Kotlin: Welcome, Register, Login, Location permission, Interests, Schedule setup, Google connect, Google calendar, Open tables map, Create open table, Open table detail, My open tables |
| Daniela Martínez | BQ 13 | Layered | Facade | Kotlin: Schedule, Gap detail, Friends, Match, Recommendations, Searching match, Match found, Waiting response, Match invitation, It's a match |
| Sofía Morato | BQ 12 | Layered | Adapter | Flutter: Location permission, Schedule, Campus map, Create open table, My open tables, Recommendations |
Repositories
- Backend: GapFinderBackend
- Frontend (Flutter / Dart): GapFinderFrontEnd-Dart
- Frontend (Kotlin): front-kotlin
- Kanban boards: GapFinder project board 2 · GapFinder project board 4
Diagram Source Files
These are our editable .drawio files. Each diagram is shown in its section below, with the source page and PNG filename beside it. We kept the original page names so they are easy to find in draw.io.
| Owner | Editable diagram file |
|---|---|
| Group | diagramas-grupo.drawio |
| Miguel Cabrera | diagramas-miguel-cabrera.drawio |
| Joel Niño | diagramas-joel.drawio |
| Sofía Morato | diagramas-sofia-morato.drawio |
| Daniela Martínez | diagramas-daniela.drawio |
| Sofía Arias | diagramas-sofia-arias.drawio |
1. Business Questions
| Sprint 1 # | Business Question (implemented version) | Type | Implemented by | Rationale | Answer format |
|---|---|---|---|---|---|
| 3 | At which step do students most often leave the Open Table creation process? | Type 2 | Group | We want to know which step of the Open Table wizard makes students give up. The answer appears in the Type 2 section of our analytics dashboard and shows us which step to simplify so creating an Open Table is easier for students. | A large text with the step name, its abandonment rate and a progress bar. The question has a single answer, so one big number is the clearest way to show it. |
| 5 | Which gap durations have the lowest match coverage? | Type 2 | Miguel Cabrera | We want to see which gap lengths are harder to fill with a match. The answer can help students decide how to use that free time or try an Open Table. The answer appears in the Type 2 section of our analytics dashboard. | A column chart with the coverage % of each duration range, with the lowest one highlighted. The ranges follow a natural order, so columns make it easy to compare them and spot the weakest one. |
| 7 | Which connection method leads to more accepted meetups: open tables or matches? | Type 3 | Sofía Arias | We compare the two ways students can meet in GapFinder. This helps us decide which feature to improve and which one may need changes. | Two horizontal bars (matches vs. open tables) with the completion %, the winner highlighted and labeled. With only two options, two bars side by side make the comparison immediate. |
| 11 | Where do the most students have free time on campus? | Type 4 | Joel Niño | Campus administration and student organizations can use these building-level results to decide where to offer spaces or activities. These decisions happen outside the app. | A ranking of horizontal bars with the number of students per building, the top building highlighted. A ranking shows at a glance which buildings matter most, and horizontal bars fit long building names. |
| 12 | Which careers and semesters have the highest rate of unmatched free time on campus? | Type 4 | Sofía Morato | University Wellbeing can use the results to see which careers and semesters have more unmatched free time and decide where to focus its programs. We show group results rather than individual students. | A ranking of horizontal bars with the unmatched % for each career and semester, the highest one highlighted. The ranking puts the groups that need attention first and only shows group-level data. |
| 13 | Which interests are shared by the largest groups of students who are free at the same time? | Type 2 + Type 4 | Daniela Martínez | This answer can improve group suggestions inside the app (Type *). It can also help student clubs plan activities around shared interests using group-level results (Type 4). | Overlapping horizontal bars for each interest (largest free group over all students with that interest), plus a table with the peak time. The bars show the size of the group and its share of the total, and the table adds when that group is free. |
1.1 Changes after feedback
| Sprint 1 # | Sprint 1 wording | Sprint 2 wording | What changed and why |
|---|---|---|---|
| 3 (group, Type 2) | At which step do students most often leave the gap-publishing process? | At which step do students most often leave the Open Table creation process? | We changed the flow to the Open Table wizard because it has four clear steps we can track: activity, description, participants, and location. |
| 5 | Which gap durations most often end without a match? | Which gap durations have the lowest match coverage? | We changed from a count to a coverage percentage. Otherwise, the duration groups with more gaps would be harder to compare with smaller groups. |
| 7 | Which connection method leads to more accepted meetups: open plans or direct invitations? | Which connection method leads to more accepted meetups: open tables or matches? | We replaced the original terms with the features we actually have in the app: Open Tables and Matches. |
| 11 | When and where do the most students have free time on campus? | Where do the most students have free time on campus? | The original wording asked both when and where. We focused on where, which we can answer with locations recorded during gaps. |
| 12 | Which faculties and semesters concentrate the highest amount of unmatched free time on campus? | Which careers and semesters have the highest rate of unmatched free time on campus? | We use a rate to compare groups of different sizes. We also changed faculty to career because our app stores the academic program. |
| 13 | Unchanged | Unchanged | We kept the same question. |
2. Analytics Pipeline
2.1 Pipeline Diagram
2.1.1 General pipeline
Diagram file: diagramas-grupo.drawio · Page: 1. Pipeline general · PNG filename: pipeline-general.png
Rationale: We organized the pipeline into five stages: data sources, ingestion, storage, processing, and presentation. The data comes from what students enter in the app, GPS updates, app events, and Google Calendar. Our REST endpoints receive it, and we store it in PostgreSQL with PostGIS. Repository queries do the calculations that involve many records, while AnalyticsService handles the rules for each question. We then return the answers through the analytics endpoints and show them in our analytics dashboard.
We use both scheduled and on-demand work. Jobs generate the weekly gaps and update meetings that have ended. The BQs are calculated when they are requested. We kept everything in one database because, at our current scale, a separate data warehouse would add complexity without giving us much benefit.
2.1.2 Pipeline per Business Question
BQ 5 – Miguel Cabrera
Diagram file: diagramas-miguel-cabrera.drawio · Page: 1. Pipeline BQ5 · PNG filename: pipeline-bq5.png
Rationale: We generate the weekly gaps from each student's class schedule. For BQ 5, the query groups those gaps by duration and adds the minutes covered by accepted or completed matches. We cap the covered minutes at the length of the gap so coverage cannot go above 100%. AnalyticsService calculates the percentage and puts the least-covered duration groups first. Grouping by duration helps us compare short and long gaps without treating them as if they were the same.
BQ 7 – Sofía Arias
Diagram file: diagramas-sofia-arias.drawio · Page: 1. Pipeline BQ7 · PNG filename: pipeline-bq7.png
Rationale: We compare Open Tables and Matches using the completed and total counts for each method. Scheduled jobs update their status to COMPLETED after they end, so students do not have to close them manually. Since we only need four counts, Spring Data queries are enough here. The service calculates both percentages and handles a tie. We use COMPLETED as the indicator of a finished meetup, although this status does not tell us whether everyone actually attended.
BQ 11 – Joel Niño
Diagram file: diagramas-joel.drawio · Page: 1. Pipeline BQ11 · PNG filename: pipeline-bq11.png
Rationale: The phone sends GPS coordinates, and our backend uses PostGIS ST_DWithin to find the campus building. For each gap, we take the latest location recorded during that gap using DISTINCT ON. This means someone who sends several updates counts once for that gap. We then group the records by building and order them to see where more students spend their free time.
BQ 12 – Sofía Morato
Diagram file: diagramas-sofia-morato.drawio · Page: 1. Pipeline BQ12 · PNG filename: pipeline-bq12.png
Rationale: We join gaps, users, and match results in SQL, then group the minutes by career and semester. We keep this calculation in the database so we do not have to load all the records into Java. Covered minutes are capped at the length of each gap. AnalyticsService checks the dates, calculates the unmatched-time rate, orders the results, and limits the response. Since this is a Type 4 question, the answer shows groups of students rather than individual records.
BQ 13 – Daniela Martínez
Diagram file: diagramas-daniela.drawio · Page: 1. Pipeline BQ13 · PNG filename: pipeline-bq13.png
Rationale: To find students who are free at the same time, we check the start of each gap. At each of those times, we count students who share an interest and have an active gap. ROW_NUMBER lets us keep the largest group for each interest. We only need to check gap starts because that is when the number of available students can increase. The service checks the date range, sorts the groups, and applies the requested limit.
Type 2 feature – Open Table creation abandonment (BQ 3, group)
Diagram file: diagramas-grupo.drawio · Page: 3. Pipeline BQ3 (group) · PNG filename: pipeline-bq3.png
Rationale: The Open Table wizard has four steps in a fixed order. A student who leaves at a later step has already passed through the earlier ones. To calculate how many students reached a step, we count completed tables and abandonments at that step or any later step. We compare rates because fewer students reach the last steps. The answer appears in the Type 2 section of our analytics dashboard, and it shows us which step of the wizard to simplify so creating an Open Table is easier for students.
2.2 Pipeline Stages
| Stage | Technology / component | Description |
|---|---|---|
| Data collection (app events) | Mobile inputs: GPS sensor, user input (profile, schedule, matches, open tables), in-app events (wizard abandonment); Google Calendar (external) | We collect GPS updates during gaps, profile and schedule details, match and Open Table activity, and wizard cancellations. Students can also import their schedule from Google Calendar. |
| Ingestion (backend endpoints) | Spring Boot REST controllers (JWT-protected) + scheduled jobs (WeeklyGapJob, completeExpiredMatches, closeExpiredTables) | Our REST controllers check incoming requests and save the data. JWT protects the restricted endpoints. Jobs generate weekly gaps and update matches and tables that have ended. |
| Storage | PostgreSQL 16 + PostGIS 3.5 (Docker container) | We keep the app's records in one relational database. PostGIS lets us store building geometry and work with campus locations. |
| Processing / aggregation | Native SQL / JPQL / Spring Data queries in repositories (projections) + AnalyticsService + AnalyticsFacade | We do the larger calculations in repository queries and return only the fields we need. AnalyticsService handles dates, rates, ordering, and limits; AnalyticsFacade combines the dashboard answers. |
| Visualization / consumption | GET /analytics/* (one endpoint per BQ), GET /analytics/dashboard; web analytics dashboard (dashboard.html served by the backend, Chart.js) | We return each answer through its own analytics endpoint and offer a dashboard response with all the answers. Our web dashboard groups the BQs by type and draws a chart for each one. It lets us filter by period (last 7, 30, or 90 days, or all time) and by Top N, and it refreshes every 60 seconds. |
2.3 BQ-to-Pipeline Mapping
| BQ | Data collected | Where it is stored | How it is computed | Where the answer is shown |
|---|---|---|---|---|
| BQ 5 – Miguel Cabrera | Class schedule, match proposals / responses | class_blocks → gaps, matches | GapRepository.findCoverageByDurationBucket (native SQL, duration buckets) + AnalyticsService.getMatchCoverageByGapDuration | GET /analytics/gap-coverage/by-duration; analytics dashboard (Type 2 section, column chart with the lowest coverage highlighted) |
| BQ 7 – Sofía Arias | Match and open table status changes | matches, open_tables | countByStatus(COMPLETED) + count() (Spring Data) + AnalyticsService.compareConnectionMethods | GET /analytics/connection-methods/accepted-meetups; analytics dashboard (Type 3 section) |
| BQ 11 – Joel Niño | GPS location during gaps, class schedule | location_logs, buildings, gaps | UserLocationLogRepository.findGapPresenceByBuilding (native SQL, DISTINCT ON) + AnalyticsService.getBuildingsByGapPresence | GET /analytics/buildings/gap-presence; analytics dashboard (Type 4 section) |
| BQ 12 – Sofía Morato | Career and semester (sign-up), class schedule, match outcomes | users, class_blocks → gaps, matches | GapRepository.findFreeTimeByCareerAndSemester (native SQL) + AnalyticsService.getUnmatchedFreeTimeByCareerAndSemester | GET /analytics/free-time/unmatched-by-career-semester; analytics dashboard (Type 4 section) |
| BQ 13 – Daniela Martínez | Interests in the profile, class schedule | interests, user_interests, gaps | GapRepository.findLargestFreeGroupByInterest (native SQL, ROW_NUMBER) + AnalyticsService.getInterestsByLargestFreeGroup | GET /analytics/interests/largest-free-groups; analytics dashboard (Type 2 + Type 4 section, with a table of peak times) |
| Type 2 feature: Open Table creation abandonment | Step where the user left the creation wizard; open tables created | open_table_abandonments, open_tables | OpenTableAbandonmentRepository.countGroupedByStep (JPQL) + OpenTableAbandonmentService.getMostAbandonedStep | GET /analytics/open-tables/most-abandoned-step; analytics dashboard (Type 2 section) |
2.4 Rationale
We kept the pipeline simple for the amount of data we expect from a campus. The app's existing records also give us the data for analytics, so we use the same database for both. SQL handles the joins and group calculations close to the data, while the services handle the rules for each question.
We calculate the BQs when they are requested so we do not have to maintain separate answer tables that could become outdated. Scheduled jobs handle tasks such as preparing weekly gaps and updating meetings that have ended. The web dashboard reads all the answers with a single call to GET /analytics/dashboard, so the whole team can check the results in one place.
3. Architectural Design
3.1 Architecture: Overall Structure and Components
Diagram file: diagramas-grupo.drawio · Page: 2. Deployment (UML) · PNG filename: deployment.png
Deployment with both mobile apps (Flutter and Kotlin) – Sofía Arias
Diagram file: Deployment diagram (draw.io) · Page: Deployment · PNG filename: sprint2-deployment-diagram.png

| Component | Technology | Responsibility |
|---|---|---|
| Mobile app (frontend) | Flutter (Dart): http, flutter_secure_storage, geolocator, flutter_map + latlong2, url_launcher, google_fonts | Our Flutter app shows the screens, reads GPS coordinates, stores tokens securely, and calls the REST API. Miguel, Joel, and Morato work on this platform. |
| Mobile app (frontend) – Kotlin | Kotlin (Android): Jetpack Compose + Material 3, OkHttp, EncryptedSharedPreferences (security-crypto), Play Services Location, Coil, Kotlin Coroutines | Our Kotlin app shows the same screens and flows as the Flutter app, reads GPS coordinates, stores tokens encrypted, and calls the same REST API. Daniela and Arias work on this platform. |
| Backend API | Spring Boot 4.1 (Java 21, Maven), Spring Security + JWT (jjwt), Spring Data JPA, ModelMapper | Our backend handles gaps, matching, Open Tables, notifications, analytics, authentication, and the Google integration. |
| Database | PostgreSQL 16 + PostGIS 3.5 (Hibernate Spatial), pgAdmin; Docker Compose | We store the app's records here and use PostGIS to work out campus locations. |
| External services | Google OAuth 2.0 + Google Calendar API | We use Google OAuth to connect the student's account and Google Calendar to import class schedules. |
| Analytics | AnalyticsService + AnalyticsFacade, /analytics/* endpoints, web dashboard (dashboard.html + Chart.js) | We calculate BQ answers when requested and return them through individual endpoints or the combined dashboard. The web dashboard shows them as charts grouped by BQ type. |
Rationale: We keep the interface and device features in the mobile apps, and the business rules in the backend. This gives Flutter and Kotlin the same rules and data, and lets us change a rule in one place. Google requests also go through the server. We use Docker Compose to run the API, database, and pgAdmin together; the API waits until the database healthcheck succeeds before starting.
3.1.1 Domain Model
Diagram file: Domain model (draw.io) · Page: Domain model · PNG filename: sprint2-domain-model.png

3.2 Architecture Design: Component Interaction
3.2.1 Authentication flow (register / login / token refresh)
Diagram file: diagramas-grupo.drawio · Page: 4. Authentication flow (UML sequence) · PNG filename: flow-auth.png
Rationale: We use a short-lived access token and a longer-lived refresh token. This limits how long an access token can be used if it is exposed. Flutter saves both tokens in secure storage, and the backend stores passwords as BCrypt hashes. We return the same login error for a wrong email or password so the response does not reveal which accounts exist. When a request comes back with 401 or 403, ApiClient asks for a new access token with the refresh token and retries the request once. If the refresh token is no longer valid, it clears the saved session and sends the student back to the Welcome screen.
3.2.2 Location flow (GPS → backend → building detection)
Diagram file: diagramas-grupo.drawio · Page: 5. Location flow (UML sequence) · PNG filename: flow-location.png
Rationale: The app sends coordinates, and the backend decides which building they belong to. That way, changing the campus map does not require an app update. If the coordinates do not match a building, we clear the student's current building. Nearby-friend results only include locations updated in the last 30 minutes. When the student changes buildings, the Flutter interaction gives haptic feedback instead of vibrating on every GPS update.
3.2.3 Open Table creation and abandonment reporting
Diagram file: diagramas-grupo.drawio · Page: 6. Open Table abandonment flow (UML sequence) · PNG filename: flow-abandonment.png
Rationale: We track the wizard's current step and use _abandonmentSent to avoid sending the same cancellation twice. The screen uses PopScope, so the back arrow, the back gesture, and the Cancel button all count as leaving the wizard. The app sends POST /open-table-abandonments/user/{userId} with the step and the choices made so far, and the backend checks that the reported data makes sense for that step before using it in BQ 3. If the request fails, the student can still leave the screen normally.
3.3 Design Patterns and Tactics
3.3.1 Architectural patterns
Layered – Sofía Morato and Daniela Martínez
Diagram file: diagramas-sofia-morato.drawio · Page: 2. Layered (UML packages) · PNG filename: layered-morato.png
Diagram file: diagramas-daniela.drawio · Page: 2. Layered (UML packages) · PNG filename: layered-daniela.png
Rationale: We split the backend into controllers, services, repositories, models, and DTOs. Controllers receive requests, services apply business rules, and repositories read or write data. DTOs define what the API returns. This structure helped us divide the work while keeping the same organization across features. Morato's contribution belongs to the Flutter team's backend work; Daniela's belongs to the Kotlin team's backend work and includes the analytics facade.
Client-Server – Joel Niño and Sofía Arias
Diagram file: diagramas-joel.drawio · Page: 2. Client-Server (UML component) · PNG filename: client-server-joel.png
Diagram file: diagramas-sofia-arias.drawio · Page: 2. Client-Server (UML component) · PNG filename: client-server-arias.png
Rationale: Both apps use the same server for data and business rules. Joel worked on JWT security and Docker deployment as part of the Flutter team. Arias worked on REST resources and their rules as part of the Kotlin team. Authentication is stateless, so an API instance does not need to keep a separate session for every logged-in student.
Event-driven – Miguel Cabrera
Diagram file: diagramas-miguel-cabrera.drawio · Page: 2. Event-driven (architecture) · PNG filename: event-driven.png
Rationale: We use events so a service can report what happened without calling the notification code directly. For example, accepting a match publishes an event that a listener can react to. If we add another reaction later, we can add a listener without changing the match logic. Spring's event system is enough for our current scale. MatchService publishes an event when a match is proposed, accepted, or rejected, and NotificationListener also has handlers for friendship and Open Table events, so those services can use the same channel.
Frontend architecture (Flutter) – Joel Niño, Sofía Morato, Miguel Cabrera
Diagram file: diagramas-grupo.drawio · Page: 7. Frontend architecture (Flutter) · PNG filename: frontend-architecture.png
Rationale: We organized the Flutter app in layers: screens and widgets, services, models, and the core code for HTTP requests and token storage. Screens call services, and services use ApiClient, which handles the base URL, the Authorization header, and the token refresh. This means a change to authentication or the API client does not need to be repeated in every screen. Each screen manages its own state with setState or FutureBuilder, and navigation uses named routes in main.dart with a global navigatorKey, which also lets ApiClient send the student to the Welcome screen when the session ends.
Frontend architecture (Kotlin) – Daniela Martínez, Sofía Arias
Diagram file: frontend-architecture-kotlin.svg · Page: Frontend architecture (Kotlin) · PNG filename: frontend-architecture-kotlin.png

Rationale: We organized the Kotlin app in the same layers as the Flutter app: screens and reusable UI components (screens/, ui/kit/), repositories (data/), domain models (domain/), and the core code for HTTP requests and token storage (foundation/). Screens call repositories, and repositories use HttpGateway, which handles the base URL, the Authorization header, and the token refresh. This means a change to authentication or the API client does not need to be repeated in every screen. Screens are Jetpack Compose functions that keep their own state, and navigation uses a sealed Destination interface with a custom StackNavigator, the equivalent of the named routes in Flutter. Screens lower in the stack stay alive, so they keep their state and timers, and HttpGateway can send the student back to the Welcome screen when the session ends.
3.3.2 Design patterns
Adapter – Sofía Morato (Google Calendar schedule import)
Diagram file: diagramas-sofia-morato.drawio · Page: 3. Adapter (UML class) · PNG filename: adapter-class.png
Diagram file: diagramas-sofia-morato.drawio · Page: 4. Adapter (UML sequence) · PNG filename: adapter-sequence.png
Rationale: Google Calendar returns Event objects that do not match our schedule model. We use ScheduleSource as the interface for importing a schedule, and GoogleCalendarScheduleAdapter translates Google's events into the data GapFinder needs. The adapter also handles Google-specific errors. This lets the import service work through one interface, and we could add another schedule source by implementing that same interface.
Strategy – Joel Niño (match scoring)
Diagram file: diagramas-joel.drawio · Page: 3. Strategy (UML class) · PNG filename: strategy-class.png
Diagram file: diagramas-joel.drawio · Page: 4. Strategy (UML sequence) · PNG filename: strategy-sequence.png
Rationale: We gave the four match-scoring rules the same MatchingStrategy interface: overlap, career, interests, and effort. MatchService adds the scores from the active rules. This makes each rule easier to test or change on its own. Each strategy is a Spring component, so MatchService receives them through its constructor.
Observer – Miguel Cabrera (notifications)
Diagram file: diagramas-miguel-cabrera.drawio · Page: 3. Observer (UML class) · PNG filename: observer-class.png
Diagram file: diagramas-miguel-cabrera.drawio · Page: 4. Observer (UML sequence) · PNG filename: observer-sequence.png
Rationale: MatchService publishes events through ApplicationEventPublisher, and NotificationListener reacts to them. The service does not need to know which listeners are watching. We use immutable events so a listener cannot change the information that was published. This also lets us add more observers without changing MatchService. NotificationListener also includes methods for FriendshipEvent and OpenTableEvent.
Builder – Sofía Arias (open table recommendation response)
Diagram file: diagramas-sofia-arias.drawio · Page: 3. Builder (UML class) · PNG filename: builder-class.png
Diagram file: diagramas-sofia-arias.drawio · Page: 4. Builder (UML sequence) · PNG filename: builder-sequence.png
Rationale: The recommendation response has several parts and two main cases: a student with a favorite building and a student without one. OpenTableRecommendationResponseBuilder lets us set each part by name, which is easier to follow than a constructor with several arguments. Its default values also cover the empty response. We did not add a separate builder interface because we only have one response product.
Facade – Daniela Martínez (analytics dashboard)
Diagram file: diagramas-daniela.drawio · Page: 3. Facade (UML class) · PNG filename: facade-class.png
Diagram file: diagramas-daniela.drawio · Page: 4. Facade (UML sequence) · PNG filename: facade-sequence.png
Rationale: The dashboard needs six answers that come from different methods. AnalyticsFacade brings them together in one call and uses the last 30 days by default. If a question has no data, we return an empty or null result for that part so the other answers can still be shown. We do not put all the queries in one transaction because we want each part to be handled independently.
3.3.3 Summary of patterns and tactics
| Pattern / Tactic | Kind | Where it is used | Responsible | Rationale |
|---|---|---|---|---|
| Layered | Architectural pattern | controllers/ → services/ → repositories/ → models/, DTOs + ModelMapper | Sofía Morato, Daniela Martínez | We use the same layers across features so each part has a clear job. |
| Adapter | Design pattern | ScheduleSource, GoogleCalendarScheduleAdapter, GoogleScheduleImportService | Sofía Morato | We keep Google-specific code behind the schedule-source interface. |
| Exceptions (availability) | Tactic | GlobalExceptionHandler, AuthExceptionHandler, ScheduleSourceUnavailableException | Sofía Morato, Daniela Martínez | We handle errors in one place and return an appropriate HTTP response. |
| Graceful degradation (availability) | Tactic | Adapter skips unsupported Google events; Facade returns null / empty list when a BQ has no data | Sofía Morato, Daniela Martínez | An unsupported Google event or a BQ with no data does not stop the rest of the result. |
| Concurrency (performance) | Tactic | @Scheduled jobs: WeeklyGapJob, completeExpiredMatches | Miguel Cabrera, Daniela Martínez | Jobs handle scheduled updates separately from user requests. |
| Authentication (security) | Tactic | JWT (JwtService, JwtAuthFilter), AuthService (BCrypt), refresh tokens (RefreshTokenService) | Joel Niño, Sofía Arias, Sofía Morato | We check identity with tokens and store passwords as hashes. |
| Layered (Flutter frontend) | Architectural pattern | screens/ + widgets/ → services/ → core/ (ApiClient, TokenStorage), models/ | Miguel Cabrera, Joel Niño, Sofía Morato | We keep request details out of the screens, so each layer has one job. |
| Encryption at rest (security, front) | Tactic | core/token_storage.dart: tokens in flutter_secure_storage | Sofía Morato | We save tokens in secure device storage. |
| Observer | Design pattern | MatchEvent / FriendshipEvent / OpenTableEvent, NotificationListener | Miguel Cabrera | We can add notification listeners without changing MatchService. |
| Event-driven | Architectural pattern | Spring ApplicationEventPublisher + NotificationListener | Miguel Cabrera | A service reports an event without needing to call the code that reacts to it. |
| Strategy | Design pattern | MatchingStrategy + 4 strategies, MatchService.findCandidates | Joel Niño | We can change and test each scoring rule separately. |
| Ping/Echo (availability) | Tactic | pg_isready healthcheck in docker-compose.yml; API waits for service_healthy | Joel Niño | We wait for the database to respond before starting the API. |
| Authorization (security) | Tactic | SecurityConfig: only /auth/**, /google/callback, /dashboard.html, and /analytics/dashboard are public | Joel Niño | We restrict every other endpoint, including the individual BQ endpoints, to authenticated users. |
| Facade (Flutter API client) | Design pattern | core/api_client.dart: get, post, put, patch, and delete hide the base URL, headers, JSON encoding, and token refresh | Joel Niño | Services make requests through one simple class instead of building HTTP calls and auth headers themselves. |
| Authentication – token refresh (security, front) | Tactic | core/api_client.dart: adds Authorization: Bearer, refreshes the token on 401 / 403 and retries the request once, forces logout if the refresh token expired | Joel Niño, Sofía Morato | The session is renewed without asking the student to log in again. |
| Dependency injection (modifiability) | Tactic | Constructor injection in every Spring component | Everyone | Spring supplies dependencies through constructors, making them easier to replace in tests. |
| Facade | Design pattern | AnalyticsFacade, AnalyticsDashboardController | Daniela Martínez | The dashboard gets its answers through one call. |
| Builder | Design pattern | OpenTableRecommendationResponseBuilder, RecommendationService | Sofía Arias | We build each part of the response by name and use defaults for the empty case. |
| Client-Server | Architectural pattern | Flutter and Kotlin apps ↔ Spring Boot REST API; security filter chain; Docker deployment | Joel Niño, Sofía Arias | Both apps use the same data and rules on the server. |
| Layered (Kotlin frontend) | Architectural pattern | screens/ + ui/kit/ → data/ (repositories) → foundation/ (HttpGateway, SessionVault), domain/ | Daniela Martínez, Sofía Arias | We keep request details out of the screens, so each layer has one job. |
| Encryption at rest (security, front) | Tactic | foundation/SessionVault.kt: tokens in EncryptedSharedPreferences (AES-256) | Sofía Arias | We save tokens in encrypted device storage. |
4. Implemented Features
4.1 All implemented functionalities
| # | Functionality | Description | Flutter app | Backend | Kotlin app |
|---|---|---|---|---|---|
| 1 | Register, login, and session | Students create an account with their email and password, log in, and stay logged in while their refresh token is valid. Logging out removes the session from the phone. | Miguel Cabrera (register, auth service), Joel Niño (welcome, login, API client), Sofía Morato (secure token storage) | Miguel Cabrera, Joel Niño, Sofía Arias, Sofía Morato, Daniela Martínez | Sofía Arias (welcome, register, login, session refresh in HttpGateway, SessionVault), Daniela Martínez (AccountRepository) |
| 2 | Onboarding: interests and location permission | After registering, students choose their interests and give location permission so the app can work with their place on campus. | Miguel Cabrera (interests, location service and Android permissions), Sofía Morato (location permission screen) | Daniela Martínez (interests), Miguel Cabrera (user location fields) | Sofía Arias (location permission, interests), Daniela Martínez (GeoProbe, HobbyRepository) |
| 3 | Class schedule (manual) | Students add their class blocks by day and time and see their weekly schedule with their classes and free gaps. | Miguel Cabrera (schedule setup), Sofía Morato (schedule view) | Sofía Morato (class blocks) | Sofía Arias (schedule setup), Daniela Martínez (schedule tab, LectureRepository) |
| 4 | Schedule import from Google Calendar | Students connect their Google account and import their classes from Google Calendar instead of typing them. | Miguel Cabrera (Google calendar screen), Joel Niño (Google connect screen), Sofía Morato (Google service) | Joel Niño, Sofía Morato, Sofía Arias, Daniela Martínez | Sofía Arias (Google connect, Google calendar import), Daniela Martínez (CalendarBridge) |
| 5 | Weekly gap generation | Every Sunday night, the backend creates next week's free gaps from each student's class schedule. Students can also generate them on demand. | Miguel Cabrera (gap detail) | Miguel Cabrera | Daniela Martínez (gap detail, WindowRepository) |
| 6 | Matching between free gaps | From a free gap, students search for another student who is free at the same time. They can search by career or by shared interests, see the match found, and send a request. | Miguel Cabrera | Joel Niño (scoring strategies), Daniela Martínez (match and interest entities) | Daniela Martínez (match tab, searching match, match found, PairingRepository) |
| 7 | Match invitations and responses | Students receive match invitations, accept or reject them, and see the "It's a match" confirmation. Matches that already ended are marked as completed automatically. | Joel Niño (invitation, it's a match), Miguel Cabrera (waiting) | Daniela Martínez, Joel Niño, Miguel Cabrera | Daniela Martínez (waiting response, match invitation, it's a match) |
| 8 | Friends | Students search other students by name, send friend requests, accept or reject them, and see their friends list. | Joel Niño | Joel Niño | Daniela Martínez (friends tab, user search, friend requests, BondRepository) |
| 9 | Notifications | The backend creates a notification when someone proposes, accepts, or rejects a match. The main screen checks for new notifications regularly and opens the match invitation when one arrives. | Joel Niño (main screen, notification service) | Miguel Cabrera | Daniela Martínez (HomeShell checks notifications every 10 s, AlertRepository) |
| 10 | GPS location and building detection | The app reads the phone's GPS and the backend finds the campus building the student is in. | Miguel Cabrera (location service), Joel Niño (location log service), Sofía Morato (location banner) | Sofía Morato (building detection), Joel Niño (location update), Sofía Arias (location logs) | Sofía Arias (SpotStatusLine), Daniela Martínez (GeoProbe, SpotRepository, PingRepository) |
| 11 | Campus map | Students see the campus buildings and their current location on a map. | Sofía Morato | Sofía Morato, Miguel Cabrera (building coordinates) | Sofía Arias (open tables map with campus buildings) |
| 12 | Nearby friends alert | When the student enters a different building, the app shows which friends are in that building and vibrates. | Miguel Cabrera, Joel Niño, Sofía Morato | Joel Niño, Sofía Morato | Sofía Arias (NearbyFriendsDialog, building change detection and haptic feedback in SpotStatusLine), Daniela Martínez (NearbyRepository) |
| 13 | Open Tables | Students create an Open Table in four steps (activity, description, participants, location), see their own tables, open a table's details, and join other tables. Tables that already ended are closed automatically. | Sofía Morato (create, my tables, map), Miguel Cabrera (table detail, participants), Joel Niño (Open Table models) | Sofía Arias, Miguel Cabrera | Sofía Arias (open tables map, create, detail, my open tables), Daniela Martínez (HangoutRepository, AttendeeRepository) |
| 14 | Open Table abandonment tracking | When a student leaves the creation wizard without publishing, the app reports the step where they left. | Sofía Morato, Miguel Cabrera, Joel Niño | Miguel Cabrera, Joel Niño, Daniela Martínez, Sofía Arias | Sofía Arias (step tracking in HangoutComposerPage), Daniela Martínez (DropOffRepository) |
| 15 | Favorite building and recommended Open Tables | The app finds the building where the student spends the most free time and recommends Open Tables there that fit their upcoming gaps. | Sofía Morato (recommendations screen), Miguel Cabrera (recommendation service), Joel Niño (Open Table models) | Joel Niño, Sofía Morato, Sofía Arias | Daniela Martínez (recommendations tab, RecommendationRepository), Sofía Arias (Recommendation model, recommendations in the bottom bar) |
| 16 | Analytics endpoints and web dashboard | The backend answers each BQ through its own endpoint, and a web dashboard shows all the answers as charts grouped by type, with period and Top N filters. | – | Miguel Cabrera, Joel Niño, Sofía Morato, Sofía Arias, Daniela Martínez | – |
4.2 Required features – Flutter app (+ backend)
Team: Miguel Cabrera, Joel Niño, and Sofía Morato.
| Item | Feature | Description | Responsibilities and contributions |
|---|---|---|---|
| a. Sensor | GPS location | The app asks for location permission and reads the phone's GPS. It sends the coordinates to our backend, which finds the campus building and saves the location record. | Miguel Cabrera: Flutter – location_service (checks and requests location access) and location permissions in AndroidManifest.xml; Backend – location fields in UserModel / UserBasicDTO / UserCompleteDTOJoel Niño: Flutter – user_location_log model and user_location_log_service, updateLocationByGps in user_service; Backend – PATCH /users/{id}/location/gps, NearbyFriendsServiceSofía Morato: Flutter – location_permission_screen; Backend – building detection with PostGIS (GET /buildings/locate, GeometryConfig) |
| b. Type 2 BQ | Open Table creation abandonment | When a student leaves Open Table creation, we record the step where they left. The back arrow, the back gesture, and the Cancel button all report it. Those records let us calculate the step with the highest abandonment rate for BQ 3. | Miguel Cabrera: Flutter – open_table_abandonment_service (createAbandonment, getStats); Backend – OpenTableAbandonmentController, OpenTableAbandonmentRepositoryJoel Niño: Flutter – open_table_abandonment and stats models; Backend – OpenTableAbandonmentModel, OpenTableCreationStepEnum, registration and validationSofía Morato: Flutter – create_open_table_screen: four-step wizard and step tracking (_currentStep, _abandonmentSent, _onCancel, PopScope) |
| c. Context aware | Nearby friends when the building changes | When a student changes buildings, the app checks which friends are in that building. It shows an alert and gives haptic feedback so the student can notice nearby friends. | Miguel Cabrera: Flutter – nearby_friends_alert_dialogJoel Niño: Flutter – nearby_friends_service; Backend – NearbyFriendsService, NearbyFriendsControllerSofía Morato: Flutter – current_location_banner: GPS updates, building change detection (_previousBuildingName, buildingChanged), and haptic feedback; Backend – GET /users/{userId}/friends/nearby |
| d. Smart feature | Favorite building + recommended open tables | We use location history to find where the student spends the most free time. We then recommend open tables in that building that overlap their upcoming gaps. | Miguel Cabrera: Flutter – recommendation_service (GET /recommendations/user/{id}/open-tables)Joel Niño: Flutter – open_table models; Backend – RecommendationController, favorite building querySofía Morato: Flutter – recommendations_screen (favorite building banner, table list, empty state, loading, and errors); Backend – findMatchingOpenTables, OpenTableRecommendationResponseDTO |
| e. Authentication | Register / login / token refresh | Students can register and log in with an email and password. Our backend returns access and refresh tokens, which Flutter stores securely. When the access token expires, the app refreshes it and retries the request without asking the student to log in again. | Miguel Cabrera: Flutter – register_screen, auth_service (register / login / refresh / logout); Backend – LoginRequest, RefreshRequest, RegisterRequest, AuthControllerJoel Niño: Flutter – login_screen, auth_response models, api_client (token refresh and retry); Backend – JwtService, JwtAuthFilter, SecurityConfigSofía Morato: Flutter – token_storage (flutter_secure_storage); Backend – refresh tokens (RefreshTokenModel / repository / service) |
| f. External service | Google Calendar import | Students connect their Google account and import their class schedule from Google Calendar. The import skips events that are not supported as classes. | Miguel Cabrera: Flutter – google_calendar_screenJoel Niño: Flutter – google_connect_screen, google_auth_url_response; Backend – GoogleOAuthConfig, GoogleAuthController, GoogleCalendarServiceSofía Morato: Flutter – google_service; Backend – GoogleCredential model / repository, GoogleEventMapper, Adapter |
4.3 Required features – Kotlin app (+ backend)
Team: Daniela Martínez and Sofía Arias.
| Item | Feature | Description | Responsibilities and contributions |
|---|---|---|---|
| a. Sensor | GPS location | The app asks for location permission and reads the phone's GPS with Play Services Location. It sends the coordinates to our backend, which finds the campus building and saves the location record. | Sofía Arias: Kotlin – LocationGatePage (location permission), SpotStatusLine (refreshes the current building every 2 min and when the GPS changes); Backend – location logs (UserLocationLog model / repository / service / controller)Daniela Martínez: Kotlin – GeoProbe (permissions, GPS state, current fix), SpotRepository, PingRepository; Backend – location log DTOs |
| b. Type 2 BQ | Open Table creation abandonment | When a student leaves Open Table creation, we record the step where they left (activity, description, participants, or location). Those records let us calculate the step with the highest abandonment rate for BQ 3. | Sofía Arias: Kotlin – HangoutComposerPage: four-step wizard and step tracking (DraftStage, HangoutDropOff); Backend – BQ 3 analytics endpoint and DTOsDaniela Martínez: Kotlin – DropOffRepository; Backend – abandonment statistics in OpenTableAbandonmentService |
| c. Context aware | Nearby friends when the building changes | When a student changes buildings, the app checks which friends are in that building. It shows an alert and gives haptic feedback so the student can notice nearby friends. | Sofía Arias: Kotlin – SpotStatusLine: GPS updates, building change detection, and haptic feedback; NearbyFriendsDialog ("Friends Nearby!")Daniela Martínez: Kotlin – NearbyRepository (saves the location and returns the friends in the same building) |
| d. Smart feature | Favorite building + recommended open tables | We use location history to find where the student spends the most free time. We then recommend open tables in that building that overlap their upcoming gaps. | Sofía Arias: Kotlin – Recommendation model, recommendations in the bottom bar (DockBar); Backend – RecommendationService (+ Builder)Daniela Martínez: Kotlin – RecommendationsTab, RecommendationRepository |
| e. Authentication | Register / login / token refresh | Students can register and log in with an email and password. Our backend returns access and refresh tokens, which the Kotlin app stores encrypted. When the access token expires, the app refreshes it and retries the request without asking the student to log in again. | Sofía Arias: Kotlin – LandingPage, SignUpPage, SignInPage, SessionVault (EncryptedSharedPreferences), HttpGateway (token refresh and retry on 401 / 403); Backend – AuthServiceDaniela Martínez: Kotlin – AccountRepository; Backend – AuthResponse, UserBasicDTO, AuthExceptionHandler |
| f. External service | Google Calendar import | Students connect their Google account and import their class schedule from Google Calendar. The import skips events that are not supported as classes. | Sofía Arias: Kotlin – CalendarLinkPage (opens Google sign-in in the browser), CalendarImportPage, TimetableSetupPage; Backend – GoogleImportController, GoogleScheduleImportServiceDaniela Martínez: Kotlin – CalendarBridge; Backend – GoogleImportResult |
5. Implemented Views
| View | Responsible | Description | Screenshot |
|---|---|---|---|
| Register, Interests, Schedule setup, Google calendar | Miguel Cabrera | Students enter their details, choose their interests, and set up their schedule manually or import it from Google Calendar. | |
| Match, Searching match, Match found, Waiting response | Miguel Cabrera | Students open a free gap and go through the match flow: choose career or interests, search for candidates, see a match, and wait for a response. | |
| Open table detail | Miguel Cabrera | Students see an Open Table's information and participants and can join it. | |
| Welcome, Login, Main screen (navigation + notifications) | Joel Niño | Students enter the app, log in, and use the main screen to navigate. The main screen also checks for new match invitations. | |
| Friends, Match invitation, It's a match | Joel Niño | Students search for other students, manage friend requests, review a received match invitation, and see the match confirmation. | |
| Google connect | Joel Niño | Students connect their Google account before importing their schedule. | |
| Analytics dashboard (web, dashboard.html) | Joel Niño, Miguel Cabrera | The team sees every BQ answer as a chart, grouped by type, with filters for the period and Top N, an Apply filters button, and auto refresh every 60 seconds. | |
| Location permission | Sofía Morato | Students learn why GapFinder needs their location and give permission. | |
| Schedule, Campus map | Sofía Morato | Students see their weekly class schedule and the campus map, including buildings and their current location. | |
| Create open table, My open tables, Recommendations | Sofía Morato | Students create an Open Table step by step, see their own tables, and browse the tables recommended for their favorite building. | |
| Nearby friends alert (context aware feature) | Miguel Cabrera, Sofía Morato, Joel Niño | When a student enters a new building, the app detects the change, vibrates, and shows an alert with the friends who are in that building. | |
| Schedule, Gap detail, Friends, Recommendations (Kotlin) | Daniela Martínez | Students see their weekly schedule and free gaps, open a gap, manage friends and friend requests, and browse the Open Tables recommended for their favorite building. | |
| Match, Searching match, Match found, Waiting response, Match invitation, It's a match (Kotlin) | Daniela Martínez | Students start a match from a free gap, see the candidate, wait for a response, answer invitations, and see the match confirmation with a contact phone. | |
| Welcome, Register, Login, Location permission, Interests (Kotlin) | Sofía Arias | Students enter the app, create an account or log in, give location permission, and choose their interests. | |
| Schedule setup, Google connect, Google calendar (Kotlin) | Sofía Arias | Students set up their schedule manually or connect their Google account and import their classes from Google Calendar. | |
| Open tables map, Create open table, Open table detail, My open tables (Kotlin) | Sofía Arias | Students see Open Tables on the campus map, create one step by step, open a table's details with its participants and seats, and see their own tables. |
6. Ethics Video
Video link: https://youtu.be/ZqWtqxVwtlI
7. Repository Evidence and Collaboration
We have three repositories in the GapFinder-Group21 organization, and every one of them follows the same workflow: issue → branch → pull request → main.
Summary
| Backend | Flutter frontend | Kotlin frontend | |
|---|---|---|---|
| Repository | GapFinderBackend | GapFinderFrontEnd-Dart | front-kotlin |
| Kanban board | Backend board | Dart board | Kotlin board |
| Issues | 41 (all closed) | 21 (all closed) | 19 (all closed) |
| Milestones | 9 | 7 | 7 |
| Merged pull requests | 42 (#2–#85) | 21 (#2–#42) | 6 (#1–#6) |
| Branch naming | {Member}-{Task} |
front-{member}-{phase} |
{member}-{part of the app} |
8. Dashboard BQ's
The analytics dashboard shows the answers to all the business questions implemented in this sprint, grouped by type, using live data from the database. It includes filters for the period and the number of results (Top N), and it refreshes automatically every 60 seconds.
How to open it: first run the backend, then open this link in the browser:
http://localhost:8080/dashboard.html
Type 2: BQ 3 and BQ 5
Type 3: BQ 7
Type 4: BQ 11 and BQ 12
Kanban boards and issues
Each repository has its own GitHub Projects board. Every task is an issue, every issue is shown as a card on the board, and the card moves across the board as the work progresses. All issues are closed.
Milestones
Milestones group the issues by required feature:
- Backend (9): Backend Foundation, Sensor & Location, Authentication Function, Google Calendar Integration, Smart Recommendations, Context-Aware Nearby Friends, BQ Grupal Type 2 Open Table Abandonment Analytics, Individual Business Questions (BQ) and DASHBOARD, and Implement design patterns (individuals).
- Flutter and Kotlin (7 each, same names): Frontend Base, Frontend Sensor & Location, Front Auth Implementation and Backend Connection, Front Google External API implementation and Backend Connection, Smart Feature Implementation and Backend Connection, Context Aware Function Front Implementation And Backend Conection, and BQ Type 2 Grupal Function Frontend Implementation and Backend Conection.
Branches, pull requests and commits
Backend. Each person worked on one branch per task, such as Sofia-Morato-Adapter-Pattern or MIGUEL-BQ5-INDIVIDUAL, and brought the work into main through a pull request. In 41 of the 42 pull requests, a teammate other than the author merged it, so almost every change was reviewed by a second person. Commit messages start with the phase (Base:, Sensor:, Auth:, Google:…) or the task name, and the latest ones use feat: and fix:.
Flutter. Joel, Miguel and Sofía Morato each have seven branches, one per phase: base, sensor, auth, Google, smart feature, context aware and BQ (for example front-sofia-context). This way each person built a complete part of the app and contributed to every required feature. Each commit describes what it adds, such as "Add current location banner with building change detection".
Kotlin.
| Sofía | Daniela |
|---|---|
| 1. Domain Models and Enums | 1. REpositories REST and GeoProbe |
| 2. Welcome, registration and login | 2. Home Container and Eyelash Utilities |
| 3. Location and Interest Permit | 3. Schedule and profile tabs |
| 4. Timetable and Google Calendar | 4. Friends and match tabs |
| 5. Open Tables Components | 5. Match Components |
| 6. Map of open tables | 6. Search and match candidate |
| 7. Create open table | 7. Wait, invite and match confirmation |
| 8. Detail and my open tables | 8. Chat |
Sofía Arias and Daniela Martínez each have three branches that build on each other in order:
- Sofía Arias (
sofiafront→sofia-nuevo-backend→sofia-navegacion-amigos-cercanos): base structure, domain models, onboarding, Open Tables, Google Calendar schedule import, and navigation with the nearby friends alert. - Daniela Martínez (
danielafront→daniela-home→daniela-match): repositories and the GPS probe, the home tabs (schedule, friends, match and recommendations), and the match flow from search to confirmation.
Each commit describes what it adds. We use merge commits so the full history of every branch is kept.
Kanban boards
We use one Kanban board per repository:
| Board | Link |
|---|---|
| Backend | GapFinder Backend board |
| Flutter frontend | GapFinder Dart board |
| Kotlin frontend | GapFinder Kotlin board |