Tracking object changes (#132)

* track history using simple django history

* tracking changes in model and saving api request sender

* minor fixes

* correcting requirements.txt

Co-authored-by: Gowtham Sai <66207607+gowtham3105@users.noreply.github.com>
This commit is contained in:
karthik mv 2022-07-21 16:41:31 +05:30 committed by GitHub
parent b7cf319643
commit 4f18d2d540
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 98 additions and 15 deletions

1
.gitignore vendored
View File

@ -139,3 +139,4 @@ dmypy.json
/CDC_Backend/Storage/
.idea
*.pyc
dev.env

View File

@ -1,4 +1,5 @@
from django.contrib import admin
from simple_history.admin import SimpleHistoryAdmin
from django.contrib.admin.templatetags.admin_urls import admin_urlname
from django.shortcuts import resolve_url
from django.utils.html import format_html
@ -6,8 +7,8 @@ from django.utils.safestring import SafeText
from .models import *
admin.site.register(User)
admin.site.register(Admin)
admin.site.register(User, SimpleHistoryAdmin)
admin.site.register(Admin, SimpleHistoryAdmin)
admin.site.site_header = "CDC Recruitment Portal"
@ -18,7 +19,7 @@ def model_admin_url(obj, name=None) -> str:
@admin.register(Student)
class Student(admin.ModelAdmin):
class Student(SimpleHistoryAdmin):
list_display = ("roll_no", "name", "batch", "branch", "phone_number", 'can_apply')
search_fields = ("roll_no", "name", "phone_number")
ordering = ("roll_no", "name", "batch", "branch", "phone_number")
@ -37,7 +38,7 @@ class Student(admin.ModelAdmin):
@admin.register(Placement)
class Placement(admin.ModelAdmin):
class Placement(SimpleHistoryAdmin):
list_display = (COMPANY_NAME, CONTACT_PERSON_NAME, PHONE_NUMBER, 'tier', 'compensation_CTC')
search_fields = (COMPANY_NAME, CONTACT_PERSON_NAME)
ordering = (COMPANY_NAME, CONTACT_PERSON_NAME, 'tier', 'compensation_CTC')
@ -45,7 +46,7 @@ class Placement(admin.ModelAdmin):
@admin.register(PlacementApplication)
class PlacementApplication(admin.ModelAdmin):
class PlacementApplication(SimpleHistoryAdmin):
list_display = ('id', 'Placement', 'Student', 'selected')
search_fields = ('id',)
ordering = ('id',)
@ -59,7 +60,7 @@ class PlacementApplication(admin.ModelAdmin):
@admin.register(PrePlacementOffer)
class PrePlacementOffer(admin.ModelAdmin):
class PrePlacementOffer(SimpleHistoryAdmin):
list_display = ('company', 'Student', 'accepted')
search_fields = ('company',)
ordering = ('company',)

View File

@ -39,6 +39,7 @@ def markStatus(request, id, email, user_type):
sendEmail(email, subject, data, STUDENT_APPLICATION_STATUS_SELECTED_TEMPLATE)
else:
sendEmail(email, subject, data, STUDENT_APPLICATION_STATUS_NOT_SELECTED_TEMPLATE)
application.chaged_by = get_object_or_404(User, id=id)
application.save()
else:
raise ValueError("Student - " + i[STUDENT_ID] + " didn't apply for this opening")
@ -89,6 +90,7 @@ def updateDeadline(request, id, email, user_type):
opening = get_object_or_404(Placement, pk=data[OPENING_ID])
# Updating deadline date with correct format in datetime field
opening.deadline_datetime = datetime.datetime.strptime(data[DEADLINE_DATETIME], '%Y-%m-%d %H:%M:%S %z')
opening.changed_by = get_object_or_404(User, id=id)
opening.save()
return Response({'action': "Update Deadline", 'message': "Deadline Updated"},
status=status.HTTP_200_OK)
@ -111,6 +113,7 @@ def updateOfferAccepted(request, id, email, user_type):
opening = get_object_or_404(Placement, pk=data[OPENING_ID])
opening.offer_accepted = True if data[OFFER_ACCEPTED] == True else False
print(opening.offer_accepted)
opening.changed_by = get_object_or_404(User, id=id)
opening.save()
return Response({'action': "Update Offer Accepted", 'message': "Offer Accepted Updated"},
status=status.HTTP_200_OK)
@ -131,6 +134,7 @@ def updateEmailVerified(request, id, email, user_type):
data = request.data
opening = get_object_or_404(Placement, pk=data[OPENING_ID])
opening.email_verified = True if data[EMAIL_VERIFIED] == "true" else False
opening.changed_by = get_object_or_404(User, id=id)
opening.save()
return Response({'action': "Update Email Verified", 'message': "Email Verified Updated"},
status=status.HTTP_200_OK)
@ -152,6 +156,7 @@ def deleteAdditionalInfo(request, id, email, user_type):
opening = get_object_or_404(Placement, pk=data[OPENING_ID])
if data[FIELD] in opening.additional_info:
opening.additional_info.remove(data[FIELD])
opening.changed_by = get_object_or_404(User, id=id)
opening.save()
return Response({'action': "Delete Additional Info", 'message': "Additional Info Deleted"},
status=status.HTTP_200_OK)
@ -182,6 +187,7 @@ def addAdditionalInfo(request, id, email, user_type):
status=status.HTTP_200_OK)
else:
raise ValueError("Additional Info Found")
except Http404:
return Response({'action': "Add Additional Info", 'message': 'Opening Not Found'},
status=status.HTTP_404_NOT_FOUND)
@ -240,6 +246,7 @@ def submitApplication(request, id, email, user_type):
else:
additional_info[i] = data[ADDITIONAL_INFO][i]
application.additional_info = json.dumps(additional_info)
application.changed_by = get_object_or_404(User, id=id)
application.save()
return Response({'action': "Add Student Application", 'message': "Application added"},
status=status.HTTP_200_OK)
@ -259,6 +266,7 @@ def submitApplication(request, id, email, user_type):
additional_info[i] = data[ADDITIONAL_INFO][i]
application.additional_info = json.dumps(additional_info)
application.changed_by = get_object_or_404(User, id=id)
application.save()
return Response({'action': "Add Student Application", 'message': "Application updated"},
status=status.HTTP_200_OK)
@ -349,6 +357,7 @@ def addPPO(request, id, email, user_type):
PPO.tier = data[TIER]
if COMPENSATION_DETAILS in data:
PPO.compensation_details = data[COMPENSATION_DETAILS]
PPO.changed_by = get_object_or_404(User, id=id)
PPO.save()
return Response({'action': "Add PPO", 'message': "PPO added"},
status=status.HTTP_200_OK)

View File

@ -1,6 +1,7 @@
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.utils import timezone
from simple_history.models import HistoricalRecords
from .constants import *
@ -13,6 +14,7 @@ class User(models.Model):
id = models.CharField(blank=False, max_length=25)
user_type = ArrayField(models.CharField(blank=False, max_length=10), size=4, default=list, blank=False)
last_login_time = models.DateTimeField(default=timezone.now)
history = HistoricalRecords()
class Meta:
verbose_name_plural = "User"
@ -30,14 +32,41 @@ class Student(models.Model):
default=list, blank=True)
cpi = models.DecimalField(decimal_places=2, max_digits=4)
can_apply = models.BooleanField(default=True, verbose_name='Registered')
changed_by = models.ForeignKey(User, blank=True, on_delete=models.RESTRICT, default=None, null=True)
history = HistoricalRecords(user_model=User)
def __str__(self):
return str(self.roll_no)
@property
def _history_user(self):
return self.changed_by
@_history_user.setter
def _history_user(self, value):
if isinstance(value, User):
self.changed_by = value
else:
self.changed_by = None
class Admin(models.Model):
id = models.CharField(blank=False, max_length=15, primary_key=True)
name = models.CharField(blank=False, max_length=JNF_TEXT_MAX_CHARACTER_COUNT)
changed_by = models.ForeignKey(User, blank=True, on_delete=models.RESTRICT, default=None, null=True)
history = HistoricalRecords(user_model=User)
@property
def _history_user(self):
return self.changed_by
@_history_user.setter
def _history_user(self, value):
if isinstance(value, User):
self.changed_by = value
else:
self.changed_by = None
def two_day_after_today():
@ -114,7 +143,8 @@ class Placement(models.Model):
deadline_datetime = models.DateTimeField(blank=False, verbose_name="Deadline Date", default=two_day_after_today)
created_at = models.DateTimeField(blank=False, default=None, null=True)
updated_at = models.DateTimeField(blank=False, default=None, null=True)
changed_by = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
history = HistoricalRecords(user_model=User)
def format(self):
if self.company_name is not None:
self.company_name = self.company_name.strip()[:JNF_SMALLTEXT_MAX_CHARACTER_COUNT]
@ -158,6 +188,17 @@ class Placement(models.Model):
if self.additional_info is not None:
self.additional_info = [info.strip()[:JNF_TEXTMEDIUM_MAX_CHARACTER_COUNT] for info in list(self.additional_info)]
@property
def _history_user(self):
return self.changed_by
@_history_user.setter
def _history_user(self, value):
if isinstance(value, User):
self.changed_by = value
else:
self.changed_by = None
def save(self, *args, **kwargs):
''' On save, add timestamps '''
if not self.created_at:
@ -179,6 +220,8 @@ class PlacementApplication(models.Model):
selected = models.BooleanField(null=True, default=None, blank=True)
applied_at = models.DateTimeField(blank=False, default=None, null=True)
updated_at = models.DateTimeField(blank=False, default=None, null=True)
changed_by = models.ForeignKey(User, blank=False, on_delete=models.RESTRICT, default=None, null=True)
history = HistoricalRecords(user_model=User)
def save(self, *args, **kwargs):
''' On save, add timestamps '''
@ -188,6 +231,17 @@ class PlacementApplication(models.Model):
return super(PlacementApplication, self).save(*args, **kwargs)
@property
def _history_user(self):
return self.changed_by
@_history_user.setter
def _history_user(self, value):
if isinstance(value, User):
self.changed_by = value
else:
self.changed_by = None
class Meta:
verbose_name_plural = "Placement Applications"
unique_together = ('placement_id', 'student_id')
@ -206,3 +260,16 @@ class PrePlacementOffer(models.Model):
tier = models.CharField(blank=False, choices=TIERS, max_length=10)
designation = models.CharField(blank=False, max_length=25, default=None, null=True)
accepted = models.BooleanField(default=None, null=True)
changed_by = models.ForeignKey(User, blank=False, on_delete=models.RESTRICT, default=None, null=True)
history = HistoricalRecords(user_model=User)
@property
def _history_user(self):
return self.changed_by
@_history_user.setter
def _history_user(self, value):
if isinstance(value, User):
self.changed_by = value
else:
self.changed_by = None

View File

@ -44,7 +44,7 @@ def addResume(request, id, email, user_type):
destination_path = STORAGE_DESTINATION_RESUMES + str(student.roll_no) + "/"
file_name = saveFile(file, destination_path)
student.resumes.append(file_name)
student.changed_by = get_object_or_404(User, id=id)
student.save()
return Response({'action': "Upload Resume", 'message': "Resume Added"},
status=status.HTTP_200_OK)
@ -104,6 +104,7 @@ def deleteResume(request, id, email, user_type):
if path.exists(destination_path):
# remove(destination_path)
student.resumes.remove(file_name)
student.changed_by = get_object_or_404(User, id=id)
student.save()
return Response({'action': "Delete Resume", 'message': "Resume Deleted"},
status=status.HTTP_200_OK)
@ -177,7 +178,7 @@ def submitApplication(request, id, email, user_type):
}
subject = STUDENT_APPLICATION_SUBMITTED_TEMPLATE_SUBJECT.format(company_name=opening.company_name)
sendEmail(email, subject, data, STUDENT_APPLICATION_SUBMITTED_TEMPLATE)
application.changed_by = get_object_or_404(User, id=id)
application.save()
return Response({'action': "Submit Application", 'message': "Application Submitted"},
status=status.HTTP_200_OK)

View File

@ -29,7 +29,7 @@ SECRET_KEY = 'e_i2g3z!y4+p3dwm%k9k=zmsot@aya-0$mmetgxz4mp#8_oy#*'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['cdc-iitdh.herokuapp.com/', 'localhost', '192.168.29.199']
ALLOWED_HOSTS = ['cdc-iitdh.herokuapp.com/', 'localhost', '192.168.29.199', '127.0.0.1']
# Application definition
@ -44,7 +44,8 @@ INSTALLED_APPS = [
'rest_framework',
'corsheaders',
'django_db_logger',
'background_task'
'background_task',
'simple_history',
]
MIDDLEWARE = [
@ -57,7 +58,7 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'simple_history.middleware.HistoryRequestMiddleware',
]
ROOT_URLCONF = 'CDC_Backend.urls'

View File

@ -5,12 +5,14 @@ certifi==2021.10.8
chardet==4.0.0
charset-normalizer==2.0.12
colorama==0.4.4
dill==0.3.5.1
dj-database-url==0.5.0
Django==3.2.13
django-background-tasks==1.2.5
django-compat==1.0.15
django-cors-headers==3.11.0
django-db-logger==0.1.12
django-simple-history==3.1.1
djangorestframework==3.13.1
google-auth==2.6.6
gunicorn==20.1.0
@ -26,17 +28,18 @@ platformdirs==2.5.1
psycopg2-binary==2.9.3
pyasn1==0.4.8
pyasn1-modules==0.2.8
PyJWT==2.4.0
pylint==2.13.5
python-dotenv==0.20.0
pytz==2022.1
PyJWT==2.4.0
requests==2.27.1
rsa==4.8
six==1.16.0
sqlparse==0.4.2
toml==0.10.2
typing_extensions==4.1.1
tomli==2.0.1
typing-extensions==4.1.1
urllib3==1.26.9
whitenoise==6.0.0
wrapt==1.14.0
zipp==3.8.0
wrapt==1.14.0