mirror of
https://github.com/lemeow125/Borrowing-TrackerBackend.git
synced 2024-11-16 22:09:27 +08:00
Merge pull request #11 from lemeow125/feature/equipment_groups
Feature/equipment groups
This commit is contained in:
commit
3dcd845f72
19 changed files with 1557 additions and 30 deletions
|
@ -6,6 +6,7 @@ from .models import CustomUser
|
|||
|
||||
class CustomUserAdmin(UserAdmin):
|
||||
model = CustomUser
|
||||
list_display = UserAdmin.list_display + ('is_technician',)
|
||||
|
||||
|
||||
admin.site.register(CustomUser, CustomUserAdmin)
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 4.2.7 on 2023-11-13 10:22
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('accounts', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='customuser',
|
||||
name='is_technician',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
|
@ -37,6 +37,7 @@ class CustomUser(AbstractUser):
|
|||
# Password inherited from base user class
|
||||
# is_admin inherited from base user class
|
||||
is_active = models.BooleanField(default=False)
|
||||
is_technician = models.BooleanField(default=False)
|
||||
avatar = models.ImageField(upload_to=_get_upload_to, null=True)
|
||||
|
||||
@property
|
||||
|
@ -65,11 +66,11 @@ def create_superuser(sender, **kwargs):
|
|||
print('Created admin account')
|
||||
superuser.save()
|
||||
|
||||
username = 'usertest1'
|
||||
username = 'test-user-technician'
|
||||
email = os.getenv('DJANGO_ADMIN_EMAIL')
|
||||
password = os.getenv('DJANGO_ADMIN_PASSWORD')
|
||||
first_name = 'Test'
|
||||
last_name = 'User'
|
||||
last_name = 'Technician'
|
||||
|
||||
if not User.objects.filter(username=username).exists():
|
||||
# Create the superuser with is_active set to False
|
||||
|
@ -78,5 +79,5 @@ def create_superuser(sender, **kwargs):
|
|||
|
||||
# Activate the user
|
||||
user.is_active = True
|
||||
print('Created debug user account')
|
||||
print('Created debug technician account')
|
||||
user.save()
|
||||
|
|
11
equipment_tracker/accounts/permissions.py
Normal file
11
equipment_tracker/accounts/permissions.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
from rest_framework.permissions import BasePermission
|
||||
|
||||
|
||||
class IsTechnician(BasePermission):
|
||||
message = "You must be a technician to perform this action."
|
||||
|
||||
def has_permission(self, request, view):
|
||||
return request.user.is_authenticated and request.user.is_technician
|
||||
|
||||
def has_object_permission(self, request, view, obj):
|
||||
return request.user.is_authenticated and request.user.is_technician
|
|
@ -29,7 +29,6 @@ SECRET_KEY = str(os.getenv('SECRET_KEY'))
|
|||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
FRONTEND_DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = ['*']
|
||||
# CSRF_TRUSTED_ORIGINS = [] To-do: Specify URL to web frontend
|
||||
|
@ -71,6 +70,7 @@ INSTALLED_APPS = [
|
|||
'drf_spectacular_sidecar',
|
||||
'accounts',
|
||||
'equipments',
|
||||
'equipment_groups',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
@ -162,9 +162,6 @@ AUTH_USER_MODEL = 'accounts.CustomUser'
|
|||
DJOSER = {
|
||||
'SEND_ACTIVATION_EMAIL': True,
|
||||
'SEND_CONFIRMATION_EMAIL': True,
|
||||
'EMAIL': {
|
||||
'activation': 'config.email.ActivationEmail'
|
||||
},
|
||||
'ACTIVATION_URL': 'activation/{uid}/{token}',
|
||||
'USER_AUTHENTICATION_RULES': ['djoser.authentication.TokenAuthenticationRule'],
|
||||
'SERIALIZERS': {
|
||||
|
@ -210,11 +207,7 @@ USE_TZ = True
|
|||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
DOMAIN = ''
|
||||
if (FRONTEND_DEBUG):
|
||||
DOMAIN = 'exp'
|
||||
else:
|
||||
DOMAIN = 'citctracker'
|
||||
DOMAIN = 'CITC-Tracker.keannu1.duckdns.org'
|
||||
|
||||
SITE_NAME = 'CITC Equipment Tracker'
|
||||
|
||||
|
|
0
equipment_tracker/equipment_groups/__init__.py
Normal file
0
equipment_tracker/equipment_groups/__init__.py
Normal file
9
equipment_tracker/equipment_groups/admin.py
Normal file
9
equipment_tracker/equipment_groups/admin.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from django.contrib import admin
|
||||
from .models import EquipmentGroup
|
||||
from simple_history.admin import SimpleHistoryAdmin
|
||||
|
||||
|
||||
@admin.register(EquipmentGroup)
|
||||
class EquipmentGroupAdmin(SimpleHistoryAdmin):
|
||||
readonly_fields = ['status']
|
||||
list_display = ('name', 'status', 'date_added', 'last_updated')
|
6
equipment_tracker/equipment_groups/apps.py
Normal file
6
equipment_tracker/equipment_groups/apps.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class EquipmentGroupsConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'equipment_groups'
|
|
@ -0,0 +1,53 @@
|
|||
# Generated by Django 4.2.7 on 2023-11-13 09:43
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import simple_history.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('equipments', '0003_historicalequipment'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='HistoricalEquipmentGroup',
|
||||
fields=[
|
||||
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=200)),
|
||||
('remarks', models.TextField(max_length=512)),
|
||||
('date_added', models.DateTimeField(default=django.utils.timezone.now, editable=False)),
|
||||
('last_updated', models.DateTimeField(blank=True, editable=False)),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField(db_index=True)),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical equipment group',
|
||||
'verbose_name_plural': 'historical equipment groups',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EquipmentGroup',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=200)),
|
||||
('remarks', models.TextField(max_length=512)),
|
||||
('date_added', models.DateTimeField(default=django.utils.timezone.now, editable=False)),
|
||||
('last_updated', models.DateTimeField(auto_now=True)),
|
||||
('equipments', models.ManyToManyField(to='equipments.equipmentinstance')),
|
||||
],
|
||||
),
|
||||
]
|
40
equipment_tracker/equipment_groups/models.py
Normal file
40
equipment_tracker/equipment_groups/models.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
from django.db import models
|
||||
from django.utils.timezone import now
|
||||
from simple_history.models import HistoricalRecords
|
||||
from django.db.models.signals import post_migrate
|
||||
from django.dispatch import receiver
|
||||
from equipments.models import EquipmentInstance
|
||||
|
||||
|
||||
class EquipmentGroup(models.Model):
|
||||
name = models.CharField(max_length=200)
|
||||
remarks = models.TextField(max_length=512)
|
||||
date_added = models.DateTimeField(default=now, editable=False)
|
||||
last_updated = models.DateTimeField(auto_now=True, editable=False)
|
||||
equipments = models.ManyToManyField(EquipmentInstance)
|
||||
history = HistoricalRecords()
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
if self.equipments.filter(status='Broken').exists():
|
||||
return 'Broken'
|
||||
elif self.equipments.filter(status='Under Maintenance').exists():
|
||||
return 'Under Maintenance'
|
||||
elif self.equipments.filter(status='Decomissioned').exists():
|
||||
return 'Decomissioned'
|
||||
else:
|
||||
return 'Working'
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@receiver(post_migrate)
|
||||
def create_superuser(sender, **kwargs):
|
||||
if sender.name == 'equipment_groups':
|
||||
PC = EquipmentInstance.objects.filter(id=1).first().id
|
||||
KEYBOARD = EquipmentInstance.objects.filter(id=2).first().id
|
||||
MOUSE = EquipmentInstance.objects.filter(id=3).first().id
|
||||
GROUP, CREATED = EquipmentGroup.objects.get_or_create(
|
||||
name="HP All-In-One PC Set", remarks="First PC set of citc tracker!")
|
||||
GROUP.equipments.set([PC, KEYBOARD, MOUSE])
|
3
equipment_tracker/equipment_groups/tests.py
Normal file
3
equipment_tracker/equipment_groups/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
3
equipment_tracker/equipment_groups/views.py
Normal file
3
equipment_tracker/equipment_groups/views.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
|
@ -2,5 +2,14 @@ from django.contrib import admin
|
|||
from simple_history.admin import SimpleHistoryAdmin
|
||||
from .models import Equipment, EquipmentInstance
|
||||
|
||||
admin.site.register(Equipment, SimpleHistoryAdmin)
|
||||
admin.site.register(EquipmentInstance, SimpleHistoryAdmin)
|
||||
|
||||
@admin.register(Equipment)
|
||||
class EquipmentAdmin(SimpleHistoryAdmin):
|
||||
readonly_fields = ('date_added', 'last_updated')
|
||||
list_display = ('name', 'date_added', 'last_updated')
|
||||
|
||||
|
||||
@admin.register(EquipmentInstance)
|
||||
class EquipmentInstanceAdmin(SimpleHistoryAdmin):
|
||||
list_display = ('equipment', 'status', 'remarks',
|
||||
'date_added', 'last_updated')
|
||||
|
|
|
@ -42,9 +42,13 @@ def create_superuser(sender, **kwargs):
|
|||
if sender.name == 'equipments':
|
||||
EQUIPMENT, CREATED = Equipment.objects.get_or_create(
|
||||
name="HP All-in-One PC", description="I5 6500 8GB RAM 1TB HDD")
|
||||
if (CREATED):
|
||||
print("Created sample equipment")
|
||||
EQUIPMENT_INSTANCE, CREATED = EquipmentInstance.objects.get_or_create(
|
||||
equipment=EQUIPMENT, status="Working", remarks="First PC of USTP!")
|
||||
if (CREATED):
|
||||
print("Created sample equipment instance")
|
||||
equipment=EQUIPMENT, status="Working", remarks="First PC of citc equipment tracker!")
|
||||
EQUIPMENT, CREATED = Equipment.objects.get_or_create(
|
||||
name="HP Keyboard", description="Generic Membrane Keyboard")
|
||||
EQUIPMENT_INSTANCE, CREATED = EquipmentInstance.objects.get_or_create(
|
||||
equipment=EQUIPMENT, status="Working", remarks="First keyboard of citc equipment tracker!")
|
||||
EQUIPMENT, CREATED = Equipment.objects.get_or_create(
|
||||
name="HP Mouse", description="Generic Mouse")
|
||||
EQUIPMENT_INSTANCE, CREATED = EquipmentInstance.objects.get_or_create(
|
||||
equipment=EQUIPMENT, status="Working", remarks="First mouse of citc equipment tracker!")
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
from rest_framework import serializers
|
||||
from django.contrib.auth.models import User
|
||||
from .models import Equipment, EquipmentInstance
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from accounts.models import CustomUser
|
||||
|
||||
# -- Equipment Serializers
|
||||
|
||||
|
@ -31,6 +33,7 @@ class EquipmentSerializer(serializers.HyperlinkedModelSerializer):
|
|||
def get_history_user(self, obj):
|
||||
return obj.history_user.username if obj.history_user else None
|
||||
|
||||
@extend_schema_field(OpenApiTypes.STR)
|
||||
def get_last_updated_by(self, obj):
|
||||
return obj.history.first().history_user if obj.history.first().history_user else None
|
||||
|
||||
|
@ -47,6 +50,7 @@ class EquipmentLogsSerializer(serializers.HyperlinkedModelSerializer):
|
|||
read_only_fields = ('history_id', 'id', 'name', 'description',
|
||||
'history_date', 'history_user')
|
||||
|
||||
@extend_schema_field(OpenApiTypes.STR)
|
||||
def get_history_user(self, obj):
|
||||
return obj.history_user.username if obj.history_user else None
|
||||
|
||||
|
@ -68,6 +72,7 @@ class EquipmentLogSerializer(serializers.HyperlinkedModelSerializer):
|
|||
read_only_fields = ('id', 'last_updated',
|
||||
'date_added', 'last_updated_by', 'history')
|
||||
|
||||
@extend_schema_field(OpenApiTypes.STR)
|
||||
def get_last_updated_by(self, obj):
|
||||
return obj.history.first().history_user if obj.history.first().history_user else None
|
||||
|
||||
|
@ -106,9 +111,11 @@ class EquipmentInstanceSerializer(serializers.HyperlinkedModelSerializer):
|
|||
read_only_fields = ('id', 'last_updated',
|
||||
'last_updated_by', 'date_added')
|
||||
|
||||
@extend_schema_field(OpenApiTypes.STR)
|
||||
def get_history_user(self, obj):
|
||||
return obj.history_user.username if obj.history_user else None
|
||||
|
||||
@extend_schema_field(OpenApiTypes.STR)
|
||||
def get_last_updated_by(self, obj):
|
||||
return obj.history.first().history_user if obj.history.first().history_user else None
|
||||
|
||||
|
@ -127,6 +134,7 @@ class EquipmentInstanceLogsSerializer(serializers.HyperlinkedModelSerializer):
|
|||
read_only_fields = ('history_id', 'id', 'equipment', 'status', 'remarks',
|
||||
'history_date', 'history_user')
|
||||
|
||||
@extend_schema_field(OpenApiTypes.STR)
|
||||
def get_history_user(self, obj):
|
||||
return obj.history_user.username if obj.history_user else None
|
||||
|
||||
|
@ -156,5 +164,6 @@ class EquipmentInstanceLogSerializer(serializers.HyperlinkedModelSerializer):
|
|||
read_only_fields = ('id', 'last_updated',
|
||||
'date_added', 'last_updated_by', 'history')
|
||||
|
||||
@extend_schema_field(OpenApiTypes.STR)
|
||||
def get_last_updated_by(self, obj):
|
||||
return obj.history.first().history_user if obj.history.first().history_user else None
|
||||
|
|
|
@ -3,13 +3,14 @@ from rest_framework import viewsets, generics
|
|||
from .models import Equipment, EquipmentInstance
|
||||
from . import serializers
|
||||
from config.settings import DEBUG
|
||||
from accounts.permissions import IsTechnician
|
||||
|
||||
# -- Equipment Viewsets
|
||||
|
||||
|
||||
class EquipmentViewSet(viewsets.ModelViewSet):
|
||||
if (not DEBUG):
|
||||
permission_classes = [IsAuthenticated]
|
||||
permission_classes = [IsAuthenticated, IsTechnician]
|
||||
serializer_class = serializers.EquipmentSerializer
|
||||
queryset = Equipment.objects.all().order_by('-date_added')
|
||||
|
||||
|
@ -18,7 +19,7 @@ class EquipmentViewSet(viewsets.ModelViewSet):
|
|||
|
||||
class EquipmentsLogsViewSet(generics.ListAPIView):
|
||||
if (not DEBUG):
|
||||
permission_classes = [IsAuthenticated]
|
||||
permission_classes = [IsAuthenticated, IsTechnician]
|
||||
serializer_class = serializers.EquipmentLogsSerializer
|
||||
queryset = Equipment.history.all().order_by('-history_date')
|
||||
|
||||
|
@ -27,7 +28,7 @@ class EquipmentsLogsViewSet(generics.ListAPIView):
|
|||
|
||||
class EquipmentLogViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
if (not DEBUG):
|
||||
permission_classes = [IsAuthenticated]
|
||||
permission_classes = [IsAuthenticated, IsTechnician]
|
||||
serializer_class = serializers.EquipmentLogSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
|
@ -39,7 +40,7 @@ class EquipmentLogViewSet(viewsets.ReadOnlyModelViewSet):
|
|||
|
||||
class LastUpdatedEquipmentViewSet(generics.ListAPIView):
|
||||
if (not DEBUG):
|
||||
permission_classes = [IsAuthenticated]
|
||||
permission_classes = [IsAuthenticated, IsTechnician]
|
||||
serializer_class = serializers.EquipmentSerializer
|
||||
queryset = Equipment.objects.all().order_by('-date_added')
|
||||
|
||||
|
@ -51,7 +52,7 @@ class LastUpdatedEquipmentViewSet(generics.ListAPIView):
|
|||
|
||||
class EquipmentInstanceViewSet(viewsets.ModelViewSet):
|
||||
if (not DEBUG):
|
||||
permission_classes = [IsAuthenticated]
|
||||
permission_classes = [IsAuthenticated, IsTechnician]
|
||||
serializer_class = serializers.EquipmentInstanceSerializer
|
||||
queryset = EquipmentInstance.objects.all().order_by('-date_added')
|
||||
|
||||
|
@ -60,7 +61,7 @@ class EquipmentInstanceViewSet(viewsets.ModelViewSet):
|
|||
|
||||
class EquipmentInstancesLogsViewSet(generics.ListAPIView):
|
||||
if (not DEBUG):
|
||||
permission_classes = [IsAuthenticated]
|
||||
permission_classes = [IsAuthenticated, IsTechnician]
|
||||
serializer_class = serializers.EquipmentInstanceLogsSerializer
|
||||
queryset = EquipmentInstance.history.all().order_by('-history_date')
|
||||
|
||||
|
@ -69,7 +70,7 @@ class EquipmentInstancesLogsViewSet(generics.ListAPIView):
|
|||
|
||||
class EquipmentInstanceLogViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
if (not DEBUG):
|
||||
permission_classes = [IsAuthenticated]
|
||||
permission_classes = [IsAuthenticated, IsTechnician]
|
||||
serializer_class = serializers.EquipmentInstanceLogSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
|
@ -81,7 +82,7 @@ class EquipmentInstanceLogViewSet(viewsets.ReadOnlyModelViewSet):
|
|||
|
||||
class LastUpdatedEquipmentInstanceViewSet(generics.ListAPIView):
|
||||
if (not DEBUG):
|
||||
permission_classes = [IsAuthenticated]
|
||||
permission_classes = [IsAuthenticated, IsTechnician]
|
||||
serializer_class = serializers.EquipmentInstanceSerializer
|
||||
queryset = EquipmentInstance.objects.all().order_by('-date_added')
|
||||
|
||||
|
|
|
@ -997,7 +997,7 @@ components:
|
|||
equipment:
|
||||
type: integer
|
||||
status:
|
||||
type: string
|
||||
$ref: '#/components/schemas/StatusEnum'
|
||||
remarks:
|
||||
type: string
|
||||
last_updated:
|
||||
|
@ -1062,6 +1062,11 @@ components:
|
|||
history_id:
|
||||
type: integer
|
||||
readOnly: true
|
||||
id:
|
||||
type: integer
|
||||
readOnly: true
|
||||
equipment:
|
||||
type: integer
|
||||
status:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/StatusEnum'
|
||||
|
@ -1077,9 +1082,11 @@ components:
|
|||
type: string
|
||||
readOnly: true
|
||||
required:
|
||||
- equipment
|
||||
- history_date
|
||||
- history_id
|
||||
- history_user
|
||||
- id
|
||||
- remarks
|
||||
- status
|
||||
EquipmentLog:
|
||||
|
@ -1206,7 +1213,7 @@ components:
|
|||
equipment:
|
||||
type: integer
|
||||
status:
|
||||
type: string
|
||||
$ref: '#/components/schemas/StatusEnum'
|
||||
remarks:
|
||||
type: string
|
||||
last_updated:
|
||||
|
|
1359
equipment_tracker/schema.yml1
Normal file
1359
equipment_tracker/schema.yml1
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue