Skip to content

Repository files navigation

Booking API

A backend for creating accounts and booking taxi rides, with token-based auth and a real-time, distance-based fare calculator. Every endpoint is self-documented through an auto-generated OpenAPI schema.

Stack

Python, Django, Django REST Framework, SQLite, drf-spectacular for schema generation. drf-spectacular docs: https://drf-spectacular.readthedocs.io/en/latest/

Quick start

git clone https://github.com/bobur-yusupov/booking-api.git
cd booking-api
docker compose up --build

The container runs migrations and starts the dev server automatically. Once it's up:

Fallback: run without Docker
pip install -r requirements.txt
python manage.py makemigrations
python manage.py migrate
python manage.py runserver

Architecture

flowchart TD
    Client[Client] -->|HTTP| App

    subgraph Compose["docker compose"]
        App["app container\nDjango + DRF"]
        DB[("db container\nPostgres 15")]
    end

    App -->|"/accounts/*"| Accounts[accounts app\ncustom User + token auth]
    App -->|"/taxi/order/*"| Taxi[taxi app\nOrderTaxi viewset]
    App -->|"/api/schema, /api/docs"| Docs[drf-spectacular]

    Taxi -->|geopy| Nominatim[("Nominatim\nOpenStreetMap geocoding")]
    App -.->|"defined, not wired\nsee issue #6"| DB
    App --> SQLite[("SQLite file")]
Loading

A client signs up and exchanges credentials for a DRF token at /accounts/token/, then sends that token on every subsequent request. TaxiOrderViewSet requires the token, scopes its queryset to request.user, and on read computes each order's price by calling out to geopy, which geocodes both locations against the public Nominatim service and derives a flat-rate fare from the straight-line distance. drf-spectacular introspects the same viewsets and serializers to generate the OpenAPI schema served at /api/docs/. Everything runs in one app container; compose also defines a db Postgres container, but the active settings hardcode SQLite, so that database isn't actually wired into the app yet (tracked in issue #6 below).

Design decisions

Custom User model built on AbstractBaseUser + PermissionsMixin, not AbstractUser. It replaces username with email as USERNAME_FIELD and uses a UUID primary key instead of Django's default auto-incrementing integer. The obvious alternative was subclassing AbstractUser and adding the extra fields on top, keeping Django's default username-based login and integer IDs.

TODO: why email/UUID over extending AbstractUser, and what did the custom manager (create_user/create_superuser) cost you versus the built-in one?

Role flag on the user is set but never actually stored. UserManager.create_user runs user.is_user = True, but User has no is_user field anywhere in the model or migrations — it's a plain Python attribute that disappears the moment the instance is reloaded from the database. There's also no is_taxi flag or driver profile at all, despite the taxi app depending on a rider (user FK) to place orders. Today there is exactly one flat account type. The obvious alternative is a real BooleanField/CharField(choices=...) on the model, or a separate Driver profile linked one-to-one to User.

TODO: why did the role flag stop at an in-memory attribute, and is rider/driver differentiation still coming, or was that dropped?

Fare calculation geocodes on every read, against a public service, with no caching. OrderTaxiSerializer exposes price as a property that calls taxi/functions/measure_length.py's Distance class, which geocodes both from_location and to_location through geopy's Nominatim (OpenStreetMap) on every access, then charges a flat rate off the resulting straight-line distance. The obvious alternative is geocoding once at order creation and storing the coordinates/distance, or using a paid geocoding provider with an SLA instead of the free, rate-limited public endpoint.

TODO: why compute live on every read instead of caching at write time, and has this been tested against Nominatim's usage/rate limits under real traffic?

Testing

Tests exist for accounts and taxi, run with:

python manage.py test

Coverage: user creation (incl. email normalization and superuser creation), the signup/token/profile API endpoints, and Django admin list/edit/add pages for accounts; taxi order creation and its string representation for taxi. Coverage is uneven — taxi/test/test_taxi_booking_api.py::test_orders_by_user is a stub with no assertions yet, and the per-user queryset filtering it names isn't actually tested.

What I'd do differently

  • Wire the db Postgres container into the actual Django settings, or drop it from docker-compose.yml — right now it starts up and does nothing (issue #6).
  • Persist the role flag properly (or remove it) instead of leaving is_user as a non-persisted attribute that a same-request test can't catch.
  • Move fare calculation off the request path — geocode once at creation time instead of on every serialization.

License

MIT — see LICENSE.

About

Booking-API. Simple app help to book appointments.

Topics

Resources

Stars

8 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages