-
Notifications
You must be signed in to change notification settings - Fork 0
Issue 7 Add room app #52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8da87e8
3d429db
a7d522c
805e9bd
94c9cf2
63aac92
5138bab
40e818f
541e107
adc116b
4ac62fd
4e29146
3609faa
17a7c6e
aca4760
08e9e08
01769cd
62315a0
490d725
dd19130
f04b843
48d8380
eba808c
97600de
30d4054
640f538
de6560f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| # Room API Endpoints | ||
|
|
||
| ## Authentication | ||
|
|
||
| - **Read (GET):** Anyone can list or retrieve rooms. | ||
| - **Write (POST, PATCH, PUT):** Only authenticated users can create or update rooms. | ||
| - **Delete:** Not allowed (returns 405). | ||
|
|
||
| --- | ||
|
|
||
| ## List Rooms | ||
|
|
||
| **GET** `/api/rooms/` | ||
|
|
||
| ### Query Parameters (all optional): | ||
|
|
||
| - `name`: Filter rooms by name (case-insensitive, partial match). | ||
| - `location`: Filter by location name (case-insensitive, partial match). | ||
| - `min_capacity`: Minimum capacity (integer). | ||
| - `max_capacity`: Maximum capacity (integer). | ||
| - `min_datetime`: Start datetime after or equal to (ISO 8601). | ||
| - `max_datetime`: End datetime before or equal to (ISO 8601). | ||
| - `amenity`: Filter by amenity name(s). Comma-separated for multiple. | ||
| Example: `?amenity=Projector,Whiteboard` | ||
|
|
||
| **Pagination:** | ||
| Results are paginated (10 per page by default). Use `?page=2` for next page. | ||
|
|
||
| **Example Request:** | ||
|
|
||
| ``` | ||
| GET /api/rooms/?name=meeting&location=Main&min_capacity=5&amenity=Projector,Whiteboard | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Retrieve Room | ||
|
|
||
| **GET** `/api/rooms/{id}/` | ||
|
|
||
| Returns details for a single room. | ||
|
|
||
| --- | ||
|
|
||
| ## Create Room | ||
|
|
||
| **POST** `/api/rooms/` | ||
| **Auth required** | ||
|
|
||
| **Body Example:** | ||
|
|
||
| ```json | ||
| { | ||
| "name": "Conference Room", | ||
| "location": 1, | ||
| "capacity": 20, | ||
| "amenities_id": [1, 2], | ||
| "start_datetime": "2025-12-11T09:00:00Z", | ||
| "end_datetime": "2025-12-11T18:00:00Z", | ||
| "recurrence_rule": "FREQ=DAILY;BYDAY=MO,TU,WE", | ||
| "is_active": true | ||
| } | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Update Room | ||
|
|
||
| **PATCH/PUT** `/api/rooms/{id}/` | ||
| **Auth required** | ||
|
|
||
| **Body:** Same as create. Partial updates allowed. | ||
|
|
||
| --- | ||
|
|
||
| ## Delete Room | ||
|
|
||
| **DELETE** `/api/rooms/{id}/` | ||
| **Not allowed** (returns 405 Method Not Allowed). | ||
|
|
||
| --- | ||
|
|
||
| ## Notes | ||
|
|
||
| - Unauthenticated users only see rooms where `is_active=true`. | ||
| - A room cannot be deleted but you can change its `is_active` | ||
| - Filtering by multiple amenities returns rooms that have **all** specified amenities. | ||
| - Validation: `end_datetime` must be after `start_datetime`. | ||
| - Validation: `recurrence_rule` must start with FREQ= and contain valid frequency | ||
|
|
||
| --- | ||
|
|
||
| ## Related Endpoints | ||
|
|
||
| - **Locations:** `/api/locations/` | ||
| - **Amenities:** `/api/amenities/` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| from django.contrib import admin | ||
| from .models import Room, Location, Amenity | ||
| # Register your models here. | ||
|
|
||
|
|
||
| @admin.register(Room) | ||
| class RoomAdmin(admin.ModelAdmin): | ||
| list_display = ("id", "name", "location", | ||
| "start_datetime", "end_datetime", "recurrence_rule", "is_active") | ||
| search_fields = ("name", "location__name", "amenities__name") | ||
| list_display_links = ("name",) | ||
|
|
||
|
|
||
| @admin.register(Location) | ||
| class LocationAdmin(admin.ModelAdmin): | ||
| list_display = ("id", "name") | ||
| search_fields = ("name",) | ||
| list_display_links = ("name",) | ||
|
|
||
|
|
||
| @admin.register(Amenity) | ||
| class AmenitiesAdmin(admin.ModelAdmin): | ||
| list_display = ("id", "name") | ||
| search_fields = ("name",) | ||
| list_display_links = ("name",) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| from django.apps import AppConfig | ||
|
|
||
|
|
||
| class RoomConfig(AppConfig): | ||
| default_auto_field = "django.db.models.BigAutoField" | ||
| name = "api.room" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| # Generated by Django 5.2.9 on 2025-12-10 13:13 | ||
|
|
||
| import django.db.models.deletion | ||
| from django.db import migrations, models | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| initial = True | ||
|
|
||
| dependencies = [] | ||
|
|
||
| operations = [ | ||
| migrations.CreateModel( | ||
| name="Amenities", | ||
| fields=[ | ||
| ("id", models.AutoField(primary_key=True, serialize=False)), | ||
| ("name", models.CharField(max_length=32)), | ||
| ], | ||
| ), | ||
| migrations.CreateModel( | ||
| name="Location", | ||
| fields=[ | ||
| ("id", models.AutoField(primary_key=True, serialize=False)), | ||
| ("name", models.CharField(max_length=64)), | ||
| ], | ||
| ), | ||
| migrations.CreateModel( | ||
| name="Room", | ||
| fields=[ | ||
| ("id", models.AutoField(primary_key=True, serialize=False)), | ||
| ("name", models.CharField(max_length=32)), | ||
| ("img", models.ImageField(upload_to="room_images/")), | ||
| ("capacity", models.IntegerField()), | ||
| ("is_active", models.BooleanField(default=True)), | ||
| ("start_datetime", models.DateTimeField()), | ||
| ("end_datetime", models.DateTimeField()), | ||
| ("recurrence_rule", models.CharField(blank=True, max_length=64)), | ||
| ("created_at", models.DateTimeField(auto_now_add=True)), | ||
| ("updated_at", models.DateTimeField(auto_now=True)), | ||
| ("amenities", models.ManyToManyField(blank=True, to="room.amenities")), | ||
| ( | ||
| "location", | ||
| models.ForeignKey( | ||
| on_delete=django.db.models.deletion.CASCADE, to="room.location" | ||
| ), | ||
| ), | ||
| ], | ||
| ), | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| # Generated by Django 5.2.9 on 2025-12-10 13:48 | ||
|
|
||
| import django.core.validators | ||
| from django.db import migrations, models | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| dependencies = [ | ||
| ("room", "0001_initial"), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AlterField( | ||
| model_name="room", | ||
| name="capacity", | ||
| field=models.PositiveIntegerField( | ||
| validators=[django.core.validators.MinValueValidator(1)] | ||
| ), | ||
| ), | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| # Generated by Django 5.2.9 on 2025-12-10 14:59 | ||
|
|
||
| from django.db import migrations, models | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| dependencies = [ | ||
| ("room", "0002_alter_room_capacity"), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AlterField( | ||
| model_name="room", | ||
| name="img", | ||
| field=models.ImageField(blank=True, null=True, upload_to="room_images/"), | ||
| ), | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| # Generated by Django 5.2.9 on 2025-12-10 15:35 | ||
|
|
||
| import django.db.models.deletion | ||
| from django.db import migrations, models | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| dependencies = [ | ||
| ("room", "0003_alter_room_img"), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AlterField( | ||
| model_name="room", | ||
| name="location", | ||
| field=models.ForeignKey( | ||
| on_delete=django.db.models.deletion.PROTECT, to="room.location" | ||
| ), | ||
| ), | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| # Generated by Django 5.2.9 on 2025-12-11 16:24 | ||
|
|
||
| from django.db import migrations | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| dependencies = [ | ||
| ("room", "0004_alter_room_location"), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AlterModelOptions( | ||
| name="amenities", | ||
| options={"ordering": ["id"]}, | ||
| ), | ||
| migrations.AlterModelOptions( | ||
| name="location", | ||
| options={"ordering": ["id"]}, | ||
| ), | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # Generated by Django 5.2.9 on 2025-12-11 16:40 | ||
|
|
||
| from django.db import migrations | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| dependencies = [ | ||
| ("room", "0005_alter_amenities_options_alter_location_options"), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.RenameModel( | ||
| old_name="Amenities", | ||
| new_name="Amenity", | ||
| ), | ||
| ] |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. all the TextField need to be replaced with CharField |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,43 @@ | ||||||||||||||||||||||
| from django.db import models | ||||||||||||||||||||||
| from django.core.validators import MinValueValidator | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| class Location(models.Model): | ||||||||||||||||||||||
| id = models.AutoField(primary_key=True) | ||||||||||||||||||||||
|
||||||||||||||||||||||
| name = models.CharField(max_length=64, blank=False) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| class Meta: | ||||||||||||||||||||||
| ordering = ['id'] | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| def __str__(self): | ||||||||||||||||||||||
| return self.name | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| class Amenity(models.Model): | ||||||||||||||||||||||
| id = models.AutoField(primary_key=True) | ||||||||||||||||||||||
| name = models.CharField(max_length=32, blank=False) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| class Meta: | ||||||||||||||||||||||
| ordering = ['id'] | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| def __str__(self): | ||||||||||||||||||||||
| return self.name | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| class Room(models.Model): | ||||||||||||||||||||||
| id = models.AutoField(primary_key=True) | ||||||||||||||||||||||
|
||||||||||||||||||||||
| id = models.AutoField(primary_key=True) |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explicitly defining id as AutoField is unnecessary since Django automatically creates this field. Consider removing this line to reduce redundancy unless there's a specific reason for the explicit declaration.
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The purpose and expected format of start_datetime and end_datetime fields needs documentation. It's unclear whether these represent the availability window for the room, a specific booking, or the room's operating hours. Adding docstrings or field-level help_text would clarify their intended use.
| start_datetime = models.DateTimeField(blank=False) | |
| end_datetime = models.DateTimeField(blank=False) | |
| start_datetime = models.DateTimeField( | |
| blank=False, | |
| help_text="The start of the room's availability window (inclusive). Format: YYYY-MM-DD HH:MM:SS." | |
| ) | |
| end_datetime = models.DateTimeField( | |
| blank=False, | |
| help_text="The end of the room's availability window (exclusive). Format: YYYY-MM-DD HH:MM:SS." | |
| ) |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no validation for the recurrence_rule field to ensure it follows valid iCalendar RRULE syntax. Consider adding validation to prevent invalid recurrence rules from being stored.
Uh oh!
There was an error while loading. Please reload this page.