The project is initialized by a Django Command common/management/commands/mock_users_and_projects.py, this command creates 15 users from user_1 to user_15 with password Passw0rd! and an admin user with username admin and password Passw0rd!.
It also creates 3 project instances assigning users as follow:
proj1-->user_1,user_2,user_3,user_4,user_5proj2-->user_6,user_7,user_8,user_9,user_10proj3--> no user- no project -->
user_11,user_12,user_13,user_14,user_15
Rest Framework serializers are used only to serialize object and to check validity for input data. Validation in general is inside the models clean method and on models constraints.
Access to database is done by crud modules, so that Views can access to DB only through crud modules.
Main business logic is done in core or in permissions modules.
-
common/models:BaseModel
Abstract model to handle common columns
-
projects/models:Project
General infos about the project, has both a 1to1 relation with
UserCustommodel ascreatorand a M2M relation asassignees -
projects/models:ProjectAssignment
Bridge table for M2M relation
Project-UserCustom, it has aunique_toghetherconstraint so that one user can be assigned to one project only once. -
time_log/models:TimeLog
Keeps track of working time per user and per project, so it has a M21 relation with
ProjectAssignmenttable both foruserandproject. Theend_timefield is null when a user is currently working on some project. It has a constraint to check thatend_timeis greater thanstart_timeand performs additional validation in thecleanmethod to ensure that two instance oftime_logper same user and same project don't overlap. -
users/models:UserCustom
Simply inherits from Django default
AbstractUsermodel.
It is Session based authentication on top of all views and it is handled by REST_FRAMEWORK Basic Authentication and SessionAuthentication DEFAULT_AUTHENTICATION_CLASSES
As a general rule, Admin user can do any operation, so next consideration are applied only to standard users. All classes inherits from rest_framework.permissions.BasePermissions overriding, as needed, has_permission and has_object_permissions method
-
common/permissions:IsAssignedToProjectOrAdmin
Checks that a user is access project or time_log information of only assigned projects
-
common/permissions:IsLogOwnerOrAdmin
Inherits from the previous and ensure that a time_log object can only be read by users that are part of the projects and can only be written by user who logged the specific object
-
common/permissions:IsAdminForWriting
Inherits from
rest_framework.permissions.IsAdminUserto ensure that specic view is accessed by non admin users only for reading operations.
All custom mixins implements the initial method of rest_framework.views.APIView mostly to inject in the view kwargs a specific object or to ensure that a required query parameter was provided
-
common/mixins:ObjectFromIdMixin
Base class that based on field
lookup_namesearches in the request kwargs the key to be used to search in thecrud_instancefield so that it can injects the specifc object in the request context using theinjection_name. Optionally it can be defined asrequiredso that if it is not found it will raise aParseErrorexception -
projects/mixins:ProjectFromIdMixin
Subclass of base
ObjectFromIdMixin -
projects/mixins:ProjectIdQueryStringMixin
Subclass of
ProjectFromIdMixinensures the?project_id=_is passed as query param -
time_log/mixins:TimeLogFromIdMixin
Subclass of base
ObjectFromIdMixin -
users/mixins:UserIdQueryStringMixin
Takes user from optional
?user_id=_query param
Is implemented with a Redis container, it caches the GET views at high level
-
projects/views:ProjectListCreateApi
/projects/
GET
Returns the list of projects per user (admin sees them all)
POST
Creates a project (only admin)
body
{ "name": "proj_test", "description": "my test proj" } -
projects/views:ProjectRetrieveUpdateDelete
/projects/<id>
GET
Read a project instance, if assigned to user (admin sees them all)
PUT
Edit a project (only admin)
body
{ "name": "proj_test_edit", "description": "my test proj edited" }DELETE
Delete a project (only admin)
-
projects/views:ProjectHandleUsers
/projects/<id>/handle_users
POST
Assign a list of users to a project (only admin)
body
[ "user_7", "user_8" ]DELETE
Remove a list of users to a project (only admin)
body
[ "user_7" ] -
projects/views:ProjectStatistics
/projects/<id>/statistics[?user_id=1]
GET
Total time spent on the given project from all assignees.
query param (optional)
?user_id=1To see only time spent by a given user
-
time_log/views:TimeLogListCreateApi
/time_log/project_id=1
GET
Returns the list of time_log per user (admin sees them all)
query param (required)
?project_id=1To indicate for which project get the logs
POST
Creates a time_log if user belongs to
?project_idprojectbody
{ "start_time": "2022-12-23T12:41:52Z", "end_time": "2022-12-23T12:41:53Z" }query param (required)
?project_id=1To indicate for which project create the logs
-
time_log/views:TimeLogRetrieveUpdateDelete
/time_log/<id>
GET
Read a time_log instance, if user is assigned to project (admin sees them all)
PUT
Edit a time_log if user was the logger (admin edits them all)
body
{ "name": "proj_test_edit", "description": "my test proj edited" }DELETE
Delete a time_log if user was the logger (admin deletes them all)
In this module there are the classes that access to Database.
All classes inherit from common/crud:BaseCRUD that implements some basic methods.
The Django server is exposed on port 7777
docker-compose upTo run tests, from a different terminal run
docker exec ptt_backend python project_time_tracker/manage.py test tests To run again the init command, run
docker exec ptt_backend python project_time_tracker/manage.py mock_users_and_projects