[UPDT] BASE: Added history for models using django audit log
This commit is contained in:
@@ -9,4 +9,4 @@
|
|||||||
</div>
|
</div>
|
||||||
{{ form.is_enable.errors }}
|
{{ form.is_enable.errors }}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -5,7 +5,11 @@ This module is used to write methods related to the history
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.core.paginator import Paginator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
from horilla.decorators import apply_decorators
|
||||||
|
|
||||||
|
|
||||||
class Bot:
|
class Bot:
|
||||||
@@ -146,3 +150,27 @@ def get_diff(instance):
|
|||||||
if track_fields:
|
if track_fields:
|
||||||
delta_changes = filter_history(delta_changes, track_fields)
|
delta_changes = filter_history(delta_changes, track_fields)
|
||||||
return delta_changes
|
return delta_changes
|
||||||
|
|
||||||
|
|
||||||
|
def history_tracking(request, obj_id, **kwargs):
|
||||||
|
model = kwargs.get("model")
|
||||||
|
decorator_strings = kwargs.get("decorators", [])
|
||||||
|
|
||||||
|
@apply_decorators(decorator_strings)
|
||||||
|
def _history_tracking(request, obj_id, model):
|
||||||
|
instance = model.objects.get(pk=obj_id)
|
||||||
|
histories = instance.horilla_history.all()
|
||||||
|
page_number = request.GET.get("page", 1)
|
||||||
|
paginator = Paginator(histories, 4)
|
||||||
|
page_obj = paginator.get_page(page_number)
|
||||||
|
context = {
|
||||||
|
"histories": page_obj,
|
||||||
|
"model_name": model,
|
||||||
|
}
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"horilla_audit/history_tracking.html",
|
||||||
|
context,
|
||||||
|
)
|
||||||
|
|
||||||
|
return _history_tracking(request, obj_id, model)
|
||||||
|
|||||||
144
horilla_audit/templates/horilla_audit/history_tracking.html
Normal file
144
horilla_audit/templates/horilla_audit/history_tracking.html
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
{% load static %} {% load i18n %} {% load audit_filters %}
|
||||||
|
<div class="oh-activity-sidebar__header" style="position:sticky; top:0; background:#fff; z-index:10">
|
||||||
|
<a
|
||||||
|
style="cursor: pointer"
|
||||||
|
onclick="$('.oh-activity-sidebar--show').removeClass('oh-activity-sidebar--show');"
|
||||||
|
>
|
||||||
|
<ion-icon
|
||||||
|
name="chevron-forward-outline"
|
||||||
|
class="oh-activity-sidebar__header-icon me-2 oh-activity-sidebar__close md hydrated"
|
||||||
|
data-target="#activitySidebar"
|
||||||
|
role="img"
|
||||||
|
aria-label="chevron forward outline"
|
||||||
|
></ion-icon>
|
||||||
|
</a>
|
||||||
|
<h1 class="oh-main__titlebar-title fw-bold ml-3 mb-3 mt-3">
|
||||||
|
{{model_name|verbose_name}} {% trans "History" %}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<div style="height:60vh; overflow-y:auto" id="infiniteContainer">
|
||||||
|
<div class="row" style="" id = "infinite">
|
||||||
|
{% if histories %} {% for history in histories %}
|
||||||
|
<div class="oh-history__container item">
|
||||||
|
<div
|
||||||
|
class="oh-history_date oh-card__title oh-card__title--sm fw-bold me-2"
|
||||||
|
>
|
||||||
|
<span class="oh-history_date-content">
|
||||||
|
<span class="dateformat_changer"
|
||||||
|
>{{ history.timestamp|date:"M. d, Y" }}</span
|
||||||
|
> , 
|
||||||
|
<span class="timeformat_changer"
|
||||||
|
>{{ history.timestamp|date:"g:i A" }}</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex">
|
||||||
|
<div class="oh-history_user-img">
|
||||||
|
{% if history.actor.employee_get %}
|
||||||
|
<img
|
||||||
|
src="{{history.actor.employee_get.get_avatar}}"
|
||||||
|
alt=""
|
||||||
|
class="oh-history_user-pic"
|
||||||
|
/>
|
||||||
|
{% else %}
|
||||||
|
<img
|
||||||
|
src="https://ui-avatars.com/api/?name=Horilla+Bot&background=random"
|
||||||
|
alt=""
|
||||||
|
class="oh-history_user-pic"
|
||||||
|
/>
|
||||||
|
{% endif %}
|
||||||
|
<div class="oh-history_user-state oh-user_inactive"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="oh-history_user-details">
|
||||||
|
<span class="oh-history__username"
|
||||||
|
>{% if history.actor.employee_get %} {{history.actor.employee_get.get_full_name}} {% else %} Horilla Bot {% endif %}</span
|
||||||
|
>
|
||||||
|
<div class="oh-history_abt pb-0">
|
||||||
|
<span class="oh-history_task-state">
|
||||||
|
{% if history.action == 0 %}
|
||||||
|
{% trans "Created" %} {{model_name|verbose_name}}
|
||||||
|
{% elif history.action == 1 %}
|
||||||
|
{% trans "Updated" %} {{model_name|verbose_name}}
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="oh-history_msg-container">
|
||||||
|
<div class="oh-history_task-tracker">
|
||||||
|
<ul class="ul">
|
||||||
|
{% for change,value in history.changes_dict.items %}
|
||||||
|
{% if value.type == 'm2m' %}
|
||||||
|
<li class="oh-history_task-list">
|
||||||
|
<div class="oh-history_track-value">
|
||||||
|
<span>{{model_name|verbose_name:change}}</span>
|
||||||
|
<img
|
||||||
|
src="{% static '/images/ui/arrow-right-line.svg' %}"
|
||||||
|
class="oh-progress_arrow"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
<span class="oh-history-task-state"
|
||||||
|
><i
|
||||||
|
>{{value.objects|join:", "}}</i
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for change,value in history.changes_display_dict.items %}
|
||||||
|
{% if not value.0 == "type" %}
|
||||||
|
<li class="oh-history_task-list">
|
||||||
|
<div class="oh-history_track-value">
|
||||||
|
<span>{{change}}</span>
|
||||||
|
<span class="oh-history_tracking-value"
|
||||||
|
><i>({{value.0}})</i></span
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="{% static '/images/ui/arrow-right-line.svg' %}"
|
||||||
|
class="oh-progress_arrow"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
<span class="oh-history-task-state"
|
||||||
|
><i>({{value.1}})</i></span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if histories.has_next and forloop.last %}
|
||||||
|
<div class="scrollButton" hx-get="{{request.META.PATH_INFO}}?page={{ histories.next_page_number }}"
|
||||||
|
hx-swap="beforeend" hx-target="#infinite" hx-select=".item"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
<div
|
||||||
|
class="d-flex justify-content-center align-items-center"
|
||||||
|
style="height: 40vh"
|
||||||
|
>
|
||||||
|
<h5 class="oh-404__subtitle">{% trans "No history found." %}</h5>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
document.body.addEventListener('htmx:load', function(evt) {
|
||||||
|
var container ="#infiniteContainer";
|
||||||
|
var $container = $(container)
|
||||||
|
var $lastItem = $('.row .item .scrollButton').last();
|
||||||
|
|
||||||
|
$container.on('scroll', function() {
|
||||||
|
if ($container[0].scrollTop + $container[0].clientHeight + 2 >= $container[0].scrollHeight) {
|
||||||
|
$lastItem.click();
|
||||||
|
$lastItem.remove()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -1,9 +1,5 @@
|
|||||||
from django import template
|
|
||||||
from django.core.paginator import Page, Paginator
|
|
||||||
from django.template.defaultfilters import register
|
from django.template.defaultfilters import register
|
||||||
|
|
||||||
from employee.models import Employee, EmployeeWorkInformation
|
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="fk_history")
|
@register.filter(name="fk_history")
|
||||||
def fk_history(instance, change):
|
def fk_history(instance, change):
|
||||||
@@ -18,3 +14,19 @@ def fk_history(instance, change):
|
|||||||
value = str(value) + f" (Previous {change['field']} deleted)"
|
value = str(value) + f" (Previous {change['field']} deleted)"
|
||||||
pass
|
pass
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter(name="verbose_name")
|
||||||
|
def verbose_name(model, field_name=None):
|
||||||
|
""" "
|
||||||
|
This method is used to fine the verbose name of a field
|
||||||
|
"""
|
||||||
|
if not field_name:
|
||||||
|
model_name = model._meta.verbose_name.capitalize()
|
||||||
|
return model_name
|
||||||
|
|
||||||
|
try:
|
||||||
|
field = model._meta.get_field(field_name)
|
||||||
|
return field.verbose_name
|
||||||
|
except:
|
||||||
|
return field_name
|
||||||
|
|||||||
@@ -441,7 +441,7 @@
|
|||||||
$(document).on("htmx:beforeRequest", function (event, data) {
|
$(document).on("htmx:beforeRequest", function (event, data) {
|
||||||
var response = event.detail.xhr.response;
|
var response = event.detail.xhr.response;
|
||||||
var target = $(event.detail.elt.getAttribute("hx-target"));
|
var target = $(event.detail.elt.getAttribute("hx-target"));
|
||||||
var avoid_target = ["BiometricDeviceTestFormTarget","reloadMessages"];
|
var avoid_target = ["BiometricDeviceTestFormTarget","reloadMessages", "infinite"];
|
||||||
if (!target.closest("form").length && avoid_target.indexOf(target.attr("id")) === -1) {
|
if (!target.closest("form").length && avoid_target.indexOf(target.attr("id")) === -1) {
|
||||||
target.html(`<div class="animated-background"></div>`);
|
target.html(`<div class="animated-background"></div>`);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user