[FIX] HORILLA_VIEWS: Generic import issue fix

This commit is contained in:
Horilla
2025-07-21 12:24:32 +05:30
parent dfe0e5ad6e
commit 1f8a352ef8
7 changed files with 87 additions and 75 deletions

View File

@@ -4,7 +4,7 @@ employee/sidebar.py
To set Horilla sidebar for employee
"""
from django.urls import reverse
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as trans
from accessibility.methods import check_is_accessible
@@ -19,48 +19,48 @@ IMG_SRC = "images/ui/employees.svg"
SUBMENUS = [
{
"menu": trans("Profile"),
"redirect": reverse("employee-profile"),
"redirect": reverse_lazy("employee-profile"),
"accessibility": "employee.sidebar.profile_accessibility",
},
{
"menu": trans("Employees"),
"redirect": reverse("employee-view"),
"redirect": reverse_lazy("employee-view"),
"accessibility": "employee.sidebar.employee_accessibility",
},
{
"menu": trans("Document Requests"),
"redirect": reverse("document-request-view"),
"redirect": reverse_lazy("document-request-view"),
"accessibility": "employee.sidebar.document_accessibility",
},
{
"menu": trans("Shift Requests"),
"redirect": reverse("shift-request-view"),
"redirect": reverse_lazy("shift-request-view"),
},
{
"menu": trans("Work Type Requests"),
"redirect": reverse("work-type-request-view"),
"redirect": reverse_lazy("work-type-request-view"),
},
{
"menu": trans("Rotating Shift Assign"),
"redirect": reverse("rotating-shift-assign"),
"redirect": reverse_lazy("rotating-shift-assign"),
"accessibility": "employee.sidebar.rotating_shift_accessibility",
},
{
"menu": trans("Rotating Work Type Assign"),
"redirect": reverse("rotating-work-type-assign"),
"redirect": reverse_lazy("rotating-work-type-assign"),
"accessibility": "employee.sidebar.rotating_work_type_accessibility",
},
{
"menu": trans("Disciplinary Actions"),
"redirect": reverse("disciplinary-actions"),
"redirect": reverse_lazy("disciplinary-actions"),
},
{
"menu": trans("Policies"),
"redirect": reverse("view-policies"),
"redirect": reverse_lazy("view-policies"),
},
{
"menu": trans("Organization Chart"),
"redirect": reverse("organisation-chart"),
"redirect": reverse_lazy("organisation-chart"),
},
]
@@ -75,7 +75,7 @@ def profile_accessibility(request, submenu, user_perms, *args, **kwargs):
return accessible
# try:
# if accessible:
# submenu["redirect"] = reverse("employee-profile", kwargs={"obj_id": request.user.employee_get.id})
# submenu["redirect"] = reverse_lazy("employee-profile", kwargs={"obj_id": request.user.employee_get.id})
# except Exception:
# # If an exception occurs, do nothing
# pass

View File

@@ -21,7 +21,7 @@
<div class="oh-profile-section__card">
<div class="row">
<div class="col-12">{{form.errors}}</div>
{% for field in form.visible_fields %}
{% for field in form.visible_fields %}
{% if field.field.widget|is_select_multiple or field.field.widget|is_text_area %}
<div class="flex oh-label" for="id_{{ field.name }}">
<label
@@ -64,12 +64,12 @@
<div class="oh-switch" style="width: 30px">
{{ field|add_class:"oh-switch__checkbox" }}
</div>
{% else %}
{{ field|add_class:"form-control" }}
{% endif %}
{% else %}
{{ field|add_class:"form-control" }}
{% endif %}
{{field.errors}}
</div>
{% endif %}
{% endif %}
{% endfor %}
</div>

View File

@@ -910,7 +910,7 @@ def assign_related(
else:
reverse_obj_dict[field] = value
else:
instance = [
instances = [
data
for data in pk_values_mapping[reverse_field]
if getattr(
@@ -919,7 +919,8 @@ def assign_related(
record[reverse_field],
)
== record[reverse_field]
][0]
reverse_obj_dict.update({reverse_field: instance})
]
if instances:
instance = instances[0]
reverse_obj_dict.update({reverse_field: instance})
return reverse_obj_dict

View File

@@ -373,6 +373,8 @@ class HorillaListView(ListView):
current = record
for part in parts[:-1]:
current = current.setdefault(part, {})
if isinstance(value, float) and value.is_integer():
value = int(value)
current[parts[-1]] = value
serialized.append(record)
with_ref, without_ref = split_by_import_reference(serialized)
@@ -496,11 +498,13 @@ class HorillaListView(ListView):
bulk_base_fk_grouping = {}
bulk_create_reverse_related_grouping = {}
bulk_create_base_grouping = []
items = []
related_fields = list(
self.reverse_model_relation_to_base_model.keys()
)
fk_fields = self.fk_o2o_field_in_base_model
for record in records_to_import:
if record.get(update_reference_key):
del record[update_reference_key]
@@ -575,12 +579,17 @@ class HorillaListView(ListView):
records=items,
view=self,
)
if not items:
items = bulk_create_base_grouping
pre_generic_import.send(
sender=self.model,
records=items,
view=self,
)
self.model.objects.bulk_create(bulk_create_base_grouping)
post_generic_import.send(
sender=self.model,
records=items,

View File

@@ -1,4 +1,9 @@
{% load generic_template_filters i18n %}
<style>
.opacity-50{
opacity: .5!important;
}
</style>
{% if import_fields and import_accessibility %}
<div id="import_{{ view_id }}" class="oh-checkpoint-badge text-warning" style="cursor: pointer;color: hsl(211.72deg 91% 60%) !important;" data-toggle="oh-modal-toggle" data-target="#importModal{{ view_id }}" onclick="
ids = $('#{{ selected_instances_key_id }}').attr('data-ids');
@@ -13,7 +18,7 @@
<button class="oh-modal__close" aria-label="Close"><ion-icon name="close-outline" role="img"></ion-icon></button>
<div class="oh-modal__dialog-body p-0 pb-4">
<form hx-post="/{{ post_import_sheet_path }}" hx-encoding="multipart/form-data" class="oh-profile-section">
<div class="oh-modal__dialog-body mr-5" id="uploading" style="display: none">
<div class="oh-modal__dialog-body mr-5" id="uploading{{view_id}}" style="display: none">
<div class="loader-container">
<div class="loader"></div>
<div class="loader-text">Uploading...</div>
@@ -27,7 +32,7 @@
<span class="oh-dropdown__import-form-title">Upload a File</span>
<span class="oh-dropdown__import-form-text">Drag and drop files here</span>
</label>
<input type="file" name="file" required="" />
<input id="resumeUpload{{ view_id }}" type="file" name="file" required="" />
<div class="d-inline float-end">
<a onclick="
$('#submitGetImportSheet{{ view_id }}').click();
@@ -52,29 +57,19 @@
</div>
{% endif %}
<div class="modal-footer d-flex flex-row-reverse">
<input type="submit" class="oh-btn oh-btn--small oh-btn--secondary w-100 mt-3" value="Upload" />
<input
onclick="
if($('#resumeUpload{{ view_id }}').val()){
$('#uploading{{view_id}}').show();
$(this).addClass('opacity-50');
setTimeout(() => {
$(this).attr('type','button');
}, 100);
}
"
type="submit" class="oh-btn oh-btn--small oh-btn--secondary w-100 mt-3" value="Upload" />
</div>
</form>
<script>
let htmxBusy = false
document.body.addEventListener('htmx:configRequest', function (event) {
if (htmxBusy) {
event.preventDefault()
console.warn('Blocked concurrent HTMX request.')
} else {
htmxBusy = true
}
})
document.body.addEventListener('htmx:afterRequest', function () {
htmxBusy = false
})
document.body.addEventListener('htmx:responseError', function () {
htmxBusy = false
})
</script>
<form id="submitGetImportSheet{{ view_id }}Form" action="/{{ get_import_sheet_path }}" method="post">
{% csrf_token %}
<input type="text" name="selected_ids" hidden />

View File

@@ -2,9 +2,10 @@ from django.apps import AppConfig
class PgGitBackupConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'pg_backup'
default_auto_field = "django.db.models.BigAutoField"
name = "pg_backup"
def ready(self):
from pg_backup import scheduler
return super().ready()

View File

@@ -37,44 +37,43 @@ Backups can also be triggered manually by calling `backup_postgres()`.
"""
import os
import environ
import datetime
import subprocess
import shutil
import logging
import logging.config
import os
import shutil
import subprocess
from pathlib import Path
import environ
from apscheduler.schedulers.background import BackgroundScheduler
from django.conf import settings
# === Logging Configuration ===
LOGGING_CONFIG = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': '[%(asctime)s] [%(levelname)s] %(name)s: %(message)s'
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"standard": {"format": "[%(asctime)s] [%(levelname)s] %(name)s: %(message)s"},
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "standard",
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'standard',
"loggers": {
"pg_backup": {
"handlers": ["console"],
"level": "INFO",
"propagate": False,
},
},
'loggers': {
'pg_backup': {
'handlers': ['console'],
'level': 'INFO',
'propagate': False,
},
}
}
logging.config.dictConfig(LOGGING_CONFIG)
logger = logging.getLogger('pg_backup')
logger = logging.getLogger("pg_backup")
# === Configuration ===
@@ -90,7 +89,9 @@ db = settings.DATABASES["default"]
DB_ENGINE = db["ENGINE"]
if "postgresql" not in DB_ENGINE:
logger.warning("Skipping backup scheduler: not a PostgreSQL database (engine: %s)", DB_ENGINE)
logger.warning(
"Skipping backup scheduler: not a PostgreSQL database (engine: %s)", DB_ENGINE
)
else:
DB_NAME = db["NAME"]
DB_USER = db["USER"]
@@ -110,13 +111,18 @@ else:
command = [
shutil.which("pg_dump"),
"-h", DB_HOST,
"-p", DB_PORT,
"-U", DB_USER,
"-F", "c",
"-h",
DB_HOST,
"-p",
DB_PORT,
"-U",
DB_USER,
"-F",
"c",
"-b",
"-v",
"-f", str(backup_file),
"-f",
str(backup_file),
DB_NAME,
]
@@ -145,11 +151,11 @@ else:
hour, minute = map(int, time_str.split(":"))
scheduler.add_job(
backup_postgres,
'cron',
"cron",
hour=hour,
minute=minute,
id=f"backup_{hour}_{minute}",
replace_existing=True
replace_existing=True,
)
logger.info("Backup scheduled at %02d:%02d", hour, minute)
except ValueError: