Feature: Visual Timeline View
Relevant source files
The following files were used as context for generating this wiki page:
The Visual Timeline View is a core feature of the Campuspal Room Finder application, designed to provide users with an at-a-glance understanding of room availability. Instead of a simple text-based list, it presents a horizontal timeline for each room, showing booked (red) and free (green) slots throughout the day. This allows users to easily spot open periods and understand the daily schedule of all rooms in a selected building.
This feature combines powerful backend data processing with a dynamic, AJAX-powered frontend. When a user searches for a specific time, the system categorizes rooms into "Available" and "Unavailable" groups, which are presented in a clean, tabbed interface. The timeline is interactive; clicking on a room's timeline opens a modal with detailed information about its schedule and amenities.
Sources: README.md:21-26, finder/views.py:38-100
Architecture and Data Flow
The timeline feature is driven by a client-server architecture. The user initiates a search from the browser, which sends an AJAX request to the Django backend. The backend processes the request, generates a detailed timeline data structure for each relevant room, and returns a rendered HTML partial. The frontend then injects this HTML into the page and attaches event listeners for interactivity.
The following diagram illustrates the high-level data flow for a typical search.
This diagram shows the flow from a user's search submission to the rendering of the timeline and the subsequent interaction with the room details modal.
Sources: finder/views.py:18-111, finder/static/finder/js/scripts.js:5-32
Backend Logic
The backend logic resides primarily in the search_rooms function within finder/views.py. This function is responsible for handling search requests, processing data, and preparing it for the frontend.
Sources: finder/views.py
Timeline Generation
For each room matching the search criteria (building), the backend constructs a timeline list. This process involves:
- Setting an initial
last_end_timeto the start of the day (7:00 AM). - Iterating through all scheduled classes for the room on the selected day.
- For each gap between classes, a "free" block is created.
- For each class, a "booked" block is created.
- A final "free" block is added to fill the time from the last class until the end of the day (11:00 PM).
Each block in the timeline is a dictionary containing its type, start/end times, and course information if applicable.
Sources: finder/views.py:43-58
Positional Calculation
A critical step is the calculation of start_percentage and width_percentage for each timeline block. These values are used directly by the frontend CSS to position and size the blocks correctly within the timeline container.
The calculation is based on the total duration of the displayable day (7:00 AM to 11:00 PM, which is 16 hours).
start_percentage: The percentage of the total day duration that has passed before the block begins.width_percentage: The percentage of the total day duration that the block itself occupies.
# finder/views.py:93-96
total_duration = (datetime.datetime.combine(datetime.date.min, datetime.time(23,0)) - datetime.datetime.combine(datetime.date.min, datetime.time(7,0))).total_seconds()
block['start_percentage'] = ((datetime.datetime.combine(datetime.date.min, block['start']) - datetime.datetime.combine(datetime.date.min, datetime.time(7,0))).total_seconds() / total_duration) * 100
block['width_percentage'] = ((datetime.datetime.combine(datetime.date.min, block['end']) - datetime.datetime.combine(datetime.date.min, block['start'])).total_seconds() / total_duration) * 100
Sources: finder/views.py:93-96
Timeline Block Data Structure
The timeline list passed to the template for each room contains blocks with the following structure:
| Key | Type | Description |
|---|---|---|
type | string | "free" or "booked". |
start | datetime.time | The start time of the block (Python object). |
end | datetime.time | The end time of the block (Python object). |
course | string | The course name if the block is "booked". None otherwise. |
start_str | string | The start time formatted as HH:MM. |
end_str | string | The end time formatted as HH:MM. |
start_percentage | float | The CSS left percentage for positioning. |
width_percentage | float | The CSS width percentage for sizing. |
Sources: finder/views.py:48-58, finder/views.py:91-96
Room Categorization
If the user provides a specific time and duration in the search form, the backend categorizes the rooms:
available_rooms: Contains rooms that have a single "free" block large enough to contain the entire requested time slot.unavailable_rooms: Contains all other rooms.
If no time is specified, all rooms are placed in the unavailable_rooms list for general browsing. This separation of data powers the "Available" and "Unavailable" tabs on the frontend.
Sources: finder/views.py:63-81
Frontend Rendering
The visual representation of the timeline is achieved through a combination of Django templates and CSS. The backend passes the processed data to the templates, which generate the necessary HTML structure.
Template Structure
The main search page, search.html, contains a div with the ID results-container. This container is initially populated on page load and is the target for AJAX updates.
The core rendering logic for a single room's timeline is in timeline_row.html. It iterates through the timeline blocks for a given room and creates a div for each one. The inline style attribute is dynamically populated with the start_percentage and width_percentage values calculated by the backend.
<!-- finder/templates/finder/timeline_row.html:6-11 -->
{% for block in room_item.timeline %}
<div class="timeline-block {{ block.type }}"
style="left: {{ block.start_percentage }}%; width: {{ block.width_percentage }}%;"
title="{{ block.start_str }} - {{ block.end_str }}{% if block.course %}: {{ block.course }}{% endif %}">
</div>
{% endfor %}
Sources: finder/templates/finder/search.html:51-57, finder/templates/finder/timeline_row.html:1-13
Styling (CSS)
The appearance of the timeline is controlled by styles.css. It defines the layout of the rows and the visual distinction between free and booked time slots.
| Class | Description |
|---|---|
.timeline-row | A container for a single room's information and its timeline. It acts as the clickable element to open the details modal. |
.timeline | The main bar that represents the full day. It acts as a positioning context for the blocks and has a faint grid background to represent time intervals. |
.timeline-block | The base class for an individual time segment. It is positioned absolutely within the .timeline container. |
.timeline-block.free | Styles a free block with a green gradient and a subtle, animated stripe pattern. |
.timeline-block.booked | Styles a booked block with a red gradient and a dark stripe pattern. |
/* finder/static/finder/css/styles.css:300-313 */
.timeline-block.free {
background: linear-gradient(135deg, rgba(16,185,129,.2), rgba(16,185,129,.35));
background-image: linear-gradient(45deg, rgba(255,255,255,.18) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.18) 50%, rgba(255,255,255,.18) 75%, transparent 75%, transparent);
background-size: 12px 12px;
animation: stripe-animate 1.6s linear infinite;
}
.timeline-block.booked {
background: linear-gradient(135deg, rgba(239,68,68,.18), rgba(239,68,68,.3));
background-image: linear-gradient(45deg, rgba(0,0,0,.08) 25%, transparent 25%, transparent 50%, rgba(0,0,0,.08) 50%, rgba(0,0,0,.08) 75%, transparent 75%, transparent);
background-size: 12px 12px;
}
Sources: finder/static/finder/css/styles.css:281-319
Client-Side Interaction
Dynamic functionality is handled by finder/static/finder/js/scripts.js, which manages AJAX requests and UI updates without requiring full page reloads.
AJAX Search
When the search form is submitted, the script prevents the default browser action and sends the form data via an AJAX fetch request. The backend responds with the rendered timeline_partial.html, which the script then injects into the #results-container.
This diagram shows the sequence of an AJAX search.
Room Details Modal
The script uses event delegation on the #results-container to listen for clicks on any element with the class .timeline-row.
- When a click is detected, it extracts the
data-room-idfrom the clicked row. - It makes an AJAX
GETrequest to the/api/room/<room_id>/endpoint, passing the current day as a query parameter. - The API returns a JSON object with the room's details and its full daily schedule.
- The script dynamically builds the HTML for the modal content and displays the modal.
Sources: finder/static/finder/js/scripts.js:35-64
Tab Toggling
The script also uses event delegation to handle clicks on the "Available" and "Unavailable" tabs (.tab-link). It toggles the .active class on the tab links and the corresponding content panes (.tab-content) to show and hide the relevant room lists. This is a purely client-side operation for instant feedback.