Files
hrms/biometric/forms.py
2026-01-16 15:08:17 +01:00

363 lines
12 KiB
Python

# pylint: disable=too-few-public-methods
"""
Module containing forms for managing biometric devices and associated data.
This module provides Django forms for creating and managing biometric devices,
employee biometric data, COSEC users, and related configurations.
"""
from django import forms
from django.db.models import Q
from django.utils.translation import gettext_lazy as _
from base.forms import Form, ModelForm
from base.methods import reload_queryset
from employee.models import Employee
from horilla.horilla_middlewares import _thread_locals
from horilla_widgets.forms import default_select_option_template
from .models import BiometricDevices, BiometricEmployees
class BiometricDeviceForm(ModelForm):
"""
Form for creating and updating biometric device configurations.
This form is used to create and update biometric device configurations.
It includes fields for specifying the device name, IP address, TCP communication port,
and other relevant settings. Additionally, it excludes fields related to scheduler
settings and device activation status.
"""
class Meta:
"""
Meta class to add additional options
"""
model = BiometricDevices
fields = "__all__"
exclude = [
"is_scheduler",
"scheduler_duration",
"last_fetch_date",
"last_fetch_time",
"is_active",
]
widgets = {
"machine_type": forms.Select(
attrs={
"id": "machineTypeInput",
"onchange": "machineTypeChange($(this))",
}
),
"bio_password": forms.TextInput(
attrs={
"class": "oh-input oh-input--password w-100",
"type": "password",
}
),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
company_widget = self.fields["company_id"].widget
if isinstance(company_widget, forms.Select):
company_widget.option_template_name = default_select_option_template
class BiometricDeviceSchedulerForm(ModelForm):
"""
Form for updating the scheduler duration of a biometric device.
This form is used to update the scheduler duration of a biometric
device to fetch attendance data.
It includes a field for entering the scheduler duration in the format HH:MM.
"""
class Meta:
"""
Meta class to add additional options
"""
model = BiometricDevices
fields = ["scheduler_duration"]
labels = {
"scheduler_duration": _("Enter the duration in the format HH:MM"),
}
class EmployeeBiometricAddForm(Form):
"""
Form for adding employees to a biometric device.
This form allows administrators to add employees to a biometric device
for biometric authentication. It includes a field for selecting employees from
a queryset and ensures that only active employees not already associated with
a 'zk' type biometric device are available for selection.
"""
employee_ids = forms.ModelMultipleChoiceField(
queryset=Employee.objects.all(),
widget=forms.SelectMultiple(),
label=_("Employees"),
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.request = getattr(_thread_locals, "request")
self.device_id = (
self.request.resolver_match.kwargs.get("device_id", None)
if self.request.resolver_match
else None
)
self.device = BiometricDevices.find(self.device_id)
zk_employee_ids = BiometricEmployees.objects.filter(
device_id=self.device
).values_list("employee_id", flat=True)
self.fields["employee_ids"].queryset = Employee.objects.filter(
is_active=True
).exclude(id__in=zk_employee_ids)
class CosecUserAddForm(Form):
"""
Form for adding users to a COSEC biometric device.
This form allows administrators to add multiple users to a COSEC biometric device
for biometric authentication. It includes a field for selecting users from
a queryset and ensures that only active users not already associated with
a 'cosec' type biometric device are available for selection.
"""
employee_ids = forms.ModelMultipleChoiceField(
queryset=Employee.objects.all(),
widget=forms.SelectMultiple(),
label=_("Employees"),
)
def __init__(self, *args, device_id=None, **kwargs):
super().__init__(*args, **kwargs)
cosec_employee_ids = BiometricEmployees.objects.filter(
device_id=device_id
).values_list("employee_id", flat=True)
self.fields["employee_ids"].queryset = Employee.objects.filter(
is_active=True
).exclude(id__in=cosec_employee_ids)
class COSECUserForm(Form):
"""
Form for adding or updating users in a COSEC biometric device.
This form allows administrators to add or update users in a COSEC biometric
device. It includes fields for specifying the user's name, whether the user
is active, whether the user is a VIP user, whether validity is enabled for
the user, the validity end date, and whether to bypass finger-based
authentication for the user. It provides validation to ensure that the
name does not exceed 15 characters and that the validity end date is
provided when validity is enabled.
"""
name = forms.CharField(
label=_("Employee Name"),
help_text=_("15 characters max."),
widget=forms.TextInput(attrs={"class": "oh-input w-100"}),
)
user_active = forms.BooleanField(
initial=False,
required=False,
widget=forms.CheckboxInput(),
)
vip = forms.BooleanField(
initial=False,
required=False,
widget=forms.CheckboxInput(),
)
validity_enable = forms.BooleanField(
initial=False,
required=False,
widget=forms.CheckboxInput(),
)
validity_end_date = forms.DateField(
required=False,
widget=forms.DateInput(
attrs={"type": "date", "class": "oh-input w-100 form-control"}
),
)
by_pass_finger = forms.BooleanField(
initial=False,
required=False,
widget=forms.CheckboxInput(),
)
def clean(self):
cleaned_data = super().clean()
if len(cleaned_data["name"]) > 15:
raise forms.ValidationError(
"Maximum 15 characters allowed for Name in COSEC Biometric Device"
)
if cleaned_data["validity_enable"]:
if cleaned_data.get("validity_end_date") is None:
raise forms.ValidationError(
"When the Validity field is enabled, a Validity End Date is required."
)
class DahuaUserForm(Form):
"""
This form is used to map a Horilla employee to a user entry on a Dahua biometric device.
"""
CARD_STATUS_CHOICES = [
(0, "Normal"),
(1 << 0, "Reported for loss"),
(1 << 1, "Canceled"),
(1 << 2, "Frozen"),
(1 << 3, "Arrearage"),
(1 << 4, "Overdue"),
(1 << 5, "Pre-arrearage (The door still can be unlocked with a voice prompt)"),
]
CARD_TYPE_CHOICES = [
(0, "Ordinary card"),
(1, "VIP card"),
(2, "Guest card"),
(3, "Patrol card"),
(4, "Blocklist card"),
(5, "Duress card"),
]
employee = forms.ModelChoiceField(
queryset=Employee.objects.all(),
widget=forms.Select(),
label=_("Employee"),
)
card_no = forms.CharField(max_length=50, required=True, label=_("Card Number"))
user_id = forms.CharField(max_length=50, required=True, label=_("User ID"))
card_status = forms.ChoiceField(
choices=CARD_STATUS_CHOICES,
required=False,
label=_("Card Status"),
initial=0,
)
card_type = forms.ChoiceField(
choices=CARD_TYPE_CHOICES, required=False, label=_("Card Type")
)
password = forms.CharField(max_length=50, required=False, label=_("Password"))
forms.DateTimeField(
widget=forms.DateTimeInput(
attrs={"class": "oh-input w-100", "type": "datetime-local"}
),
)
valid_date_start = forms.DateTimeField(
required=False,
label=_("Valid Date Start"),
widget=forms.DateTimeInput(
attrs={"class": "oh-input w-100", "type": "datetime-local"}
),
)
valid_date_end = forms.DateTimeField(
required=False,
label=_("Valid Date End"),
widget=forms.DateTimeInput(
attrs={"class": "oh-input w-100", "type": "datetime-local"}
),
)
def __init__(self, *args, **kwargs):
self.request = getattr(_thread_locals, "request")
self.device_id = (
self.request.resolver_match.kwargs.get("device_id", None)
if self.request.resolver_match
else None
)
super().__init__(*args, **kwargs)
reload_queryset(self.fields)
self.fields["employee"].widget.attrs.update(
{
"hx-include": "#dahuaBiometricUserForm",
"hx-target": "#id_user_id",
"hx-swap": "outerHTML",
"hx-trigger": "change",
"hx-get": "/biometric/find-employee-badge-id",
}
)
def clean(self):
cleaned_data = super().clean()
device = None
error_fields = {}
card_no = cleaned_data.get("card_no")
user_id = cleaned_data.get("user_id")
if self.device_id:
device = BiometricDevices.find(self.device_id)
if card_no and device:
if BiometricEmployees.objects.filter(
dahua_card_no=card_no, device_id=device
).exists():
error_fields["card_no"] = _("This Card Number already exists.")
if user_id and device:
if BiometricEmployees.objects.filter(
user_id=user_id, device_id=device
).exists():
error_fields["user_id"] = _("This User ID already exists.")
if error_fields:
raise forms.ValidationError(error_fields)
return cleaned_data
class MapBioUsers(ModelForm):
"""
Form for mapping biometric users to Horilla employees.
This form is used to associate a biometric user (from a biometric device) with
an employee in the Horilla system.
"""
class Meta:
"""
Meta class to add additional options
"""
model = BiometricEmployees
fields = ["employee_id", "user_id"]
def __init__(self, *args, **kwargs):
self.request = getattr(_thread_locals, "request")
self.device_id = (
self.request.resolver_match.kwargs.get("device_id", None)
if self.request.resolver_match
else None
)
super().__init__(*args, **kwargs)
if self.device_id:
already_mapped_employees = BiometricEmployees.objects.filter(
device_id=self.device_id
).values_list("employee_id", flat=True)
self.fields["employee_id"].queryset = Employee.objects.exclude(
Q(id__in=already_mapped_employees) | Q(is_active=False)
)
self.fields["user_id"].required = True
def clean(self):
cleaned_data = super().clean()
user_id = cleaned_data.get("user_id")
user_id_label = self.fields["user_id"].label or "User ID"
if self.device_id and user_id:
if BiometricEmployees.objects.filter(
user_id=user_id, device_id=self.device_id
).exists():
raise forms.ValidationError(
{
"user_id": _(
"This biometric %(label)s is already mapped with an employee"
)
% {"label": user_id_label}
}
)
return cleaned_data