Sprint 2 ‐ Deliverable - GapFinder-Group21/GapFinderBackend GitHub Wiki

Sprint 2 - Deliverable

Table of Contents

  1. Business Questions
  2. Analytics Pipeline
  3. Architectural Design
  4. Implemented Features
  5. Implemented Views
  6. Ethics Video
  7. Repository Evidence and Collaboration
  8. 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


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

General analytics pipeline

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

BQ 5 pipeline

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

BQ 7 pipeline

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

BQ 11 pipeline

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

BQ 12 pipeline

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

BQ 13 pipeline

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

Open Table abandonment pipeline

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 diagram

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

Deployment diagram with Flutter and Kotlin apps

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

Domain model

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

Authentication flow

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

Location flow

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

Open Table abandonment flow

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

Layered – Sofía Morato

Diagram file: diagramas-daniela.drawio · Page: 2. Layered (UML packages) · PNG filename: layered-daniela.png

Layered – Daniela Martínez

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

Client-Server – security and deployment (Joel Niño)

Diagram file: diagramas-sofia-arias.drawio · Page: 2. Client-Server (UML component) · PNG filename: client-server-arias.png

Client-Server – REST resources (Sofía Arias)

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

Event-driven – notifications

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

Frontend architecture (Flutter)

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

Frontend architecture (Kotlin)

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

Adapter – class diagram

Diagram file: diagramas-sofia-morato.drawio · Page: 4. Adapter (UML sequence) · PNG filename: adapter-sequence.png

Adapter – sequence diagram

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

Strategy – class diagram

Diagram file: diagramas-joel.drawio · Page: 4. Strategy (UML sequence) · PNG filename: strategy-sequence.png

Strategy – sequence diagram

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

Observer – class diagram

Diagram file: diagramas-miguel-cabrera.drawio · Page: 4. Observer (UML sequence) · PNG filename: observer-sequence.png

Observer – sequence diagram

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

Builder – class diagram

Diagram file: diagramas-sofia-arias.drawio · Page: 4. Builder (UML sequence) · PNG filename: builder-sequence.png

Builder – sequence diagram

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

Facade – class diagram

Diagram file: diagramas-daniela.drawio · Page: 4. Facade (UML sequence) · PNG filename: facade-sequence.png

Facade – sequence diagram

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