[IMP] Remove inter module dependency (#274)

This commit introduces significant changes to the architecture of the Horilla HRMS system by decoupling interdependent modules. The following modifications were made:

1. **Module Independence**: Each module has been refactored to eliminate reliance on other modules, promoting a more modular and maintainable codebase.
2. **Refactored Imports and Dependencies**: Adjusted import statements and dependency injections to support independent module operation.
3. **Compatibility and Functionality**: Ensured that all modules are compatible with existing systems and maintain their intended functionality both independently and when integrated with other modules.

These changes enhance the modularity, maintainability, and scalability of the Horilla HRMS, allowing developers to work on individual modules without affecting the entire system. Future development and deployment will be more efficient and less prone to issues arising from tightly coupled code.

**NOTE**
For existing Horilla users, if you face any issues during the migrations, please run the following command and try again the migrations.

- `python3 manage.py makemigrations`
- `python3 manage.py migrate base`
- `python3 manage.py migrate`





* [IMP] ASSET: Asset module dependency removal from other Horilla apps

* [IMP] ATTENDANCE: Attendance module dependency removal from other Horilla apps

* [IMP] BASE: Base module dependency removal from other Horilla apps

* [IMP] EMPLOYEE: Employee module dependency removal from other Horilla apps

* [IMP] HELPDESK: Helpdesk module dependency removal from other Horilla apps

* [IMP] HORILLA AUDIT: Horilla Audit module dependency removal from other Horilla apps

* [IMP] HORILLA CRUMBS: Horilla Crumbs module dependency removal from other Horilla apps

* [IMP] HORILLA AUTOMATIONS: Horilla Automations module dependency removal from other Horilla apps

* [IMP] HORILLA VIEWS: Horilla Views module dependency removal from other Horilla apps

* [IMP] LEAVE: Leave module dependency removal from other Horilla apps

* [IMP] OFFBOARDING: Offboarding module dependency removal from other Horilla apps

* [IMP] ONBOARDING: Onboarding module dependency removal from other Horilla apps

* [IMP] PMS: PMS module dependency removal from other Horilla apps

* [IMP] PAYROLL: Payroll module dependency removal from other Horilla apps

* [IMP] RECRUITMENT: Recruitment module dependency removal from other Horilla apps

* [IMP] HORILLA: Dependency removal updates

* [IMP] TEMPLATES: Dependency removal updates

* [IMP] STATIC: Dependency removal updates

* [IMP] HORILLA DOCUMENTS: Horilla Documents module dependency removal from other Horilla apps

* [ADD] HORILLA: methods.py

* [UPDT] HORILLA: Settings.py

* [FIX] EMPLOYEE: About tab issue

* Update horilla_settings.py

* Remove dummy db init password
This commit is contained in:
Horilla
2024-08-05 14:22:44 +05:30
committed by GitHub
parent 746272d801
commit 2fee7c18bb
308 changed files with 12414 additions and 9577 deletions

View File

@@ -1,13 +1,5 @@
from django.contrib import admin
from horilla_views import models
from horilla_views.models import ActiveGroup, ActiveTab, ToggleColumn
admin.site.register(
[
models.ToggleColumn,
models.ActiveTab,
models.ActiveGroup,
models.SavedFilter,
models.ActiveView,
]
)
admin.site.register([ToggleColumn, ActiveTab, ActiveGroup])

View File

@@ -9,13 +9,7 @@ from venv import logger
from django import template
from django.contrib import messages
from django.core.cache import cache as CACHE
from django.core.paginator import Paginator
from django.db.models.fields.related import ForeignKey
from django.db.models.fields.related_descriptors import (
ForwardManyToOneDescriptor,
ReverseOneToOneDescriptor,
)
from django.http import HttpResponse
from django.middleware.csrf import get_token
from django.shortcuts import redirect, render
@@ -200,11 +194,10 @@ def get_short_uuid(length: int, prefix: str = "hlv"):
def update_initial_cache(request: object, cache: dict, view: object):
if cache.get(request.session.session_key + "cbv"):
cache.get(request.session.session_key + "cbv").update({view: {}})
if cache.get(request.session.session_key):
cache[request.session.session_key].update({view: {}})
return
cache.set(request.session.session_key + "cbv", {view: {}})
cache.update({request.session.session_key: {view: {}}})
return
@@ -225,28 +218,8 @@ class Reverse:
reverse: bool = True
page: str = ""
def __str__(self) -> str:
return str(self.reverse)
def getmodelattribute(value, attr: str):
"""
Gets an attribute of a model dynamically from a string name, handling related fields.
"""
result = value
attrs = attr.split("__")
for attr in attrs:
if hasattr(result, attr):
result = getattr(result, attr)
if isinstance(result, ForwardManyToOneDescriptor):
result = result.field.related_model
elif hasattr(result, "field") and isinstance(result.field, ForeignKey):
result = getattr(result.field.remote_field.model, attr, None)
elif hasattr(result, "related") and isinstance(
result, ReverseOneToOneDescriptor
):
result = getattr(result.related.related_model, attr, None)
return result
cache = {}
def sortby(
@@ -257,17 +230,16 @@ def sortby(
"""
request = getattr(_thread_locals, "request", None)
sort_key = query_dict[key]
if not CACHE.get(request.session.session_key + "cbvsortby"):
CACHE.set(request.session.session_key + "cbvsortby", Reverse())
CACHE.get(request.session.session_key + "cbvsortby").page = (
if not cache.get(request.session.session_key):
cache[request.session.session_key] = Reverse()
cache[request.session.session_key].page = (
"1" if not query_dict.get(page) else query_dict.get(page)
)
reverse_object = CACHE.get(request.session.session_key + "cbvsortby")
reverse = reverse_object.reverse
reverse = cache[request.session.session_key].reverse
none_ids = []
none_queryset = []
model = queryset.model
model_attr = getmodelattribute(model, sort_key)
model_attr = getattribute(model, sort_key)
is_method = isinstance(model_attr, types.FunctionType)
if not is_method:
none_queryset = queryset.filter(**{f"{sort_key}__isnull": True})
@@ -284,16 +256,19 @@ def sortby(
current_page = query_dict.get(page)
if current_page or is_first_sort:
order = not order
if reverse_object.page == current_page and not is_first_sort:
if (
cache[request.session.session_key].page == current_page
and not is_first_sort
):
order = not order
reverse_object.page = current_page
cache[request.session.session_key].page = current_page
try:
queryset = sorted(queryset, key=_sortby, reverse=order)
except TypeError:
none_queryset = list(queryset.filter(id__in=none_ids))
queryset = sorted(queryset.exclude(id__in=none_ids), key=_sortby, reverse=order)
reverse_object.reverse = order
cache[request.session.session_key].reverse = order
if order:
order = "asc"
queryset = list(queryset) + list(none_queryset)
@@ -302,7 +277,6 @@ def sortby(
order = "desc"
setattr(request, "sort_order", order)
setattr(request, "sort_key", sort_key)
CACHE.set(request.session.session_key + "cbvsortby", reverse_object)
return queryset
@@ -310,21 +284,22 @@ def update_saved_filter_cache(request, cache):
"""
Method to save filter on cache
"""
if cache.get(request.session.session_key + request.path + "cbv"):
cache.get(request.session.session_key + request.path + "cbv").update(
if cache.get(request.session.session_key):
cache[request.session.session_key].update(
{
"path": request.path,
"query_dict": request.GET,
# "request": request,
"request": request,
}
)
return cache
cache.set(
request.session.session_key + request.path + "cbv",
cache.update(
{
"path": request.path,
"query_dict": request.GET,
# "request": request,
},
request.session.session_key: {
"path": request.path,
"query_dict": request.GET,
"request": request,
}
}
)
return cache

View File

@@ -7,7 +7,6 @@ from django.template.loader import render_to_string
from django.utils.safestring import SafeText
from horilla.horilla_middlewares import _thread_locals
from horilla_views import models
class ToggleColumnForm(forms.Form):
@@ -35,43 +34,3 @@ class ToggleColumnForm(forms.Form):
context = {"form": self, "request": self.request}
table_html = render_to_string("generic/as_list.html", context)
return table_html
class SavedFilterForm(forms.ModelForm):
"""
SavedFilterForm
"""
color = forms.CharField(
widget=forms.TextInput(
attrs={
"class": "oh-input w-100",
"type": "color",
"placeholder": "Choose a color",
}
)
)
class Meta:
model = models.SavedFilter
fields = ["title", "is_default", "color"]
def structured(self):
"""
Render the form fields as HTML table rows with Bootstrap styling.
"""
request = getattr(_thread_locals, "request", None)
context = {
"form": self,
"request": request,
}
table_html = render_to_string("common_form.html", context)
return table_html
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
attrs = self.fields["title"].widget.attrs
attrs["class"] = "oh-input w-100"
attrs["placeholder"] = "Saved filter title"
if self.instance.pk:
self.verbose_name = self.instance.title

View File

@@ -3,15 +3,13 @@ horilla/generic/views.py
"""
import json
import re
from typing import Any
from urllib.parse import parse_qs
from bs4 import BeautifulSoup
from django import forms
from django.core.cache import cache as CACHE
from django.core.paginator import Page
from django.http import HttpRequest, HttpResponse, QueryDict
from django.shortcuts import render
from django.urls import resolve, reverse
from django.views.generic import DetailView, FormView, ListView, TemplateView
@@ -31,6 +29,9 @@ from horilla_views.cbv_methods import (
from horilla_views.forms import ToggleColumnForm
from horilla_views.templatetags.generic_template_filters import getattribute
cache = {}
saved_filters = {}
class HorillaListView(ListView):
"""
@@ -79,23 +80,18 @@ class HorillaListView(ListView):
sortby_key: str = "sortby"
sortby_mapping: list = []
selected_instances_key_id: str = "selectedInstances"
show_filter_tags: bool = True
filter_keys_to_remove: list = []
records_per_page: int = 50
export_fields: list = []
verbose_name: str = ""
def __init__(self, **kwargs: Any) -> None:
if not self.view_id:
self.view_id = get_short_uuid(4)
self.view_id = get_short_uuid(4)
super().__init__(**kwargs)
request = getattr(_thread_locals, "request", None)
self.request = request
update_initial_cache(request, CACHE, HorillaListView)
update_initial_cache(request, cache, HorillaListView)
# hidden columns configuration
existing_instance = models.ToggleColumn.objects.filter(
@@ -114,45 +110,27 @@ class HorillaListView(ListView):
self.visible_column.remove(column)
def get_queryset(self):
if not self.queryset:
queryset = super().get_queryset()
if self.filter_class:
query_dict = self.request.GET
if "filter_applied" in query_dict.keys():
update_saved_filter_cache(self.request, CACHE)
elif CACHE.get(
str(self.request.session.session_key) + self.request.path + "cbv"
):
query_dict = CACHE.get(
str(self.request.session.session_key)
+ self.request.path
+ "cbv"
)["query_dict"]
queryset = super().get_queryset()
default_filter = models.SavedFilter.objects.filter(
path=self.request.path,
created_by=self.request.user,
is_default=True,
).first()
if not bool(query_dict) and default_filter:
data = eval(default_filter.filter)
query_dict = QueryDict("", mutable=True)
for key, value in data.items():
query_dict[key] = value
if self.filter_class:
query_dict = self.request.GET
if "filter_applied" in query_dict.keys():
update_saved_filter_cache(self.request, saved_filters)
elif saved_filters.get(
str(self.request.session.session_key) + self.request.path
):
query_dict = saved_filters[
str(self.request.session.session_key) + self.request.path
]["query_dict"]
query_dict._mutable = False
self._saved_filters = query_dict
self.request.exclude_filter_form = True
self.queryset = self.filter_class(
data=query_dict, queryset=queryset, request=self.request
).qs
return self.queryset
self._saved_filters = query_dict
queryset = self.filter_class(query_dict, queryset).qs
return queryset
def get_context_data(self, **kwargs: Any):
context = super().get_context_data(**kwargs)
context["view_id"] = self.view_id
context["columns"] = self.visible_column
context["hidden_columns"] = list(set(self.columns) - set(self.visible_column))
context["toggle_form"] = self.toggle_form
context["search_url"] = self.search_url
@@ -168,25 +146,8 @@ class HorillaListView(ListView):
context["row_status_class"] = self.row_status_class
context["sortby_key"] = self.sortby_key
context["sortby_mapping"] = self.sortby_mapping
context["selected_instances_key_id"] = self.selected_instances_key_id
context["row_status_indications"] = self.row_status_indications
context["saved_filters"] = self._saved_filters
if not self.verbose_name:
self.verbose_name = self.model.__class__
context["model_name"] = self.verbose_name
context["export_fields"] = self.export_fields
referrer = self.request.GET.get("referrer", "")
if referrer:
# Remove the protocol and domain part
referrer = "/" + "/".join(referrer.split("/")[3:])
context["stored_filters"] = (
models.SavedFilter.objects.filter(
path=self.request.path, created_by=self.request.user
)
| models.SavedFilter.objects.filter(
referrer=referrer, created_by=self.request.user
)
).distinct()
context["select_all_ids"] = self.select_all
if self._saved_filters.get("field"):
@@ -209,13 +170,13 @@ class HorillaListView(ListView):
if value[0] in ["unknown", "on"] + self.filter_keys_to_remove
]
for key in keys_to_remove + ["referrer"]:
if key in data_dict.keys():
data_dict.pop(key)
for key in keys_to_remove:
data_dict.pop(key)
context["filter_dict"] = data_dict
request = self.request
ordered_ids = list(queryset.values_list("id", flat=True))
model = queryset.model
is_first_sort = False
query_dict = self.request.GET
if (
@@ -232,13 +193,8 @@ class HorillaListView(ListView):
queryset = sortby(
query_dict, queryset, self.sortby_key, is_first_sort=is_first_sort
)
ordered_ids = [instance.id for instance in queryset]
ordered_ids = []
if not self._saved_filters.get("field"):
for instance in queryset:
instance.ordered_ids = ordered_ids
ordered_ids.append(instance.pk)
setattr(model, "ordered_ids", ordered_ids)
context["queryset"] = paginator_qry(
queryset, self._saved_filters.get("page"), self.records_per_page
@@ -257,19 +213,13 @@ class HorillaListView(ListView):
context["groups"] = paginator_qry(
groups, self._saved_filters.get("page"), 10
)
for group in context["groups"]:
for instance in group["list"]:
instance.ordered_ids = ordered_ids
ordered_ids.append(instance.pk)
CACHE.get(self.request.session.session_key + "cbv")[HorillaListView] = context
cache[self.request.session.session_key][HorillaListView] = context
from horilla.urls import path, urlpatterns
self.export_path = f"export-list-view-{get_short_uuid(4)}/"
urlpatterns.append(path(self.export_path, self.export_data))
context["export_path"] = self.export_path
return context
def select_all(self, *args, **kwargs):
@@ -286,52 +236,30 @@ class HorillaListView(ListView):
request = getattr(_thread_locals, "request", None)
ids = eval(request.GET["ids"])
_columns = eval(request.GET["columns"])
queryset = self.model.objects.filter(id__in=ids)
_model = self.model
MODEL = self.model
FIELDS = self.visible_column
class HorillaListViewResorce(resources.ModelResource):
"""
Instant Resource class
"""
id = fields.Field(column_name="ID")
class Meta:
"""
Meta class for additional option
"""
model = _model
model = MODEL
fields = []
def dehydrate_id(self, instance):
"""
Dehydrate method for id field
"""
return instance.pk
for field_tuple in _columns:
dynamic_fn_str = f"def dehydrate_{field_tuple[1]}(self, instance):return self.remove_extra_spaces(getattribute(instance, '{field_tuple[1]}'))"
def before_export(self, queryset, *args, **kwargs):
return super().before_export(queryset, *args, **kwargs)
for field_tuple in FIELDS:
dynamic_fn_str = f"def dehydrate_{field_tuple[1]}(self, instance):return str(getattribute(instance, '{field_tuple[1]}'))"
exec(dynamic_fn_str)
dynamic_fn = locals()[f"dehydrate_{field_tuple[1]}"]
locals()[field_tuple[1]] = fields.Field(column_name=field_tuple[0])
def remove_extra_spaces(self, text):
"""
Remove blank space but keep line breaks and add new lines for <li> tags.
"""
soup = BeautifulSoup(str(text), "html.parser")
for li in soup.find_all("li"):
li.insert_before("\n")
li.unwrap()
text = soup.get_text()
lines = text.splitlines()
non_blank_lines = [line.strip() for line in lines if line.strip()]
cleaned_text = "\n".join(non_blank_lines)
return cleaned_text
book_resource = HorillaListViewResorce()
# Export the data using the resource
@@ -357,7 +285,7 @@ class HorillaSectionView(TemplateView):
super().__init__(**kwargs)
request = getattr(_thread_locals, "request", None)
self.request = request
update_initial_cache(request, CACHE, HorillaListView)
update_initial_cache(request, cache, HorillaListView)
nav_url: str = ""
view_url: str = ""
@@ -399,14 +327,17 @@ class HorillaDetailedView(DetailView):
super().__init__(**kwargs)
request = getattr(_thread_locals, "request", None)
self.request = request
update_initial_cache(request, CACHE, HorillaDetailedView)
update_initial_cache(request, cache, HorillaDetailedView)
def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
def get_context_data(self, **kwargs: Any):
context = super().get_context_data(**kwargs)
instance_ids = eval(str(self.request.GET.get(self.ids_key)))
pk = context["object"].pk
context["instance"] = context["object"]
url = resolve(self.request.path)
key = list(url.kwargs.keys())[0]
@@ -430,9 +361,7 @@ class HorillaDetailedView(DetailView):
context["actions"] = self.actions
context["action_method"] = self.action_method
CACHE.get(self.request.session.session_key + "cbv")[
HorillaDetailedView
] = context
cache[self.request.session.session_key][HorillaDetailedView] = context
return context
@@ -450,7 +379,7 @@ class HorillaTabView(TemplateView):
super().__init__(**kwargs)
request = getattr(_thread_locals, "request", None)
self.request = request
update_initial_cache(request, CACHE, HorillaTabView)
update_initial_cache(request, cache, HorillaTabView)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
@@ -462,7 +391,7 @@ class HorillaTabView(TemplateView):
context["active_target"] = active_tab.tab_target
context["tabs"] = self.tabs
CACHE.get(self.request.session.session_key + "cbv")[HorillaTabView] = context
cache[self.request.session.session_key][HorillaTabView] = context
return context
@@ -514,44 +443,23 @@ class HorillaCardView(ListView):
super().__init__(**kwargs)
request = getattr(_thread_locals, "request", None)
self.request = request
update_initial_cache(request, CACHE, HorillaCardView)
update_initial_cache(request, cache, HorillaCardView)
self._saved_filters = QueryDict()
def get_queryset(self):
if not self.queryset:
queryset = super().get_queryset()
if self.filter_class:
query_dict = self.request.GET
if "filter_applied" in query_dict.keys():
update_saved_filter_cache(self.request, CACHE)
elif CACHE.get(
str(self.request.session.session_key) + self.request.path + "cbv"
):
query_dict = CACHE.get(
str(self.request.session.session_key)
+ self.request.path
+ "cbv"
)["query_dict"]
queryset = super().get_queryset()
if self.filter_class:
query_dict = self.request.GET
if "filter_applied" in query_dict.keys():
update_saved_filter_cache(self.request, saved_filters)
elif saved_filters.get(self.request.session.session_key):
query_dict = saved_filters[self.request.session.session_key][
"query_dict"
]
self._saved_filters = query_dict
self.request.exclude_filter_form = True
self.queryset = self.filter_class(
query_dict, queryset, request=self.request
).qs
default_filter = models.SavedFilter.objects.filter(
path=self.request.path,
created_by=self.request.user,
is_default=True,
).first()
if not bool(query_dict) and default_filter:
data = eval(default_filter.filter)
query_dict = QueryDict("", mutable=True)
for key, value in data.items():
query_dict[key] = value
query_dict._mutable = False
return self.queryset
self._saved_filters = query_dict
queryset = self.filter_class(query_dict, queryset).qs
return queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
@@ -580,30 +488,11 @@ class HorillaCardView(ListView):
if value[0] in ["unknown", "on"] + self.filter_keys_to_remove
]
for key in keys_to_remove + ["referrer"]:
if key in data_dict.keys():
data_dict.pop(key)
for key in keys_to_remove:
data_dict.pop(key)
context["filter_dict"] = data_dict
ordered_ids = list(queryset.values_list("id", flat=True))
if not self._saved_filters.get("field"):
for instance in queryset:
instance.ordered_ids = ordered_ids
ordered_ids.append(instance.pk)
CACHE.get(self.request.session.session_key + "cbv")[HorillaCardView] = context
referrer = self.request.GET.get("referrer", "")
if referrer:
# Remove the protocol and domain part
referrer = "/" + "/".join(referrer.split("/")[3:])
context["stored_filters"] = (
models.SavedFilter.objects.filter(
path=self.request.path, created_by=self.request.user
)
| models.SavedFilter.objects.filter(
referrer=referrer, created_by=self.request.user
)
).distinct()
cache[self.request.session.session_key][HorillaCardView] = context
return context
@@ -615,6 +504,9 @@ class ReloadMessages(TemplateView):
return context
dynamic_create_cache = {}
def save(self: forms.ModelForm, commit=True, *args, **kwargs):
"""
This method is used to super save the form using custom logic
@@ -626,35 +518,28 @@ def save(self: forms.ModelForm, commit=True, *args, **kwargs):
if commit:
response = super(type(self), self).save(*args, **kwargs)
new_isntance_pk = self.instance.pk
CACHE.set(
request.session.session_key + "cbv" + dynamic_field,
{
"dynamic_field": dynamic_field,
"value": new_isntance_pk,
"model": self._meta.model,
},
)
dynamic_create_cache[request.session.session_key + dynamic_field] = {
"dynamic_field": dynamic_field,
"value": new_isntance_pk,
"form": self,
}
return response
from django.views.generic import UpdateView
class HorillaFormView(FormView):
"""
HorillaFormView
"""
class HttpResponse:
"""
HttpResponse
"""
def __new__(
self, content: str = "", targets_to_reload: list = [], script: str = ""
) -> HttpResponse:
"""
__new__ method
"""
targets_to_reload = list(set(targets_to_reload))
# targets_to_reload.append("#reloadMessagesButton")
targets_to_reload.append("#reloadMessagesButton")
script_id = get_short_uuid(4)
script = (
f"<script id='scriptTarget{script_id}'>"
@@ -679,7 +564,6 @@ class HorillaFormView(FormView):
view_id: str = get_short_uuid(4)
form_class: forms.ModelForm = None
template_name = "generic/horilla_form.html"
ids_key: str = "instance_ids"
form_disaply_attr: str = ""
new_display_title: str = "Add New"
close_button_attrs: str = """"""
@@ -693,7 +577,7 @@ class HorillaFormView(FormView):
super().__init__(**kwargs)
request = getattr(_thread_locals, "request", None)
self.request = request
update_initial_cache(request, CACHE, HorillaFormView)
update_initial_cache(request, cache, HorillaFormView)
if self.form_class:
setattr(self.form_class, "structured", structured)
@@ -717,29 +601,12 @@ class HorillaFormView(FormView):
context["dynamic_create_fields"] = self.dynamic_create_fields
context["form_class_path"] = self.form_class_path
context["view_id"] = self.view_id
pk = self.form.instance.pk
# next/previous option in the forms
if pk and self.request.GET.get(self.ids_key):
instance_ids = eval(str(self.request.GET.get(self.ids_key)))
url = resolve(self.request.path)
key = list(url.kwargs.keys())[0]
url_name = url.url_name
if instance_ids:
previous_id, next_id = closest_numbers(instance_ids, pk)
next_url = reverse(url_name, kwargs={key: next_id})
previous_url = reverse(url_name, kwargs={key: previous_id})
self.form.instance_ids = str(instance_ids)
self.form.ids_key = self.ids_key
self.form.next_url = next_url
self.form.previous_url = previous_url
return context
def get_form(self, form_class=None):
pk = self.kwargs.get("pk")
instance = self.model.objects.filter(pk=pk).first()
data = None
files = None
if self.request.method == "POST":
@@ -752,29 +619,18 @@ class HorillaFormView(FormView):
self.form_class_path = form.__class__.__module__ + "." + form.__class__.__name__
if self.request.method == "GET":
[
(
"employee_id",
FormView,
)
]
for dynamic_tuple in self.dynamic_create_fields:
view = dynamic_tuple[1]
view.display_title = "Dynamic create"
field = dynamic_tuple[0]
key = self.request.session.session_key + "cbv" + field
CACHE.set(
key,
{
"dynamic_field": field,
"value": getattribute(form.instance, field),
"model": form._meta.model,
},
)
dynamic_create_cache[self.request.session.session_key + field] = {
"dynamic_field": field,
"value": getattr(form.instance, field, ""),
"form": form,
}
from django.urls import path
from horilla.urls import urlpatterns
from horilla.urls import path, urlpatterns
urlpatterns.append(
path(
@@ -804,7 +660,7 @@ class HorillaFormView(FormView):
self.form_class.verbose_name = self.new_display_title
form.close_button_attrs = self.close_button_attrs
form.submit_button_attrs = self.submit_button_attrs
CACHE.get(self.request.session.session_key + "cbv")[HorillaFormView] = form
cache[self.request.session.session_key][HorillaFormView] = form
self.form = form
return form
@@ -836,7 +692,7 @@ class HorillaNavView(TemplateView):
super().__init__(**kwargs)
request = getattr(_thread_locals, "request", None)
self.request = request
update_initial_cache(request, CACHE, HorillaNavView)
update_initial_cache(request, cache, HorillaNavView)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
@@ -853,8 +709,5 @@ class HorillaNavView(TemplateView):
context["filter_instance_context_name"] = self.filter_instance
if self.filter_instance:
context[self.filter_form_context_name] = self.filter_instance.form
context["active_view"] = models.ActiveView.objects.filter(
path=self.request.path
).first()
CACHE.get(self.request.session.session_key + "cbv")[HorillaNavView] = context
cache[self.request.session.session_key][HorillaNavView] = context
return context

View File

@@ -50,38 +50,3 @@ class ActiveGroup(HorillaModel):
path = models.CharField(max_length=256)
group_target = models.CharField(max_length=256)
group_by_field = models.CharField(max_length=256)
class SavedFilter(HorillaModel):
"""
SavedFilter
"""
title = models.CharField(max_length=20, null=True)
color = models.CharField(max_length=10, default="")
is_default = models.BooleanField(default=False)
filter = models.TextField()
urlencode = models.TextField(default="")
path = models.CharField(max_length=256)
referrer = models.CharField(max_length=256, default="")
def save(self, *args, **kwargs):
SavedFilter.objects.filter(
is_default=True, path=self.path, created_by=self.created_by
).exclude(id=self.pk).update(is_default=False)
return super().save(*args, **kwargs)
def __str__(self) -> str:
return str(self.title)
class ActiveView(HorillaModel):
"""
This model to store the active view type for HNV
"""
path = models.CharField(max_length=256)
type = models.CharField(max_length=50)
def save(self, *args, **kwargs):
return super().save(*args, **kwargs)

View File

@@ -1,16 +1,6 @@
{% load i18n %} {% load basefilters %}
<!-- filter items showing here -->
<div
class="oh-modal"
id="savedFilterModal"
role="dialog"
aria-labelledby="savedFilterModal"
aria-hidden="true"
>
<div class="oh-modal__dialog" style="max-width: 550px" id="SavedFilterFormTarget"></div>
</div>
<button hx-get='{% url "saved-filter" %}?path={{request.path}}&referrer={{request.META.HTTP_REFERER}}&{{saved_filters.urlencode}}' hx-target="#SavedFilterFormTarget" hidden id="loadsavedfilterform"></button>
<div style="display: none">{{filter_dict}}</div>
<script>
function fieldLabel(value, field) {
@@ -57,26 +47,23 @@
{% endif %}
{%for field,values in filter_dict.items %} {% if values %}
{% with translation_field=field|filter_field %}
{% if field != "path" %}
<span class="oh-titlebar__tag filter-field" >
{% trans translation_field %} :
{% for value in values %}
${fieldLabel("{% trans value %}", "{{field}}")}
{% endfor %}
{% for value in values %}
${fieldLabel("{% trans value %}", "{{field}}")}
{% endfor %}
<button class="oh-titlebar__tag-close" onclick="clearFilterFromTag($(this))" data-x-field="{{field}}">
<ion-icon
name="close-outline"
role="img"
class="md hydrated close-icon"
aria-label="close outline"
>
>
</ion-icon>
</button>
</span>
{% endif %}
{% endwith %} {% endif %} {% endfor %}
{% if filter_dict %}
<button class="oh-titlebar__save" onclick="$('#loadsavedfilterform').click();$('#savedFilterModal').addClass('oh-modal--show')" name="save" aria-label="save" title='{% trans "Save filter" %}''><ion-icon name="bookmark-outline"></ion-icon>Save</button></span>
<span class="oh-titlebar__tag oh-titlebar__tag--custom" title="{% trans 'Clear All' %}" role="button" onclick="clearAllFilter($(this));"
><ion-icon class="close-icon" name="close-outline"></ion-icon></span
>

View File

@@ -1,5 +1,4 @@
{% load widget_tweaks %} {% load i18n %}
{% load generic_template_filters %}
<style>
.condition-highlight {
background-color: #ffa5000f;
@@ -15,36 +14,15 @@
</button>
</div>
{% endif %}
<div class="oh-modal__dialog-body oh-modal__dialog-relative">
{% if form.instance_ids %}
<div class="oh-modal__dialog oh-modal__dialog--navigation m-0 p-0">
<button
hx-get="{{form.previous_url}}?{{form.ids_key}}={{form.instance_id}}&{{request.GET.urlencode}}"
hx-swap="innerHTML"
hx-target="#genericModalBody"
class="oh-modal__diaglog-nav oh-modal__nav-prev"
>
<ion-icon name="chevron-back-outline"></ion-icon>
</button>
<button
hx-get="{{form.next_url}}?{{form.ids_key}}={{form.instance_id}}&{{request.GET.urlencode}}"
hx-swap="innerHTML"
hx-target="#genericModalBody"
class="oh-modal__diaglog-nav oh-modal__nav-next"
>
<ion-icon name="chevron-forward-outline"></ion-icon>
</button>
</div>
{% endif %}
<div class="oh-modal__dialog-body">
<div class="oh-general__tab-target oh-profile-section" id="personal">
<div class="oh-profile-section__card row">
<div class="row" style="padding-right: 0;">
<div class="col-12" style="padding-right: 0;">{{ form.non_field_errors }}</div>
<div class="row">
<div class="col-12">{{ form.non_field_errors }}</div>
{% for field in form.visible_fields %}
<div class="col-12 col-md-{{field|col}}" id="id_{{ field.name }}_parent_div" style="padding-right: 0;">
<div class="col-12 col-md-6">
<div class="oh-label__info" for="id_{{ field.name }}">
<label class="oh-label {% if field.field.required %} required-star{% endif %}" for="id_{{ field.name }}"
<label class="oh-label" for="id_{{ field.name }}"
>{% trans field.label %}</label
>
{% if field.help_text != '' %}
@@ -61,10 +39,8 @@
</div>
{% else %}
<div id="dynamic_field_{{field.name}}">
{{ field|add_class:'form-control' }}
{{ field.errors }}
{{ field|add_class:'form-control' }} {% endif %} {{ field.errors }}
</div>
{% endif %}
</div>
{% endfor %}
</div>

View File

@@ -1,18 +1,13 @@
{% load static i18n generic_template_filters %}
<div id="{{view_id|safe}}">
<script>
if (!$(".HTV").length) {
$("#reloadMessagesButton").click()
}
</script>
{% if bulk_select_option %}
<div class="d-flex justify-content-between">
<div>
<div class="oh-checkpoint-badge text-success"
onclick="
addToSelectedId({{select_all_ids|safe}},'{{selected_instances_key_id}}');
selectSelected('#{{view_id|safe}}','{{selected_instances_key_id}}');
reloadSelectedCount($('#count_{{view_id|safe}}'),'{{selected_instances_key_id}}');
addToSelectedId({{select_all_ids|safe}});
selectSelected('#{{view_id|safe}}');
reloadSelectedCount($('#count_{{view_id|safe}}'));
"
style="cursor: pointer;">
{% trans "Select All" %} ({{queryset.paginator.count}})
@@ -22,9 +17,9 @@
class="oh-checkpoint-badge text-secondary d-none"
style="cursor: pointer;"
onclick="
$('#{{selected_instances_key_id}}').attr('data-ids',JSON.stringify([]));
selectSelected('#{{view_id|safe}}','{{selected_instances_key_id}}');
reloadSelectedCount($('#count_{{view_id|safe}}'),'{{selected_instances_key_id}}');
$('#selectedInstances').attr('data-ids',JSON.stringify([]));
selectSelected('#{{view_id|safe}}');
reloadSelectedCount($('#count_{{view_id|safe}}'));
$('#{{view_id}} .list-table-row').prop('checked',false);
$('#{{view_id}} .highlight-selected').removeClass('highlight-selected');
$('#{{view_id}} .bulk-list-table-row').prop('checked',false);
@@ -44,31 +39,12 @@
class="oh-checkpoint-badge text-info d-none"
style="cursor: pointer;"
onclick="
selectedIds = $('#{{selected_instances_key_id}}').attr('data-ids')
selectedIds = $('#selectedInstances').attr('data-ids')
window.location.href = '{{export_path}}' + '?ids='+selectedIds
"
>
{% trans "Export" %}
</div>
{% for filter in stored_filters %}
<div class="oh-hover-btn-container"
hx-get="{{request.path}}?{{filter.urlencode}}"
hx-target="#{{view_id|safe}}" hx-swap="outerHTML"
>
<button class="oh-hover-btn" style="
cursor: pointer;
border: solid 2px {{filter.color}};
color: {{filter.color}} !important;
">
{{filter.title}}
</button>
<div class="oh-hover-btn-drawer" onclick="event.stopPropagation()">
<button class="oh-hover-btn__small" onclick="$('#savedFilterModal').addClass('oh-modal--show')" hx-get="{% url "saved-filter-update" filter.id %}" hx-target="#SavedFilterFormTarget" hx-swap="innerHTML"><ion-icon name="create-outline"></ion-icon></button>
<button class="oh-hover-btn__small" onclick="$(this).parent().find('button:hidden').click();$(this).closest('.oh-hover-btn-container').remove()" ><ion-icon name="trash-outline"></ion-icon></button>
<button hidden hx-get="{% url "delete-saved-filter" filter.id %}" hx-swap="none"></button>
</div>
</div>
{% endfor %}
</div>
{% if row_status_indications %}
<div class="d-flex flex-row-reverse">
@@ -97,6 +73,7 @@
onclick="
event.stopPropagation()
$(this).parent().find('.oh-accordion-meta__body').toggleClass('d-none')
$(this).toggleClass('oh-accordion-meta__header--show')
"
>
<span class="oh-accordion-meta__title p-2">
@@ -135,7 +112,7 @@
onchange="
$(this).closest('.oh-sticky-table').find('.list-table-row').prop('checked',$(this).is(':checked')).change();
$(document).ready(function () {
reloadSelectedCount($('#count_{{view_id|safe}}'),'{{selected_instances_key_id}}');
reloadSelectedCount($('#count_{{view_id|safe}}'));
});
"
title="Select All"
@@ -186,7 +163,7 @@
if (!element.is(':checked')) {
removeId(element)
}
reloadSelectedCount($('#count_{{view_id|safe}}'),'{{selected_instances_key_id}}');
reloadSelectedCount($('#count_{{view_id|safe}}'));
});
"
value = "{{instance.pk}}"
@@ -199,7 +176,7 @@
<div
class="{% if index == 1 %} oh-sticky-table__sd {% else %} oh-sticky-table__td{% endif %}"
>
{{instance|getattribute:attribute|selected_format:request.user.employee_get.employee_work_info.company_id|safe}}
{{instance|getattribute:attribute|safe}}
</div>
{% else %}
<div
@@ -385,7 +362,7 @@
{% if bulk_select_option %}
<script>
ids = JSON.parse(
$("#{{selected_instances_key_id}}").attr("data-ids") || "[]"
$("#selectedInstances").attr("data-ids") || "[]"
);
$.each(ids, function (indexInArray, valueOfElement) {
$(`#{{view_id|safe}} .oh-sticky-table__tbody .list-table-row[value=${valueOfElement}]`).prop("checked",true).change()
@@ -395,7 +372,7 @@
) {
id = $(this).val()
ids = JSON.parse(
$("#{{selected_instances_key_id}}").attr("data-ids") || "[]"
$("#selectedInstances").attr("data-ids") || "[]"
);
ids = Array.from(new Set(ids));
let index = ids.indexOf(id);
@@ -406,7 +383,7 @@
ids.splice(index, 1);
}
}
$("#{{selected_instances_key_id}}").attr("data-ids", JSON.stringify(ids));
$("#selectedInstances").attr("data-ids", JSON.stringify(ids));
}
);
@@ -418,11 +395,6 @@
$(".oh-accordion-meta__header").click(function (e) {
var open = $(this).attr("data-open");
open = JSON.parse(open)
if (!$(this).parent().parent().find(".oh-accordion-meta__body.d-none").length && !$(this).find(".oh-accordion-meta__header--show").length) {
$(this).removeClass("oh-accordion-meta__header--show");
}else{
$(this).addClass("oh-accordion-meta__header--show");
}
$(this).attr("data-open", !open);
var field = $(this).attr("data-field");
var groupIndex = $(this).attr("data-group");

View File

@@ -1,42 +1,16 @@
{% load static i18n generic_template_filters %}
<div id="{{view_id|safe}}">
<script>
if (!$(".HTV").length) {
$("#reloadMessagesButton").click()
}
</script>
<div class="d-flex justify-content-between mb-2">
<div>
{% for filter in stored_filters %}
<div class="oh-hover-btn-container"
hx-get="{{request.path}}?{{filter.urlencode}}"
hx-target="#{{view_id|safe}}" hx-swap="outerHTML"
>
<button class="oh-hover-btn" style="
cursor: pointer;
border: solid 2px {{filter.color}};
color: {{filter.color}} !important;
">
{{filter.title}}
</button>
<div class="oh-hover-btn-drawer" onclick="event.stopPropagation()">
<button class="oh-hover-btn__small" onclick="$('#savedFilterModal').addClass('oh-modal--show')" hx-get="{% url "saved-filter-update" filter.id %}" hx-target="#SavedFilterFormTarget" hx-swap="innerHTML"><ion-icon name="create-outline"></ion-icon></button>
<button class="oh-hover-btn__small" onclick="$(this).parent().find('button:hidden').click();$(this).closest('.oh-hover-btn-container').remove()" ><ion-icon name="trash-outline"></ion-icon></button>
<button hidden hx-get="{% url "delete-saved-filter" filter.id %}" hx-swap="none"></button>
</div>
</div>
{% endfor %}
</div>
{% if card_status_indications %}
<div class="d-flex flex-row-reverse">
{% for indication in card_status_indications %}
<span class="m-1" style="cursor: pointer;margin-left: 7px;" {{indication.2|safe}}>
<span class="oh-dot oh-dot--small me-1 {{indication.0}}"></span>
{{indication.1}}
</span>
{% endfor %}
</div>
{% if queryset|length %}
{% if card_status_indications %}
<div class="d-flex flex-row-reverse">
{% for indication in card_status_indications %}
<span class="m-1" style="cursor: pointer;margin-left: 7px;" {{indication.2|safe}}>
<span class="oh-dot oh-dot--small me-1 {{indication.0}}"></span>
{{indication.1}}
</span>
{% endfor %}
</div>
{% endif %}
<button class="reload-record" hidden hx-get="{{request.path}}?{{request.GET.urlencode}}" hx-target="#{{view_id|safe}}" hx-swap="outerHTML">
</button>
{% if show_filter_tags %}
@@ -55,8 +29,8 @@
</div>
</div>
<div class="oh-kanban-card__details">
<span class="oh-kanban-card__title">{{details.title|format:instance|selected_format:request.user.employee_get.employee_work_info.company_id|safe}}</span>
<span class="oh-kanban-card__subtitle">{{details.subtitle|format:instance|selected_format:request.user.employee_get.employee_work_info.company_id|safe}}</span>
<span class="oh-kanban-card__title">{{details.title|format:instance|safe}}</span>
<span class="oh-kanban-card__subtitle">{{details.subtitle|format:instance|safe}}</span>
</div>
{% if actions %}
<div class="oh-kanban-card__dots" onclick="event.stopPropagation()">

View File

@@ -48,13 +48,13 @@
</div>
<div class="oh-timeoff-modal__profile-info">
<span class="oh-timeoff-modal__user fw-bold">
{{object|getattribute:header.title|selected_format:request.user.employee_get.employee_work_info.company_id}}
{{object|getattribute:header.title}}
</span>
<span
class="oh-timeoff-modal__user m-0"
style="font-size: 18px; color: #4d4a4a"
>
{{object|getattribute:header.subtitle|selected_format:request.user.employee_get.employee_work_info.company_id}}</span
{{object|getattribute:header.subtitle}}</span
>
</div>
</div>
@@ -67,16 +67,12 @@
<div class="row">
{% for col in body %}
<div class="col-6 mt-3">
{% if not col.2 %}
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{{col.0}}</span>
<span class="oh-timeoff-modal__stat-count"
>{{object|getattribute:col.1|selected_format:request.user.employee_get.employee_work_info.company_id|safe}}</span
>{{object|getattribute:col.1|safe}}</span
>
</div>
{% else %}
{{object|getattribute:col.1|safe}}
{% endif %}
</div>
{% endfor %}
</div>

View File

@@ -12,12 +12,12 @@
></div>
</div>
{% endfor %}
<form id="{{view_id}}" hx-post="{{request.path}}?{{request.GET.urlencode}}" hx-encoding="multipart/form-data" hx-swap="outerHTML">{{form.structured}}</form>
<form id="{{view_id}}" hx-post="{{request.path}}?{{request.GET.urlencode}}" hx-swap="outerHTML">{{form.structured}}</form>
{% for field_tuple in dynamic_create_fields %}
<button
hidden
id="modalButton{{field_tuple.0}}"
hx-get="/dynamic-path-{{field_tuple.0}}-{{request.session.session_key}}?dynamic_field={{field_tuple.0}}"
hx-get="dynamic-path-{{field_tuple.0}}-{{request.session.session_key}}?dynamic_field={{field_tuple.0}}"
hx-target="#dynamicModal{{field_tuple.0}}Body"
onclick="$('#dynamicModal{{field_tuple.0}}').addClass('oh-modal--show');"
>

View File

@@ -1,11 +1,5 @@
{% load static i18n generic_template_filters %}
<div id="{{view_id|safe}}">
{% include "generic/export_fields_modal.html" %}
<script>
if (!$(".HTV").length) {
$("#reloadMessagesButton").click()
}
</script>
<button class="reload-record" hidden hx-get="{{request.path}}?{{saved_filters.urlencode}}" hx-target="#{{view_id|safe}}" hx-swap="outerHTML">
</button>
{% if show_filter_tags %} {% include "generic/filter_tags.html" %} {% endif %}
@@ -15,9 +9,9 @@
<div>
<div class="oh-checkpoint-badge text-success"
onclick="
addToSelectedId({{select_all_ids|safe}},'{{selected_instances_key_id}}');
selectSelected('#{{view_id|safe}}','{{selected_instances_key_id}}');
reloadSelectedCount($('#count_{{view_id|safe}}'),'{{selected_instances_key_id}}');
addToSelectedId({{select_all_ids|safe}});
selectSelected('#{{view_id|safe}}');
reloadSelectedCount($('#count_{{view_id|safe}}'));
"
style="cursor: pointer;">
{% trans "Select All" %} ({{queryset.paginator.count}})
@@ -27,9 +21,9 @@
class="oh-checkpoint-badge text-secondary d-none"
style="cursor: pointer;"
onclick="
$('#{{selected_instances_key_id}}').attr('data-ids',JSON.stringify([]));
selectSelected('#{{view_id|safe}}','{{selected_instances_key_id}}');
reloadSelectedCount($('#count_{{view_id|safe}}'),'{{selected_instances_key_id}}');
$('#selectedInstances').attr('data-ids',JSON.stringify([]));
selectSelected('#{{view_id|safe}}');
reloadSelectedCount($('#count_{{view_id|safe}}'));
$('#{{view_id}} .list-table-row').prop('checked',false);
$('#{{view_id}} .highlight-selected').removeClass('highlight-selected');
$('#{{view_id}} .bulk-list-table-row').prop('checked',false);
@@ -48,31 +42,13 @@
id="export_{{view_id}}"
class="oh-checkpoint-badge text-info d-none"
style="cursor: pointer;"
data-toggle="oh-modal-toggle"
data-target="#exportFields{{view_id|safe}}"
onclick="
selectedIds = $('#selectedInstances').attr('data-ids')
window.location.href = '/{{export_path}}' + '?ids='+selectedIds
"
>
{% trans "Export" %}
</div>
{% for filter in stored_filters %}
<div class="oh-hover-btn-container"
hx-get="{{request.path}}?{{filter.urlencode}}"
hx-target="#{{view_id|safe}}" hx-swap="outerHTML"
>
<button class="oh-hover-btn" style="
cursor: pointer;
border: solid 2px {{filter.color}};
color: {{filter.color}} !important;
">
{{filter.title}}
</button>
<div class="oh-hover-btn-drawer" onclick="event.stopPropagation()">
<button class="oh-hover-btn__small" onclick="$('#savedFilterModal').addClass('oh-modal--show')" hx-get="{% url "saved-filter-update" filter.id %}" hx-target="#SavedFilterFormTarget" hx-swap="innerHTML"><ion-icon name="create-outline"></ion-icon></button>
<button class="oh-hover-btn__small" onclick="$(this).parent().find('button:hidden').click();$(this).closest('.oh-hover-btn-container').remove()" ><ion-icon name="trash-outline"></ion-icon></button>
<button hidden hx-get="{% url "delete-saved-filter" filter.id %}" hx-swap="none"></button>
</div>
</div>
{% endfor %}
</div>
{% if row_status_indications %}
<div class="d-flex flex-row-reverse">
@@ -132,7 +108,7 @@
onchange="
$(this).closest('.oh-sticky-table').find('.list-table-row').prop('checked',$(this).is(':checked')).change();
$(document).ready(function () {
reloadSelectedCount($('#count_{{view_id|safe}}'),'{{selected_instances_key_id}}');
reloadSelectedCount($('#count_{{view_id|safe}}'));
});
"
title="Select All"
@@ -207,7 +183,7 @@
if (!element.is(':checked')) {
removeId(element)
}
reloadSelectedCount($('#count_{{view_id|safe}}'),'{{selected_instances_key_id}}');
reloadSelectedCount($('#count_{{view_id|safe}}'));
});
"
value = "{{instance.pk}}"
@@ -219,7 +195,7 @@
<div
class="{% if index == 1 %} oh-sticky-table__sd {% else %} oh-sticky-table__td{% endif %}"
>
{{instance|getattribute:attribute|selected_format:request.user.employee_get.employee_work_info.company_id|safe}}
{{instance|getattribute:attribute|safe}}
</div>
{% else %}
<div
@@ -353,7 +329,7 @@
</nav>
</div>
<script>
reloadSelectedCount($('#count_{{view_id|safe}}'),'{{selected_instances_key_id}}');
reloadSelectedCount($('#count_{{view_id|safe}}'));
var tabId = $("#{{view_id}}").closest(".oh-tabs__content").attr("id");
let badge = $(`#badge-${tabId}`);
let count = "{{queryset.paginator.count}}";
@@ -364,10 +340,11 @@
</script>
{% if bulk_select_option %}
<script>
selectSelected("#{{view_id|safe}}",'{{selected_instances_key_id}}')
selectSelected("#{{view_id|safe}}")
</script>
{% endif %}
{% endif %}
</div>
<script>
$("ul[data-search-url] a").click(function (e) {
e.preventDefault();

View File

@@ -14,7 +14,7 @@
></ion-icon>
</a>
</div>
<form autocomplete="off" id="filterForm" onsubmit="event.preventDefault()" hx-get="{{search_url}}?&referrer={{request.META.HTTP_REFERER}}&{{request.GET.urlencode}}" hx-replace-url="true" hx-target="{{search_swap_target}}" class="oh-main__titlebar oh-main__titlebar--right">
<form autocomplete="off" id="filterForm" onsubmit="event.preventDefault()" hx-get="{{search_url}}" hx-replace-url="true" hx-target="{{search_swap_target}}" class="oh-main__titlebar oh-main__titlebar--right">
<div class="oh-input-group oh-input__search-group" id="searchGroup">
<ion-icon
name="search-outline"
@@ -73,20 +73,11 @@
{% if view_types %}
<ul class="oh-view-types">
{% for type in view_types %}
<li class="oh-view-type" data-url="{{type.url}}" data-type="{{type.type}}" hx-get="{% url "active-hnv-view-type" %}?view={{type.type}}&path={{request.path}}" hx-swap="none" onclick="$(this).closest('form').attr('hx-get','{{type.url}}?&referrer={{request.META.HTTP_REFERER}}&{{request.GET.urlencode}}');$(this).closest('form').find('#applyFilter').click();
<li class="oh-view-type" onclick="$(this).closest('form').attr('hx-get','{{type.url}}');$(this).closest('form').find('#applyFilter').click();
">
<a class="oh-btn oh-btn--view" {{type.attrs|safe}}
><ion-icon name="{{type.icon}}"></ion-icon
></a>
{% if active_view.type == type.type %}
<script>
$("form#filterForm.oh-main__titlebar oh-main__titlebar--right").attr('hx-get','{{type.url}}?&referrer={{request.META.HTTP_REFERER}}&{{request.GET.urlencode}}');
setTimeout(() => {
$(".oh-view-types .oh-view-type[data-type={{type.type}}]").click()
$(".oh-view-types .oh-view-type[data-type={{type.type}}] a").addClass("oh-btn--view-active")
}, 10);
</script>
{% endif %}
</li>
{% endfor %}
</ul>

View File

@@ -4,11 +4,7 @@
{% comment %} {% include "attendance/attendance/attendance_nav.html" %} {% endcomment %}
{% load i18n generic_template_filters %}
<div class="oh-tabs HTV">
<script>
$("#reloadMessagesButton").click()
</script>
<div class="HTV"></div>
<div class="oh-tabs">
<ul class="oh-tabs__tablist">
{% for tab in tabs %}
<li

View File

@@ -1,11 +1,10 @@
"""
attendancefilters.py
horillafilters.py
This module is used to write custom template filters.
"""
import datetime
import re
import types
@@ -22,38 +21,6 @@ register = template.Library()
numeric_test = re.compile("^\d+$")
date_format_mapping = {
"DD-MM-YYYY": "%d-%m-%Y",
"DD.MM.YYYY": "%d.%m.%Y",
"DD/MM/YYYY": "%d/%m/%Y",
"MM/DD/YYYY": "%m/%d/%Y",
"YYYY-MM-DD": "%Y-%m-%d",
"YYYY/MM/DD": "%Y/%m/%d",
"MMMM D, YYYY": "%B %d, %Y",
"DD MMMM, YYYY": "%d %B, %Y",
"MMM. D, YYYY": "%b. %d, %Y",
"D MMM. YYYY": "%d %b. %Y",
"dddd, MMMM D, YYYY": "%A, %B %d, %Y",
}
time_format_mapping = {
"hh:mm A": "%I:%M %p",
"HH:mm": "%H:%M",
}
@register.filter(name="selected_format")
def selected_format(date: datetime.date, company: object = None) -> str:
if company and (company.date_format or company.time_format):
if isinstance(date, datetime.date):
format = company.date_format
date_format_mapping.get(format)
return date.strftime(date_format_mapping[format])
elif isinstance(date, datetime.time):
format = company.time_format
return date.strftime(time_format_mapping[format])
return date
@register.filter(name="getattribute")
def getattribute(value, attr: str):
@@ -110,13 +77,3 @@ def accessibility(method: str, instance=None):
PermWrapper(request.user),
)
return True
@register.filter("col")
def col(field: object):
"""
Method to get field col sepration
"""
field_name = field.name
cols = getattr(field.form, "cols", {})
return cols.get(field_name, 6)

View File

@@ -13,20 +13,4 @@ urlpatterns = [
path("active-group", views.ActiveGroup.as_view(), name="cbv-active-group"),
path("reload-field", views.ReloadField.as_view(), name="reload-field"),
path("reload-messages", ReloadMessages.as_view(), name="reload-messages"),
path("saved-filter/", views.SavedFilter.as_view(), name="saved-filter"),
path(
"saved-filter/<int:pk>/",
views.SavedFilter.as_view(),
name="saved-filter-update",
),
path(
"delete-saved-filter/<int:pk>/",
views.DeleteSavedFilter.as_view(),
name="delete-saved-filter",
),
path(
"active-hnv-view-type/",
views.ActiveView.as_view(),
name="active-hnv-view-type",
),
]

View File

@@ -1,22 +1,17 @@
import importlib
from django import forms
from django.contrib import messages
from django.core.cache import cache as CACHE
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render
from django.utils.decorators import method_decorator
from django.views import View
from horilla_views import models
from horilla_views.cbv_methods import get_short_uuid, login_required
from horilla_views.forms import SavedFilterForm
from horilla_views.generic.cbv.views import HorillaFormView
from horilla_views.cbv_methods import get_short_uuid
from horilla_views.generic.cbv.views import dynamic_create_cache
# Create your views here.
@method_decorator(login_required, name="dispatch")
class ToggleColumn(View):
"""
ToggleColumn
@@ -47,7 +42,6 @@ class ToggleColumn(View):
return HttpResponse("success")
@method_decorator(login_required, name="dispatch")
class ReloadField(View):
"""
ReloadField
@@ -64,15 +58,17 @@ class ReloadField(View):
module = importlib.import_module(module_name)
parent_form = getattr(module, class_name)()
dynamic_cache = CACHE.get(request.session.session_key + "cbv" + reload_field)
model: models.HorillaModel = dynamic_cache["model"]
dynamic_cache = dynamic_create_cache.get(
request.session.session_key + reload_field
)
form: forms.ModelForm = dynamic_cache["form"]
cache_field = dynamic_cache["dynamic_field"]
if cache_field != reload_field:
cache_field = reload_field
field = parent_form.fields[cache_field]
queryset = model.objects.all()
queryset = form._meta.model.objects.all()
queryset = field.queryset
choices = [(instance.id, instance) for instance in queryset]
choices.insert(0, ("", "Select option"))
@@ -95,7 +91,6 @@ class ReloadField(View):
)
@method_decorator(login_required, name="dispatch")
class ActiveTab(View):
def get(self, *args, **kwargs):
"""
@@ -117,7 +112,6 @@ class ActiveTab(View):
return JsonResponse({"message": "Success"})
@method_decorator(login_required, name="dispatch")
class ActiveGroup(View):
def get(self, *args, **kwargs):
"""
@@ -141,74 +135,3 @@ class ActiveGroup(View):
instance.group_target = target
instance.save()
return JsonResponse({"message": "Success"})
@method_decorator(login_required, name="dispatch")
class SavedFilter(HorillaFormView):
"""
SavedFilter
"""
model = models.SavedFilter
form_class = SavedFilterForm
new_display_title = "Save Applied Filter"
template_name = "generic/saved_filter_form.html"
form_disaply_attr = "Blah"
def form_valid(self, form: SavedFilterForm) -> HttpResponse:
referrer = self.request.POST.get("referrer", "")
path = self.request.POST.get("path", "/")
result_dict = {key: value[0] for key, value in self.request.GET.lists()}
if form.is_valid():
instance: models.SavedFilter = form.save(commit=False)
if not instance.pk:
instance.path = path
instance.referrer = referrer
instance.filter = result_dict
instance.urlencode = self.request.GET.urlencode()
instance.save()
messages.success(self.request, "Filter Saved")
return self.HttpResponse()
return super().form_valid(form)
def get_context_data(self, **kwargs) -> dict:
context = super().get_context_data(**kwargs)
referrer = self.request.GET.get("referrer", "")
if referrer:
# Remove the protocol and domain part
referrer = "/" + "/".join(referrer.split("/")[3:])
context["path"] = self.request.GET.get("path", "")
context["referrer"] = referrer
return context
@method_decorator(login_required, name="dispatch")
class DeleteSavedFilter(View):
"""
Delete saved filter
"""
def get(self, *args, **kwargs):
pk = kwargs["pk"]
models.SavedFilter.objects.filter(created_by=self.request.user, pk=pk).delete()
return HttpResponse("")
@method_decorator(login_required, name="dispatch")
class ActiveView(View):
"""
ActiveView CBV
"""
def get(self, *args, **kwargs):
path = self.request.GET["path"]
view_type = self.request.GET["view"]
active_view = models.ActiveView.objects.filter(
path=path, created_by=self.request.user
).first()
active_view = active_view if active_view else models.ActiveView()
active_view.path = path
active_view.type = view_type
active_view.save()
return HttpResponse("")