Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
193c1cc
create empty base drf project
Mxbely Dec 22, 2024
aa01569
add drf-spectacular and accounts app
Mxbely Dec 22, 2024
6c24cbd
add accounts app and jwt authentication
Mxbely Dec 22, 2024
28e459f
add register user and serializer
Mxbely Dec 22, 2024
2050591
create all planetarium models
Mxbely Dec 22, 2024
8befbda
create serializers for all planetarium models
Mxbely Dec 22, 2024
ecae97e
create views and urls for all models
Mxbely Dec 22, 2024
eca4f84
upgrade serializers, add list, retrieve serializers, add related name…
Mxbely Dec 23, 2024
daa82ee
add debug toolbar, fix n+1 problem
Mxbely Dec 23, 2024
a05b85a
refactor user model to use email for authentication
Mxbely Dec 23, 2024
aae1e5c
refactor user model to use uuid
Mxbely Dec 23, 2024
aae871a
Refactor search, validation, and API schema.
Mxbely Dec 23, 2024
a9f8630
add custom permissions, paginations and throttling
Mxbely Dec 24, 2024
e4a075c
add docker integration and caching
Mxbely Dec 24, 2024
2efa772
fix security key and add some first tests
Mxbely Dec 24, 2024
4391371
refactor project structere, rewrite tests, add filtering for the tick…
Mxbely Dec 26, 2024
d391787
update readme.md and redis host
Mxbely Dec 26, 2024
b4e8bac
add tests for accounts app, delete comments, add __str__ to ticket mo…
Mxbely Dec 26, 2024
c0ff341
implement jazmin, implement postgresql insted of sqlite3, update migr…
Mxbely Dec 27, 2024
ec25ab1
update readme.md for postgresql and features
Mxbely Dec 27, 2024
7d3cb1d
rm docker.readme, add isort, fix imports, add healthchecker for db, c…
Mxbely Dec 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Include any files or directories that you don't want to be copied to your
# container here (e.g., local build artifacts, temporary files, etc.).
#
# For more help, visit the .dockerignore file reference guide at
# https://docs.docker.com/go/build-context-dockerignore/

**/.DS_Store
**/__pycache__
**/.venv
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/bin
**/charts
**/docker-compose*
**/compose.y*ml
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
10 changes: 10 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
SECRET_KEY=SECRET_KEY
REDIS_URL=REDIS_URL
PGDATA=PGDATA

# DB
POSTGRES_DB=POSTGRES_DB
POSTGRES_USER=POSTGRES_USER
POSTGRES_PASSWORD=POSTGRES_PASSWORD
POSTGRES_HOST=POSTGRES_HOST
POSTGRES_PORT=POSTGRES_PORT
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.idea/
.venv
.vscode
db.sqlite3
__pycache__
.env
7 changes: 7 additions & 0 deletions .isort.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[settings]
profile = pycharm
line_length = 88
multi_line_output = 3
include_trailing_comma = true
force_grid_wrap = 0
combine_as_imports = true
22 changes: 22 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM python:3.12-alpine

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

WORKDIR /app

COPY requirements.txt requirements.txt
RUN pip install --upgrade pip
RUN pip install -r requirements.txt

COPY . .

RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
appuser

USER appuser
20 changes: 20 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.PHONY: test run migrate docker docker-superuser docker-test

test:
python manage.py test

run:
python manage.py runserver

migrate:
python manage.py makemigrations
python manage.py migrate

docker:
docker-compose up --build

docker-superuser:
docker-compose run app sh -c "python manage.py createsuperuser"

docker-test:
docker-compose run app sh -c "python manage.py test"
119 changes: 118 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,118 @@
# planetarium-api-service
# Planetarium API Service

The Planetarium API Service is a Django REST Framework-based application that allows managing planetarium show bookings,
ticket reservations, and viewing information about astronomy shows, available domes, and show themes.

## Key Features

- Manage show themes.
- View, create, update, and delete astronomy shows.
- Manage planetarium domes (seats, halls, etc.).
- Advanced caching using **Redis**.
- Ticket reservation system for shows.
- Authentication using **JWT tokens**.
- Support for custom user model with `UUID` as the primary key.

## Prerequisites

Make sure you have the following tools installed on your system:

- Docker and Docker Compose
- Python 3.12+
- Redis for caching
- PostgreSQL

## Running the Project with Docker-Compose

1. Clone the repository:
```bash
git clone <repository-url>
cd planetarium-api-service
```

2. Create a `.env` file in the root directory of the project:
```env
SECRET_KEY='<your-django-secret-key>'
```

3. Start the application using Docker-Compose:
```bash
make docker
```
or
```bash
docker-compose up --build
```

4. All API caching is managed using **Redis**. Configure `REDIS_URL` in the `.env` file:

``` env
REDIS_URL=redis://redis:6379/0
```

Redis is automatically started via `docker-compose`.

5. The API will be available at: [http://localhost:8000](http://localhost:8000).

### Docker-Compose Configuration

- **app**: The Django application.
- **redis**: Caching service for API responses.
- **redisinsight**: A tool for monitoring Redis.

### Database Management

The project uses **PostgreSQL** as the database backend,
configured automatically with `docker-compose.yaml`.

## User Management

### Creating a Superuser

To access the admin panel:

```bash
make docker-superuser
```

You can then access the admin panel at: [http://localhost:8000/admin](http://localhost:8000/admin).

## API Documentation

The project provides interactive API documentation using `drf-spectacular`:

- Swagger: [http://localhost:8000/api/schema/swagger/](http://localhost:8000/api/schema/swagger/)
- Redoc: [http://localhost:8000/api/schema/redoc/](http://localhost:8000/api/schema/redoc/)

## Testing

Unit tests cover the core API logic. To run tests, execute:

```bash
make docker-test
```

## Project Structure

- **planetarium/**
- API logic for managing planetarium shows, domes, and tickets.
- Serializers, models, and views.
- **accounts/**
- Custom user model implementation based on email authentication.

## Key Dependencies

- Django==5.1.4
- Django REST Framework
- Django Filters
- Redis
- drf-spectacular
- django-debug-toolbar
- psycopg2-binary
- JWT (JSON Web Token)

## Future Enhancements

- Add Celery for background tasks like delayed booking confirmations.
- Real-time WebSocket Updates
- Payment Management
Empty file added accounts/__init__.py
Empty file.
38 changes: 38 additions & 0 deletions accounts/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin
from django.utils.translation import gettext_lazy as _

from accounts.models import User


@admin.register(User)
class UserAdmin(DjangoUserAdmin):
fieldsets = (
(None, {"fields": ("email", "password")}),
(_("Personal info"), {"fields": ("first_name", "last_name")}),
(
_("Permissions"),
{
"fields": (
"is_active",
"is_staff",
"is_superuser",
"groups",
"user_permissions",
)
},
),
(_("Important dates"), {"fields": ("last_login", "date_joined")}),
)
add_fieldsets = (
(
None,
{
"classes": ("wide",),
"fields": ("email", "password1", "password2"),
},
),
)
list_display = ("email", "first_name", "last_name", "is_staff")
search_fields = ("email", "first_name", "last_name")
ordering = ("email",)
6 changes: 6 additions & 0 deletions accounts/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class AccountsConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "accounts"
117 changes: 117 additions & 0 deletions accounts/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Generated by Django 5.1.4 on 2024-12-27 14:47

import uuid

import django.utils.timezone
from django.db import migrations, models

import accounts.models


class Migration(migrations.Migration):

initial = True

dependencies = [
("auth", "0012_alter_user_first_name_max_length"),
]

operations = [
migrations.CreateModel(
name="User",
fields=[
("password", models.CharField(max_length=128, verbose_name="password")),
(
"last_login",
models.DateTimeField(
blank=True, null=True, verbose_name="last login"
),
),
(
"is_superuser",
models.BooleanField(
default=False,
help_text="Designates that this user has all permissions without explicitly assigning them.",
verbose_name="superuser status",
),
),
(
"first_name",
models.CharField(
blank=True, max_length=150, verbose_name="first name"
),
),
(
"last_name",
models.CharField(
blank=True, max_length=150, verbose_name="last name"
),
),
(
"is_staff",
models.BooleanField(
default=False,
help_text="Designates whether the user can log into this admin site.",
verbose_name="staff status",
),
),
(
"is_active",
models.BooleanField(
default=True,
help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
verbose_name="active",
),
),
(
"date_joined",
models.DateTimeField(
default=django.utils.timezone.now, verbose_name="date joined"
),
),
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
(
"email",
models.EmailField(
max_length=254, unique=True, verbose_name="email address"
),
),
(
"groups",
models.ManyToManyField(
blank=True,
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
related_name="user_set",
related_query_name="user",
to="auth.group",
verbose_name="groups",
),
),
(
"user_permissions",
models.ManyToManyField(
blank=True,
help_text="Specific permissions for this user.",
related_name="user_set",
related_query_name="user",
to="auth.permission",
verbose_name="user permissions",
),
),
],
options={
"abstract": False,
},
managers=[
("objects", accounts.models.UserManager()),
],
),
]
Empty file added accounts/migrations/__init__.py
Empty file.
Loading