- Installation guide
- Windows
- Linux
- API Endpoints
- City Weather
- User
- Token authorization
- Swagger
- Celery
- Run scheduled tasks
- Run basic tasks
- Add new scheduled tasks
- Technical decisions
-
Install Python 3.8 , PostgreSQL , Erlang for rabbitmq , RabbitMQ
- Start RabbitMQ:
- Navigate to
C:\Program Files\RabbitMQ Server\rabbitmq_server-3.10.5\sbin
- Start
rabbitmq-server.bat
as Administrator
- Navigate to
- Start RabbitMQ:
-
If you haven't already clone the repository and
cd
into itgit clone https://github.com/antonarnaudov/weather_api.git cd weather_api
-
Create local database named
weather-api-db
using pgAdmin or SQL Shell (psql)
NOTE: Make sure you set proper credentials inDATABASE
settings -
Create
venv
by running:python -m venv venv
-
Activate
venv
in:- cmd by running:
call venv/scripts/activate
- Powershell by running:
& venv/scripts/activate
- cmd by running:
-
Install all dependencies by running:
pip install -r requirements.txt
-
Run migrations with:
python manage.py migrate
-
Try starting the app with:
python manage.py runserver
-
Try running the tests:
python manage.py test
-
Install PostgreSQL
Python3.8:sudo apt-get install python3.8
RabbitMQ:
sudo apt-get install rabbitmq-server sudo systemctl enable rabbitmq-server sudo systemctl start rabbitmq-server
Optional (to make sure it works):
systemctl status rabbitmq-server
-
If you haven't already clone the repository and
cd
into itgit clone https://github.com/antonarnaudov/weather_api.git cd weather_api
-
Create local database named
weather-api-db
using pgAdmin or SQL Shell (psql)
NOTE: Make sure you set proper credentials inDATABASE
settings -
Create
venv
by running:python3 -m venv venv
-
Activate
venv
by runningsource venv/bin/activate
-
Install all dependencies with:
pip install -r requirements.txt
-
Run migrations with:
python3 manage.py migrate
-
Try starting the app with:
python3 manage.py runserver
-
Try running the tests:
python3 manage.py test
Provides access to weather data for all cities
Read Only: api/cityweather/
Detailed: api/cityweather/{id}/
Search by city: api/cityweather/?city=sofia
Provides endpoints for user profile management
CRUD: api/auth/user/
{
"id": 1,
"extended": {
"id": 1,
"phone": "0888666754"
},
"last_login": null,
"is_superuser": true,
"username": "admin",
"first_name": "Admin",
"last_name": "Adminov",
"email": "admin@cityweather.com",
"is_staff": true,
"is_active": true,
"date_joined": "2022-07-09T18:53:58.243512Z",
"groups": [],
"user_permissions": []
}
Search: api/auth/user/?search=admin
Search fields: first_name
, last_name
, email
, username
Register Users on POST
Required fields on POST: username
, password
, password2
, email
Takes a set of user credentials and returns an access and refresh JSON web
token pair to prove the authentication of those credentials.
POST: api/auth/login/
Expects:
{
"username": "admin",
"password": "1234"
}
Returns:
{
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTY1NzY1NTA2NywiaWF0IjoxNjU3NTY4NjY3LCJqdGkiOiIxOTg4NWE1OGIzNTk0NzJiYjI2NmNjY2FjZGZmN2JkNyIsInVzZXJfaWQiOjF9.7zKhghN_UgQQW_7QnjfMckUOfaE3txwYnoXvEVoZQ8E",
"access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjU3NTY4OTY3LCJpYXQiOjE2NTc1Njg2NjcsImp0aSI6ImVjZTQ5YjdiZTdkNzQ2Y2NhZjlhNWEwZTNlOWQ4MjhjIiwidXNlcl9pZCI6MX0.HqXlOh1RrOxocD2kmpUJy2cqH1lZagtwCl-iJSsm4ro"
}
POST: api/auth/refresh/
Expects:
{
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTY1NzY1NTA2NywiaWF0IjoxNjU3NTY4NjY3LCJqdGkiOiIxOTg4NWE1OGIzNTk0NzJiYjI2NmNjY2FjZGZmN2JkNyIsInVzZXJfaWQiOjF9.7zKhghN_UgQQW_7QnjfMckUOfaE3txwYnoXvEVoZQ8E"
}
Returns:
{
"access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjU3NTY5MTIwLCJpYXQiOjE2NTc1Njg2NjcsImp0aSI6IjRlZThkZTk5ZDYzYzRiNjdiODAyYmU3ODhlZGY0ZWFjIiwidXNlcl9pZCI6MX0.6i0qPK0KLjRwFMkoO6R-s5IrWBImT5Rt86Xu6GntQc8"
}
To use the access token in request, simply add it to request headers as: Authorization: Bearer ${access}
Token Lifespan:
- Access token lifespan is 5 minutes
- Refresh token lifespan is 1 day
- To retain an ongoing session you should refresh the access token on every request
Auto-generated documentation for all accessible endpoints
GET: api/swagger/
Make sure you have started the RabbitMQ Server
NOTE: You can skip those steps if you've already done them
On Linux you should execute the following commands:
sudo systemctl enable rabbitmq-server
sudo systemctl start rabbitmq-server
On Windows you should:
- Navigate to
C:\Program Files\RabbitMQ Server\rabbitmq_server-3.10.5\sbin
- Start
rabbitmq-server.bat
as Administrator
Open any terminal and execute this line to start the celery workers:
For Windows:
celery -A weather_api worker -l info --pool=solo
For Linux:
celery -A weather_api worker -l info
In a separate terminal run this line to start the celery beat and all scheduled tasks:
celery -A weather_api beat -l INFO --scheduler django_celery_beat.schedulers:DatabaseScheduler
Open any terminal and execute this line to start the celery workers:
For Windows:
celery -A weather_api worker -l info --pool=solo
For Linux:
celery -A weather_api worker -l info
NOTE: Go to weather_api > celery
and change update_city_weather_data_per_day
schedule to 60 seconds to see instant result.
- Create
@app.task
decorated function in any file or app- Import
app
fromweather_api.celery
- Import
- Open
weather_api > celery.py
and register your worker insideapp.conf.beat_schedule
following this structure
"example_worker_runs_every_minute": {
"task": "app.file.worker_function",
"schedule": 60.0 # schedule expects seconds
}
-
I used RabbitMQ instead of Redis for message broker because I aimed to make the project executable on both Windows and Unix based OS. Redis requires a WSL to run under windows and RabbitMQ can work directly under it, although it requires to manually start the server.
-
I used a custom filter instead of overriding the list method for the search by city, thus keeping the ViewSet super clean and simple
-
I used JsonFiled for storing the third party api data, which worked really nice with the Django ORM, allowing me to tap into the nested fields as if they were written in the model
-
I used created_at/updated_at DateTime fields to keep track of time ;-D, so I know when to refresh the data
-
I extended the user model (although it wasn't necessary) as a matter of good practice, because its nightmare to do it later in the development process, when the app expands
-
I moved all non-weather specific functionality in app named core, and all utility functions in core > utils
-
I added a custom mixin which allows me to dynamically change the serializer, based on the request type
-
The default pagination class is overridden, so it utilizes size parameter, for dynamic single page pagination
-
I added a custom exception handler, to minimize the chance for Server Error 500 response
-
All Open Weather Map API credentials are set as settings
-
Added a blank page on base rout, so it does not throw errors
-
I used PR-s for these projects and did not delete the branches, so you can track the full development history
-
I used WritableNestedSerializers to create Extended objects from the Users serializer
-
I provided console logging on the celery task which logs the time of execution and the next time of execution
-
I added a few model and api tests, for the user, token authentication and city weather using django tests and django-factory_boy