diff --git a/attendance/scheduler.py b/attendance/scheduler.py index 103a6a608..d483b6ccd 100644 --- a/attendance/scheduler.py +++ b/attendance/scheduler.py @@ -1,7 +1,7 @@ import datetime import sys -from apscheduler.schedulers.background import BackgroundScheduler +from apscheduler.schedulers.background import BackgroundScheduler today = datetime.datetime.today() @@ -22,7 +22,11 @@ def create_work_record(date=today): WorkRecords.objects.get_or_create( employee_id=employee, date=date, - defaults={"work_record_type": "DFT", "shift_id": shift, "message": ""}, + defaults={ + "work_record_type": "DFT", + "shift_id": shift, + "message": "", + }, ) except: pass diff --git a/attendance/signals.py b/attendance/signals.py index 7220b1122..cedd0ea40 100644 --- a/attendance/signals.py +++ b/attendance/signals.py @@ -1,22 +1,18 @@ # attendance/signals.py from datetime import datetime, timedelta + from django.apps import apps from django.db.models.signals import post_migrate, post_save, pre_delete -from django.utils.translation import gettext_lazy as _ from django.dispatch import receiver +from django.utils.translation import gettext_lazy as _ from attendance.methods.utils import strtime_seconds -from attendance.models import ( - Attendance, - AttendanceGeneralSetting, - WorkRecords, -) +from attendance.models import Attendance, AttendanceGeneralSetting, WorkRecords from base.models import Company, PenaltyAccounts from employee.models import Employee from horilla.methods import get_horilla_model_class - if apps.is_installed("payroll"): @receiver(post_save, sender=PenaltyAccounts) @@ -148,7 +144,7 @@ def add_missing_attendance_to_workrecord(sender, **kwargs): if sender.label not in ["attendance", "leave"]: return - from attendance.models import WorkRecords, Attendance + from attendance.models import Attendance, WorkRecords try: work_records = WorkRecords.objects.filter( @@ -230,6 +226,7 @@ def create_attendance_setting(sender, instance, created, raw, **kwargs): if created: AttendanceGeneralSetting.objects.get_or_create(company_id=instance) + @receiver(post_migrate) def create_missing_work_records(sender, **kwargs): if sender.label not in ["attendance"]: @@ -247,10 +244,15 @@ def create_missing_work_records(sender, **kwargs): end_date = datetime.today().date() existing_dates = set( - WorkRecords.objects.filter(employee_id=employee.id).values_list("date", flat=True) + WorkRecords.objects.filter(employee_id=employee.id).values_list( + "date", flat=True + ) ) - all_dates = {start_date + timedelta(days=i) for i in range((end_date - start_date).days)} + all_dates = { + start_date + timedelta(days=i) + for i in range((end_date - start_date).days) + } missing_dates = all_dates - existing_dates work_records_to_create = [ @@ -264,8 +266,11 @@ def create_missing_work_records(sender, **kwargs): ] if work_records_to_create: - WorkRecords.objects.bulk_create(work_records_to_create, batch_size=500, ignore_conflicts=True) + WorkRecords.objects.bulk_create( + work_records_to_create, batch_size=500, ignore_conflicts=True + ) except Exception as e: - print(f"Error creating missing work records for employee {employee}: {e}") - \ No newline at end of file + print( + f"Error creating missing work records for employee {employee}: {e}" + ) diff --git a/horilla_ldap/apps.py b/horilla_ldap/apps.py index 680529379..898134c06 100644 --- a/horilla_ldap/apps.py +++ b/horilla_ldap/apps.py @@ -1,16 +1,18 @@ from django.apps import AppConfig from django.conf import settings + import horilla.horilla_settings class HorillaLdapConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'horilla_ldap' + default_auto_field = "django.db.models.BigAutoField" + name = "horilla_ldap" def ready(self): from django.urls import include, path - from horilla.urls import urlpatterns + from horilla.horilla_settings import APPS + from horilla.urls import urlpatterns APPS.append("horilla_ldap") urlpatterns.append( diff --git a/horilla_ldap/forms.py b/horilla_ldap/forms.py index 90fbf556a..8655e2054 100644 --- a/horilla_ldap/forms.py +++ b/horilla_ldap/forms.py @@ -1,14 +1,19 @@ from django import forms -from .models import LDAPSettings from django.template.loader import render_to_string + from base.forms import ModelForm +from .models import LDAPSettings + + class LDAPSettingsForm(ModelForm): - bind_password = forms.CharField(widget=forms.PasswordInput(attrs={"class":"oh-input w-100"}), required=True) + bind_password = forms.CharField( + widget=forms.PasswordInput(attrs={"class": "oh-input w-100"}), required=True + ) class Meta: model = LDAPSettings - fields = ['ldap_server', 'bind_dn', 'bind_password', 'base_dn'] + fields = ["ldap_server", "bind_dn", "bind_password", "base_dn"] def as_p(self): """ diff --git a/horilla_ldap/management/commands/import_ldap_users.py b/horilla_ldap/management/commands/import_ldap_users.py index 0a4ef1a7d..35ff0bd32 100644 --- a/horilla_ldap/management/commands/import_ldap_users.py +++ b/horilla_ldap/management/commands/import_ldap_users.py @@ -1,16 +1,18 @@ +import platform import re import sys -import platform + +from django.contrib.auth.models import User from django.core.management.base import BaseCommand from django.db.models import Q -from django.contrib.auth.models import User -from horilla_ldap.models import LDAPSettings + from employee.models import Employee +from horilla_ldap.models import LDAPSettings if platform.system() == "Linux": import ldap # Use python-ldap for Linux else: - from ldap3 import Server, Connection, ALL # Use ldap3 for Windows + from ldap3 import ALL, Connection, Server # Use ldap3 for Windows class Command(BaseCommand): @@ -33,7 +35,11 @@ def handle(self, *args, **kwargs): base_dn = settings.base_dn if not all([ldap_server, bind_dn, bind_password, base_dn]): - self.stdout.write(self.style.ERROR("LDAP settings are incomplete. Please check your configuration.")) + self.stdout.write( + self.style.ERROR( + "LDAP settings are incomplete. Please check your configuration." + ) + ) return try: @@ -42,7 +48,9 @@ def handle(self, *args, **kwargs): connection = ldap.initialize(ldap_server) connection.simple_bind_s(bind_dn, bind_password) search_filter = "(objectClass=inetOrgPerson)" - results = connection.search_s(base_dn, ldap.SCOPE_SUBTREE, search_filter) + results = connection.search_s( + base_dn, ldap.SCOPE_SUBTREE, search_filter + ) for dn, entry in results: user_id = entry.get("uid", [b""])[0].decode("utf-8") @@ -53,13 +61,17 @@ def handle(self, *args, **kwargs): phone = entry.get("telephoneNumber", [b""])[0].decode("utf-8") # Get the password from LDAP - ldap_password = entry.get("telephoneNumber", [b""])[0].decode("utf-8") + ldap_password = entry.get("telephoneNumber", [b""])[0].decode( + "utf-8" + ) # Remove non-numeric characters but keep numbers - clean_phone = re.sub(r"[^\d]", "", phone) + clean_phone = re.sub(r"[^\d]", "", phone) ldap_password = clean_phone - self.create_or_update_employee(user_id, email, first_name, last_name, phone, ldap_password) + self.create_or_update_employee( + user_id, email, first_name, last_name, phone, ldap_password + ) connection.unbind_s() @@ -68,11 +80,27 @@ def handle(self, *args, **kwargs): server = Server(ldap_server, get_info=ALL) connection = Connection(server, user=bind_dn, password=bind_password) if not connection.bind(): - self.stdout.write(self.style.ERROR(f"Failed to bind to LDAP server: {connection.last_error}")) + self.stdout.write( + self.style.ERROR( + f"Failed to bind to LDAP server: {connection.last_error}" + ) + ) return search_filter = "(objectClass=inetOrgPerson)" - connection.search(base_dn, search_filter, attributes=['uid', 'mail', 'givenName', 'sn', 'cn', 'telephoneNumber', 'userPassword']) + connection.search( + base_dn, + search_filter, + attributes=[ + "uid", + "mail", + "givenName", + "sn", + "cn", + "telephoneNumber", + "userPassword", + ], + ) for entry in connection.entries: user_id = entry.uid.value if entry.uid else "" @@ -86,32 +114,44 @@ def handle(self, *args, **kwargs): clean_phone = re.sub(r"[^\d]", "", phone) ldap_password = clean_phone - self.create_or_update_employee(user_id, email, first_name, last_name, phone, ldap_password) + self.create_or_update_employee( + user_id, email, first_name, last_name, phone, ldap_password + ) connection.unbind() except Exception as e: self.stderr.write(self.style.ERROR(f"Error: {e}")) - def create_or_update_employee(self, user_id, email, first_name, last_name, phone, ldap_password): + def create_or_update_employee( + self, user_id, email, first_name, last_name, phone, ldap_password + ): employee, created = Employee.objects.update_or_create( email=email, defaults={ "employee_first_name": first_name or "", "employee_last_name": last_name or "", "phone": phone or "", - } + }, ) try: - user = User.objects.get(Q(username=email) | Q(username=user_id) | Q(email=email)) + user = User.objects.get( + Q(username=email) | Q(username=user_id) | Q(email=email) + ) user.username = user_id user.set_password(ldap_password) # Hash and store password securely user.save() action = "Updated" except User.DoesNotExist: - self.stdout.write(self.style.WARNING(f"User for employee {first_name} {last_name} does not exist.")) + self.stdout.write( + self.style.WARNING( + f"User for employee {first_name} {last_name} does not exist." + ) + ) return action = "Created" if created else "Updated" - self.stdout.write(self.style.SUCCESS(f"{action} employee {first_name} {last_name}.")) + self.stdout.write( + self.style.SUCCESS(f"{action} employee {first_name} {last_name}.") + ) diff --git a/horilla_ldap/management/commands/import_users_to_ldap.py b/horilla_ldap/management/commands/import_users_to_ldap.py index b6d81a3f4..aeda4155e 100644 --- a/horilla_ldap/management/commands/import_users_to_ldap.py +++ b/horilla_ldap/management/commands/import_users_to_ldap.py @@ -1,13 +1,15 @@ -import hashlib import base64 +import hashlib + from django.core.management.base import BaseCommand -from ldap3 import Server, Connection, ALL, ALL_ATTRIBUTES -from horilla_ldap.models import LDAPSettings +from ldap3 import ALL, ALL_ATTRIBUTES, Connection, Server + from employee.models import Employee +from horilla_ldap.models import LDAPSettings class Command(BaseCommand): - help = 'Import users from Django to LDAP using LDAP settings from the database' + help = "Import users from Django to LDAP using LDAP settings from the database" def handle(self, *args, **kwargs): # Get LDAP settings from the database @@ -23,7 +25,11 @@ def handle(self, *args, **kwargs): base_dn = settings.base_dn if not all([ldap_server, bind_dn, bind_password, base_dn]): - self.stdout.write(self.style.ERROR("LDAP settings are incomplete. Please check your configuration.")) + self.stdout.write( + self.style.ERROR( + "LDAP settings are incomplete. Please check your configuration." + ) + ) return # Connect to the LDAP server @@ -37,40 +43,65 @@ def handle(self, *args, **kwargs): for user in users: if not user.employee_user_id: - self.stdout.write(self.style.WARNING(f"Skipping user {user} due to missing employee_user_id")) + self.stdout.write( + self.style.WARNING( + f"Skipping user {user} due to missing employee_user_id" + ) + ) continue dn = f"uid={user.employee_user_id.username},{base_dn}" # Securely hash the password using SHA - hashed_password = "{SHA}" + base64.b64encode(hashlib.sha1(user.phone.encode()).digest()).decode() + hashed_password = ( + "{SHA}" + + base64.b64encode( + hashlib.sha1(user.phone.encode()).digest() + ).decode() + ) if user.employee_last_name is None: user.employee_last_name = " " attributes = { - 'objectClass': ['inetOrgPerson'], - 'givenName': user.employee_first_name or "", - 'sn': user.employee_last_name or "", - 'cn': f"{user.employee_first_name} {user.employee_last_name}", - 'uid': user.email or "", - 'mail': user.email or "", + "objectClass": ["inetOrgPerson"], + "givenName": user.employee_first_name or "", + "sn": user.employee_last_name or "", + "cn": f"{user.employee_first_name} {user.employee_last_name}", + "uid": user.email or "", + "mail": user.email or "", "telephoneNumber": user.phone or "", - 'userPassword': hashed_password, # Securely store password + "userPassword": hashed_password, # Securely store password } # Check if the user already exists in LDAP - conn.search(base_dn, f'(uid={user.employee_user_id.username})', attributes=ALL_ATTRIBUTES) + conn.search( + base_dn, + f"(uid={user.employee_user_id.username})", + attributes=ALL_ATTRIBUTES, + ) if conn.entries: - self.stdout.write(self.style.WARNING(f'{user.employee_first_name} {user.employee_last_name} already exists in LDAP. Skipping...')) + self.stdout.write( + self.style.WARNING( + f"{user.employee_first_name} {user.employee_last_name} already exists in LDAP. Skipping..." + ) + ) else: # Add user to LDAP if not conn.add(dn, attributes=attributes): - self.stdout.write(self.style.ERROR(f'Failed to add {user.employee_first_name} {user.employee_last_name}: {conn.result}')) + self.stdout.write( + self.style.ERROR( + f"Failed to add {user.employee_first_name} {user.employee_last_name}: {conn.result}" + ) + ) else: - self.stdout.write(self.style.SUCCESS(f'Successfully added {user.employee_first_name} {user.employee_last_name} to LDAP.')) + self.stdout.write( + self.style.SUCCESS( + f"Successfully added {user.employee_first_name} {user.employee_last_name} to LDAP." + ) + ) conn.unbind() except Exception as e: - self.stdout.write(self.style.ERROR(f'An error occurred: {e}')) \ No newline at end of file + self.stdout.write(self.style.ERROR(f"An error occurred: {e}")) diff --git a/horilla_ldap/models.py b/horilla_ldap/models.py index a816ed63b..393e604c4 100644 --- a/horilla_ldap/models.py +++ b/horilla_ldap/models.py @@ -3,8 +3,6 @@ # Create your models here. -from django.db import models - class LDAPSettings(models.Model): ldap_server = models.CharField(max_length=255, default="ldap://127.0.0.1:389") bind_dn = models.CharField(max_length=255, default="cn=admin,dc=horilla,dc=com") @@ -13,4 +11,3 @@ class LDAPSettings(models.Model): def __str__(self): return f"LDAP Settings ({self.ldap_server})" - diff --git a/horilla_ldap/templates/ldap_settings.html b/horilla_ldap/templates/ldap_settings.html index 449e7d6f4..4457b06e0 100644 --- a/horilla_ldap/templates/ldap_settings.html +++ b/horilla_ldap/templates/ldap_settings.html @@ -5,4 +5,4 @@

{% trans "LDAP Configuration" % {% csrf_token %} {{ form.as_p }} -{% endblock settings %} \ No newline at end of file +{% endblock settings %} diff --git a/horilla_ldap/urls.py b/horilla_ldap/urls.py index aa47bf64c..facb76fb2 100644 --- a/horilla_ldap/urls.py +++ b/horilla_ldap/urls.py @@ -5,8 +5,9 @@ """ from django.urls import path + from horilla_ldap import views urlpatterns = [ - path('settings/ldap-settings/', views.ldap_settings_view, name='ldap-settings'), -] \ No newline at end of file + path("settings/ldap-settings/", views.ldap_settings_view, name="ldap-settings"), +] diff --git a/horilla_ldap/views.py b/horilla_ldap/views.py index 95792242a..1e1ebcc11 100644 --- a/horilla_ldap/views.py +++ b/horilla_ldap/views.py @@ -1,13 +1,14 @@ +from django.contrib import messages from django.shortcuts import render - -# Create your views here. +from django.utils.translation import gettext as __ +from django.utils.translation import gettext_lazy as _ from horilla.decorators import login_required -from .models import LDAPSettings + from .forms import LDAPSettingsForm -from django.utils.translation import gettext as __ -from django.utils.translation import gettext_lazy as _ -from django.contrib import messages +from .models import LDAPSettings + +# Create your views here. @login_required diff --git a/leave/signals.py b/leave/signals.py index c8d13ee40..088f05afb 100644 --- a/leave/signals.py +++ b/leave/signals.py @@ -3,14 +3,13 @@ import threading from django.apps import apps -from django.db.models.signals import post_migrate, pre_save, post_save, pre_delete +from django.db.models.signals import post_migrate, post_save, pre_delete, pre_save from django.dispatch import receiver from django.utils.translation import gettext_lazy as _ from horilla.methods import get_horilla_model_class from leave.models import LeaveRequest - if apps.is_installed("attendance"): @receiver(pre_save, sender=LeaveRequest) diff --git a/recruitment/signals.py b/recruitment/signals.py index 02a3218e7..8d7ab3188 100644 --- a/recruitment/signals.py +++ b/recruitment/signals.py @@ -1,7 +1,12 @@ - +from django.db.models.signals import m2m_changed, post_save from django.dispatch import receiver -from django.db.models.signals import post_save, m2m_changed -from recruitment.models import CandidateDocument, CandidateDocumentRequest, Recruitment, Stage + +from recruitment.models import ( + CandidateDocument, + CandidateDocumentRequest, + Recruitment, + Stage, +) @receiver(post_save, sender=Recruitment) @@ -44,4 +49,3 @@ def candidate_document_create(instance): ) document.title = f"Upload {instance.title}" document.save() -