This project provides a simple Web App for a fake wedding car rental company - Carcall. It was created using Flask, SQLAlchemy, Python, REST API, Bootstrap, CSS, JS. App tests were written using the unittest module.
There are four user types on the platform with different permissions:
- anonymous user
- registered user
- moderator
- admin
The operations allowed depending on user permissions:
Anonymous User | Registered User | Moderator | Admin | |
---|---|---|---|---|
Register | ✔️ | ❌ | ❌ | ❌ |
Log in | ❌ | ✔️ | ✔️ | ✔️ |
Add post | ❌ | ❌ | ✔️ | ✔️ |
Comment post | ❌ | ✔️ | ✔️ | ✔️ |
Add car | ❌ | ❌ | ✔️ | ✔️ |
Edit car | ❌ | ❌ | ✔️ | ✔️ |
Rent car | ❌ | ✔️ | ✔️ | ✔️ |
Add opinion | ❌ | ✔️ | ✔️ | ✔️ |
Change data | ❌ | ✔️ | ✔️ | ✔️ |
Change users data | ❌ | ❌ | ❌ | ✔️ |
Delete car rental | ❌ | ❌ | ❌ | ✔️ |
Send message | ✔️ | ✔️ | ✔️ | ✔️ |
The mentioned operations can also be performed by a dedicated REST API.
The client app is programmed using: Python, HTML, CSS, Bootstrap, JS, JQuery.
Register
demo_register.mp4
Rent a car
demo_rent_car.mp4
Add a comment to the post
demo_add_comment.mp4
Add an opinion
demo_add_opinion.mp4
Change data
demo_change_user_data.mp4
Send a message
demo_contact.mp4
Add a car
demo_add_car.mp4
Edit the car
demo_edit_car.mp4
Add a post
demo_add_post.mp4
Change users data
demo_edit_user_admin.mp4
Delete car rental
demo_delete.mp4
Based on Flask (Python).
Routing of the application:
Parameters: id, name, price, year, model, image, rentals_number
Extra Parameters: rentals_url
Headers:
Accept: application/json
Possible Query Params:
sort sort by parameters
parameter is equal (for rentals_number works only without paging)
parameter[filter] filters: [gt],[gte],[lt],[lte],[like] (for rentals_number works only without paging)
params parameters to be removed (parameters and extra parameters)
page page number for paging
per_page number of records on one page for paging (required for paging)
Example Request:
curl --location -g --request GET 'http://127.0.0.1:5000//api/v1/cars/?params=image,name,rentals_url,price&id[lte]=17&sort=-year,-id&rentals_number=2&id[lt]=5' \
--header 'Accept: application/json'
Example Response:
{
"data": [
{
"id": 3,
"model": "everything",
"rentals_number": 2,
"year": 1999
},
{
"id": 2,
"model": "administration",
"rentals_number": 2,
"year": 1983
}
],
"number_of_records": 2,
"success": true
/api/v1/cars/{{car_id}}/ Get single car
Parameters: id, name, price, year, model, image, rentals_number
Extra Parameters: rentals_url
Headers:
Accept: application/json
Possible Query Params:
params parameters to be removed (parameters and extra parameters)
Example Request:
curl --location --request GET 'http://127.0.0.1:5000//api/v1/cars/1?params=image,rentals_url' \
--header 'Accept: application/json'
Example Response:
{
"data": {
"id": 1,
"model": "mini",
"name": "baby",
"price": 123.0,
"rentals_number": 4,
"year": 2021
},
"success": true
}
Headers:
Accept: application/json
Content-Type: application/json
Authorization: Bearer {token}
Example Request:
curl --location --request POST 'http://127.0.0.1:5000//api/v1/cars/' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {token}' \
--data-raw '{
"name":"Car name",
"price":20,
"year":2021,
"model":"Car model"
}'
Example Response:
{
"data": {
"id": 27,
"image": "/static/img/no_img.jpg",
"model": "Car model",
"name": "Car name",
"price": 20.0,
"rentals_number": 0,
"rentals_url": [],
"year": 2021
},
"success": true
}
/api/v1/cars/{{car_id}}/ Update car
Headers:
Accept: application/json
Content-Type: application/json
Authorization: Bearer {token}
Example Request:
curl --location --request PUT 'http://127.0.0.1:5000//api/v1/cars/' \
--header 'Authorization: Bearer {token}' \
--header 'Content-Type: application/json' \
--data-raw '{
"car_to_edit_id":27,
"name":"New name",
"price":12,
"year":2000,
"model":"New model"
}'
Example Response:
{
"data": {
"id": 27,
"image": "/static/img/no_img.jpg",
"model": "New model",
"name": "New name",
"price": 12.0,
"rentals_number": 0,
"rentals_url": [],
"year": 2000
},
"success": true
}
Parameters: id, name, surname, email, telephone, address, role_id, rentals_number, post_number, opinions_number,
comments_number
Extra Parameters: rentals, posts, opinions, comments
Headers:
Accept: application/json
Authorization: Bearer {token}
Possible Query Params:
sort sort by parameters
parameter is equal (for parameters with _number works only without paging)
parameter[filter] filters: [gt],[gte],[lt],[lte],[like] (for parameters with _number works only without paging)
params parameters to be removed (parameters and extra parameters)
page page number for paging
per_page number of records on one page for paging (required for paging)
Example Request:
curl --location -g --request GET 'http://127.0.0.1:5000//api/v1/users/?params=comments,posts,rentals,opinions&rentals_number[gte]=4&role_id=1' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer {token}'
Example Response:
{
"data": [
{
"address": "ul kwiatowa 11",
"comments_number": 10,
"email": "cookjose@hoover.com",
"id": 7,
"name": "Jose",
"opinions_number": 1,
"post_number": 2,
"rentals_number": 4,
"role_id": 1,
"surname": "Cook",
"telephone": 444444444
}
],
"number_of_records": 1,
"success": true
}
/api/v1/users/{{user_id}}/ Get single user
Parameters: id, name, surname, email, telephone, address, role_id, rentals_number, post_number, opinions_number,
comments_number
Extra Parameters: rentals, posts, opinions, comments
Headers:
Accept: application/json
Authorization: Bearer {token}
Possible Query Params:
params parameters to be removed (parameters and extra parameters)
Example Request:
curl --location --request GET 'http://127.0.0.1:5000//api/v1/users/7/?params=posts,address,comments,rentals' \
--header 'Authorization: Bearer {token}' \
--header 'Accept: application/json'
Example Response:
{
"data": {
"comments_number": 10,
"email": "cookjose@hoover.com",
"id": 7,
"name": "Jose",
"opinions": [
"/api/v1/opinions/12/"
],
"opinions_number": 1,
"post_number": 2,
"rentals_number": 4,
"role_id": 1,
"surname": "Cook",
"telephone": 444444444
},
"success": true
}
/api/v1/auth/about_me/ Get user data
Headers:
Authorization: Bearer {token}
Example Request:
curl --location --request GET 'http://127.0.0.1:5000//api/v1/auth/about_me/' \
--header 'Authorization: Bearer {token}'
Example Response:
{
"data": {
"address": ul kwiatowa 11,
"email": "cookjose@hoover.com",
"name": "Cook",
"surname": "Jose",
"telephone": 444444444
},
"success": true
}
/api/v1/auth/register/ Register
Headers:
Accept: application/json
Content-Type: application/json
Example Request:
curl --location --request POST 'http://127.0.0.1:5000//api/v1/auth/register/' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data-raw '{
"name":"Your_name",
"surname":"Your_surname",
"email":"your_email@example.com",
"password":"your_password",
"telephone":"your_telephone"
}'
Example Response:
{
"success": true,
"token": "token_value"
}
Headers:
Accept: application/json
Content-Type: application/json
Example Request:
curl --location --request POST 'http://127.0.0.1:5000//api/v1/auth/login/' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data-raw '{
"email":"your_email@example.com",
"password":"your_password"
}'
Example Response:
{
"success": true,
"token": "token_value"
}
/api/v1/auth/user/ Update user
Headers:
Accept: application/json
Content-Type: application/json
Authorization: Bearer {token}
Example Request:
curl --location --request PUT 'http://127.0.0.1:5000//api/v1/auth/user/' \
--header 'Authorization: Bearer {token}' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data-raw '{
"name": "new_name",
"surname": "new_surname",
"address": "new_address",
"telephone": "new_telephone",
"email":"your_new_email@example.com",
"password":"your_password",
"new_password":"your_new_password"
}'
Example Response:
{
"data": {
"address": "new_address",
"email": "your_new_email@example.com",
"name": "new_name",
"surname": "new_surname",
"telephone": "new_telephone"
},
"success": true
}
/api/v1/cars/{{car_id}} Update user by admin
Headers:
Accept: application/json
Content-Type: application/json
Authorization: Bearer {token}
Example Request:
curl --location --request PUT 'http://127.0.0.1:5000//api/v1/auth/admin/' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {token}' \
--data-raw '{
"user_to_edit_id":14,
"role_id":2,
"address": null,
"email": "new@test.com",
"name": "name",
"surname": "surname",
"telephone": 12345
}'
Example Response:
{
"data": {
"address": null,
"email": "new@test.com",
"name": "name",
"surname": "surname",
"telephone": 12345
},
"role": "Moderator",
"success": true
}
/api/v1/cars/{{car_id}} Update password, email
Headers:
Accept: application/json
Content-Type: application/json
Authorization: Bearer {token}
Example Request:
curl --location --request PATCH 'http://127.0.0.1:5000//api/v1/auth/user/' \
--header 'Authorization: Bearer {token}' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data-raw '{
"password":"password",
"new_password":"new_password",
"new_email":"your_new_email@example.com"
}'
Example Response:
{
"success": true
}
/api/v1/comments/ Get all comments
Parameters: id, post_url, author_url, text, date, upper_comment_url
Headers:
Accept: application/json
Possible Query Params:
sort sort by parameters
parameter is equal
parameter[filter] filters: [gt],[gte],[lt],[lte],[like]
params parameters to be removed
page page number for paging
per_page number of records on one page for paging (required for paging)
Example Request:
curl --location -g --request GET 'http://127.0.0.1:5000//api/v1/comments/?params=author_url&sort=-id&id[lte]=12&per_page=2&id[gt]=2' \
--header 'Accept: application/json'
Example Response:
{
"data": [
{
"date": "15/08/1975, 09:54:32",
"id": 12,
"post_url": "/api/v1/posts/7/",
"text": "Figure perform east day raise day. Fall argue message yeah court. Give visit others very.\nBuild least knowledge group today her range. Per military free summer early ever. Fund fill quickly fast.",
"upper_comment_url": null
},
{
"date": "15/03/1978, 08:44:34",
"id": 11,
"post_url": "/api/v1/posts/1/",
"text": "Big sister list huge while beyond capital identify. Congress campaign garden somebody. Individual receive think second itself.\nLive stock officer mouth head major scene. Painting peace stage it just.",
"upper_comment_url": null
}
],
"number_of_records": 2,
"pagination": {
"current_page_url": "/api/v1/comments/?page=1¶ms=author_url&sort=-id&id%5Blte%5D=12&per_page=2&id%5Bgt%5D=2",
"next_page": "/api/v1/comments/?page=2¶ms=author_url&sort=-id&id%5Blte%5D=12&per_page=2&id%5Bgt%5D=2",
"number_of_all_pages": 5,
"number_of_all_records": 10
},
"success": true
}
/api/v1/comments/{{comment_id}}/ Get single comment
Parameters: id, post_url, author_url, text, date, upper_comment_url
Headers:
Accept: application/json
Possible Query Params:
params parameters to be removed
Example Request:
curl --location --request GET 'http://127.0.0.1:5000//api/v1/comments/1?params=upper_comment_url' \
--header 'Accept: application/json'
Example Response:
{
"data": {
"author_url": "/api/v1/users/11/",
"date": "12/08/2020, 11:26:38",
"id": 1,
"post_url": "/api/v1/posts/14/",
"text": "first one"
},
"success": true
}
Headers:
Accept: application/json
Content-Type: application/json
Authorization: Bearer {token}
Example Request:
curl --location --request POST 'http://127.0.0.1:5000//api/v1/comments/' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {token}' \
--data-raw '{
"text":"New comment",
"post_id":1,
"upper_comment":11
}'
Example Response:
{
"data": {
"author_url": "/api/v1/users/12/",
"date": "07/01/2022, 15:05:25",
"id": 95,
"post_url": "/api/v1/posts/1/",
"text": "New comment",
"upper_comment_url": "/api/v1/comments/11/"
},
"success": true
}
/api/v1/opinions/ Get all opinions
Parameters: id, text, date, image_url, author_url
Headers:
Accept: application/json
Possible Query Params:
sort sort by parameters
parameter is equal
parameter[filter] filters: [gt],[gte],[lt],[lte],[like]
params parameters to be removed
page page number for paging
per_page number of records on one page for paging (required for paging)
Example Request:
curl --location -g --request GET 'http://127.0.0.1:5000//api/v1/opinions/?sort=-date&id[lt]=13¶ms=author_url&date[gte]=2017-01-27_00:00:00' \
--header 'Accept: application/json'
Example Response:
{
"data": [
{
"date": "25/08/2021, 21:12:08",
"id": 12,
"image_url": "/static/img/opinion1.jpg",
"text": "My opinion"
},
{
"date": "12/08/2021, 11:15:56",
"id": 1,
"image_url": "/static/img/opinion1.jpg",
"text": "GREAT"
},
{
"date": "31/10/2020, 00:00:00",
"id": 2,
"image_url": "/static/img/opinion1.jpg",
"text": "Fly end reality."
},
{
"date": "27/01/2017, 00:00:00",
"id": 6,
"image_url": "/static/img/opinion2.jpg",
"text": "Everybody field in."
}
],
"number_of_records": 4,
"success": true
}
/api/v1/opinions/{{opinion_id}}/ Get single opinion
Parameters: id, date, author_url, text, image_url
Headers:
Accept: application/json
Possible Query Params:
params parameters to be removed
Example Request:
curl --location --request GET 'http://127.0.0.1:5000//api/v1/opinions/3?params=author_url' \
--header 'Accept: application/json'
Example Response:
{
"data": {
"date": "15/05/1973, 00:00:00",
"id": 3,
"image_url": "/static/img/opinion2.jpg",
"text": "Television article until region listen brother money."
},
"success": true
}
Headers:
Accept: application/json
Content-Type: application/json
Authorization: Bearer {token}
Example Request:
curl --location --request POST 'http://127.0.0.1:5000//api/v1/opinions/' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer {token}' \
--header 'Content-Type: application/json' \
--data-raw '{
"text":"New opinion"
}'
Example Response:
{
"data": {
"author_url": "/api/v1/users/4/",
"date": "03/01/2022, 17:33:48",
"id": 15,
"image_url": "/static/img/opinion3.jpg",
"text": "New opinion"
},
"success": true
}
/api/v1/rentals/ Get all rentals
Parameters: id, from_date, available_from, to_date, car_url, user_url
Headers:
Accept: application/json
Authorization: Bearer {token}
Possible Query Params:
sort sort by parameters
parameter is equal
parameter[filter] filters: [gt],[gte],[lt],[lte],[like]
Example Request:
curl --location -g --request GET 'http://127.0.0.1:5000//api/v1/rentals/?from_date[gt]=2016-03-18_13:40:00&to_date[lt]=2020-01-01_00:00:00' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer {token}'
Example Response:
{
"data": [
{
"available_from": "25/03/2016, 12:29:38",
"car_url": "/api/v1/cars/3/",
"from_date": "18/03/2016, 13:40:18",
"id": {
"car": 3,
"from": "18/03/2016, 13:40",
"user": 11
},
"to_date": "25/03/2016, 11:29:38",
"user_url": "/api/v1/users/11/"
}
],
"success": true
}
/api/v1/rentals/car{{car_id}}/user{{user_id}}/from{{%Y%m%d%H%M}}/ Get single rental
Parameters: id, from_date, available_from, to_date, car_url, user_url
Headers:
Accept: application/json
Authorization: Bearer {token}
Possible Query Params:
params parameters to be removed
Example Request:
curl --location --request GET 'http://127.0.0.1:5000//api/v1/rentals/car2/user7/from202308122020/?params=id' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer {token}'
Example Response:
{
"data": {
"available_from": "28/09/2023, 21:21:00",
"car_url": "/api/v1/cars/2/",
"from_date": "12/08/2023, 20:20:00",
"to_date": "28/09/2023, 20:21:00",
"user_url": "/api/v1/users/7/"
},
"success": true
}
Headers:
Accept: application/json
Content-Type: application/json
Authorization: Bearer {token}
Example Request:
curl --location --request POST 'http://127.0.0.1:5000//api/v1/rentals/' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {token}' \
--data-raw '{
"user_id_rental" :2,
"car_id":5,
"from_date": 202308122020,
"to_date": 202309282021
}'
Example Response:
{
"data": {
"available_from": "28/09/2023, 21:21:00",
"car_url": "/api/v1/cars/5/",
"from_date": "12/08/2023, 20:20:00",
"id": {
"car": 5,
"from": "12/08/2023, 20:20",
"user": 2
},
"to_date": "28/09/2023, 20:21:00",
"user_url": "/api/v1/users/2/"
},
"success": true
}
/api/v1/rentals/ Delete rental
Headers:
Accept: application/json
Authorization: Bearer {token}
Example Request:
curl --location --request DELETE 'http://127.0.0.1:5000//api/v1/rentals/car5/user2/from202308122020/' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer {token}'
Example Response:
{
"success": true
}
/api/v1/posts/ Get all news posts
Parameters: id, title, text, date, img_url, user_url, comments_urls
Extra Parameters: comments_number
Headers:
Accept: application/json
Possible Query Params:
sort sort by parameters
parameter is equal (for rentals_number works only without paging)
parameter[filter] filters: [gt],[gte],[lt],[lte],[like] (for rentals_number works only without paging)
params parameters to be removed (parameters and extra parameters)
page page number for paging
per_page number of records on one page for paging (required for paging)
Example Request:
curl --location -g --request GET 'http://127.0.0.1:5000//api/v1/posts/?params=comments_urls,text,id&date[gte]=2020-03-10' \
--header 'Accept: application/json'
Example Response:
{
"data": [
{
"comments_number": 0,
"date": "2020-03-10",
"img_url": "/static/img/car3.jpg",
"title": "You must position.",
"user_url": "/api/v1/users/5/"
},
{
"comments_number": 0,
"date": "2022-01-04",
"img_url": "/static/img/opinion1.jpg",
"title": "My new post!",
"user_url": "/api/v1/users/11/"
}
],
"number_of_records": 2,
"success": true
}
/api/v1/posts/{{post_id}}/ Get single news post
Parameters: id, title, text, date, img_url, user_url, comments_number, comments_urls
Headers:
Accept: application/json
Possible Query Params:
params parameters to be removed
Example Request:
curl --location --request GET 'http://127.0.0.1:5000//api/v1/posts/11/?params=comments_urls,date' \
--header 'Accept: application/json'
Example Response:
{
"data": {
"comments_number": 7,
"id": 11,
"img_url": "/static/img/car3.jpg",
"text": "Really agree laugh like development discuss miss analysis. Position option above follow cell actually.\nTough lawyer daughter need away provide message. Speak long top it address serve cultural.",
"title": "Low project follow probably.",
"user_url": "/api/v1/users/9/"
},
"success": true
}
Headers:
Accept: application/json
Content-Type: application/json
Authorization: Bearer {token}
Example Request:
curl --location --request POST 'http://127.0.0.1:5000//api/v1/posts/' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer {token}' \
--header 'Content-Type: application/json' \
--data-raw '{
"title":"My post!",
"text":"Text content"
}'
Example Response:
{
"data": {
"comments_number": 0,
"comments_urls": [],
"date": "2022-01-08",
"id": 71,
"img_url": "/static/img/no_img.jpg",
"text": "Text content",
"title": "My post",
"user_url": "/api/v1/users/12/"
},
"success": true
}
To run this project, install it locally:
$ git clone https://github.com/msbetsy/car_rental
After you clone this repo to your desktop, go to its root directory, create .flaskenv file, create and activate virtual environment, install requirements and run application (http://localhost:5000). You can also log in as admin: email: mail@mail.com, password: admin.
$ cd car_rental
$ (echo FLASK_ENV=development &echo.FLASK_APP=car_rental.py) > .flaskenv
$ python -m venv venv
$ venv\Scripts\activate
$ pip install -r requirements.txt
$ flask run