Serialize History and expose to API

This commit is contained in:
Tanner Collin 2020-02-25 23:32:28 +00:00
parent 73362b69c6
commit ce4602018b
6 changed files with 59 additions and 8 deletions

View File

@ -131,10 +131,11 @@ class MetaInfo(models.Model):
class HistoryIndex(models.Model): class HistoryIndex(models.Model):
content_type = models.ForeignKey(ContentType, null=True, on_delete=models.SET_NULL) content_type = models.ForeignKey(ContentType, null=True, on_delete=models.SET_NULL)
object_id = models.PositiveIntegerField() object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id') history = GenericForeignKey('content_type', 'object_id')
owner_id = models.PositiveIntegerField() owner_id = models.PositiveIntegerField()
owner_name = models.TextField() owner_name = models.TextField()
object_name = models.TextField()
history_user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL) history_user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
history_date = models.DateTimeField() history_date = models.DateTimeField()
history_type = models.TextField() history_type = models.TextField()

View File

@ -41,6 +41,13 @@ class ReadOnly(BasePermission):
def has_object_permission(self, request, view, obj): def has_object_permission(self, request, view, obj):
return bool(request.method in SAFE_METHODS) return bool(request.method in SAFE_METHODS)
class IsAdmin(BasePermission):
def has_permission(self, request, view):
return bool(
request.user
and is_admin_director(request.user)
)
class IsAdminOrReadOnly(BasePermission): class IsAdminOrReadOnly(BasePermission):
def has_permission(self, request, view): def has_permission(self, request, view):
return bool( return bool(

View File

@ -396,3 +396,17 @@ class MyPasswordChangeSerializer(PasswordChangeSerializer):
raise ValidationError(dict(non_field_errors='Problem connecting to LDAP server: set.')) raise ValidationError(dict(non_field_errors='Problem connecting to LDAP server: set.'))
super().save() super().save()
class HistoryChangeSerializer(serializers.ModelSerializer):
class Meta:
model = models.HistoryChange
fields = ['field', 'old', 'new']
class HistorySerializer(serializers.ModelSerializer):
changes = HistoryChangeSerializer(many=True)
history_user = serializers.StringRelatedField()
class Meta:
model = models.HistoryIndex
fields = '__all__'

View File

@ -10,6 +10,9 @@ from .permissions import is_admin_director
def get_object_owner(obj): def get_object_owner(obj):
full_name = lambda member: member.first_name + ' ' + member.last_name full_name = lambda member: member.first_name + ' ' + member.last_name
if obj.__class__.__name__ == 'Member':
return full_name(obj), obj.id
if getattr(obj, 'user', False): if getattr(obj, 'user', False):
return full_name(obj.user.member), obj.user.member.id return full_name(obj.user.member), obj.user.member.id
@ -35,27 +38,45 @@ def post_create_historical_record_callback(
using, using,
**kwargs): **kwargs):
changes = history_instance.diff_against(history_instance.prev_record).changes history_type = history_instance.get_history_type_display()
object_name = instance.__class__.__name__
if changes: if object_name in ['User']: return
if history_type == 'Changed':
changes = history_instance.diff_against(history_instance.prev_record).changes
else:
changes = []
# it's possible for changes to be empty if model saved with no diff
if changes or history_type in ['Created', 'Deleted']:
owner = get_object_owner(instance) owner = get_object_owner(instance)
index = models.HistoryIndex.objects.create( index = models.HistoryIndex.objects.create(
content_object=history_instance, history=history_instance,
owner_name=owner[0],
owner_id=owner[1], owner_id=owner[1],
owner_name=owner[0],
object_name=object_name,
history_user=history_user, history_user=history_user,
history_date=history_instance.history_date, history_date=history_instance.history_date,
history_type=history_instance.get_history_type_display(), history_type=history_type,
revert_url=history_instance.revert_url(), revert_url=history_instance.revert_url(),
is_system=bool(history_user == None), is_system=bool(history_user == None),
is_admin=is_admin_director(history_user), is_admin=is_admin_director(history_user),
) )
change_old = change.old or ''
change_new = change.new or ''
if len(change_old) > 200:
change_old = change_old[:200] + '... [truncated]'
if len(change_new) > 200:
change_new = change_new[:200] + '... [truncated]'
for change in changes: for change in changes:
models.HistoryChange.objects.create( models.HistoryChange.objects.create(
index=index, index=index,
field=change.field, field=change.field,
old=change.old, old=change_old,
new=change.new, new=change_new,
) )

View File

@ -23,6 +23,7 @@ from .permissions import (
IsObjOwnerOrAdmin, IsObjOwnerOrAdmin,
IsSessionInstructorOrAdmin, IsSessionInstructorOrAdmin,
ReadOnly, ReadOnly,
IsAdmin,
IsAdminOrReadOnly, IsAdminOrReadOnly,
IsInstructorOrReadOnly IsInstructorOrReadOnly
) )
@ -413,6 +414,12 @@ class PasteView(views.APIView):
raise exceptions.ValidationError(dict(paste='This field is required.')) raise exceptions.ValidationError(dict(paste='This field is required.'))
class HistoryViewSet(Base, List, Retrieve):
permission_classes = [AllowMetadata | IsAdmin]
serializer_class = serializers.HistorySerializer
queryset = models.HistoryIndex.objects.order_by('-history_date')
class RegistrationView(RegisterView): class RegistrationView(RegisterView):
serializer_class = serializers.MyRegisterSerializer serializer_class = serializers.MyRegisterSerializer

View File

@ -15,6 +15,7 @@ router.register(r'stats', views.StatsViewSet, basename='stats')
router.register(r'search', views.SearchViewSet, basename='search') router.register(r'search', views.SearchViewSet, basename='search')
router.register(r'members', views.MemberViewSet, basename='members') router.register(r'members', views.MemberViewSet, basename='members')
router.register(r'courses', views.CourseViewSet, basename='course') router.register(r'courses', views.CourseViewSet, basename='course')
router.register(r'history', views.HistoryViewSet, basename='history')
router.register(r'sessions', views.SessionViewSet, basename='session') router.register(r'sessions', views.SessionViewSet, basename='session')
router.register(r'training', views.TrainingViewSet, basename='training') router.register(r'training', views.TrainingViewSet, basename='training')
router.register(r'transactions', views.TransactionViewSet, basename='transaction') router.register(r'transactions', views.TransactionViewSet, basename='transaction')