Files
hrms/base/scheduler.py

502 lines
18 KiB
Python
Raw Permalink Normal View History

import calendar
import sys
from datetime import date, datetime, timedelta
from apscheduler.schedulers.background import BackgroundScheduler
from django.urls import reverse
from notifications.signals import notify
def update_rotating_work_type_assign(rotating_work_type, new_date):
"""
Here will update the employee work information details and send notification
"""
from django.contrib.auth.models import User
employee = rotating_work_type.employee_id
employee_work_info = employee.employee_work_info
work_type1 = rotating_work_type.rotating_work_type_id.work_type1
work_type2 = rotating_work_type.rotating_work_type_id.work_type2
additional_work_types = (
rotating_work_type.rotating_work_type_id.additional_work_types()
)
if additional_work_types is None:
total_rotate_work_types = [work_type1, work_type2]
else:
total_rotate_work_types = [work_type1, work_type2] + list(additional_work_types)
next_work_type_index = rotating_work_type.additional_data.get(
"next_work_type_index", 0
)
next_work_type = total_rotate_work_types[next_work_type_index]
if next_work_type_index < len(total_rotate_work_types) - 1:
next_work_type_index += 1
else:
next_work_type_index = 0
rotating_work_type.additional_data["next_work_type_index"] = next_work_type_index
employee_work_info.work_type_id = rotating_work_type.next_work_type
employee_work_info.save()
rotating_work_type.next_change_date = new_date
rotating_work_type.current_work_type = rotating_work_type.next_work_type
rotating_work_type.next_work_type = next_work_type
rotating_work_type.save()
bot = User.objects.filter(username="Horilla Bot").first()
if bot is not None:
employee = rotating_work_type.employee_id
notify.send(
bot,
recipient=employee.employee_user_id,
verb="Your Work Type has been changed.",
verb_ar="لقد تغير نوع عملك.",
verb_de="Ihre Art der Arbeit hat sich geändert.",
verb_es="Su tipo de trabajo ha sido cambiado.",
verb_fr="Votre type de travail a été modifié.",
icon="infinite",
redirect=reverse("employee-profile"),
)
return
def work_type_rotate_after(rotating_work_work_type):
"""
This method for rotate work type based on after day
"""
date_today = datetime.now()
switch_date = rotating_work_work_type.next_change_date
if switch_date.strftime("%Y-%m-%d") == date_today.strftime("%Y-%m-%d"):
new_date = date_today + timedelta(days=rotating_work_work_type.rotate_after_day)
update_rotating_work_type_assign(rotating_work_work_type, new_date)
return
def work_type_rotate_weekend(rotating_work_type):
"""
This method for rotate work type based on weekend
"""
date_today = datetime.now()
switch_date = rotating_work_type.next_change_date
if switch_date.strftime("%Y-%m-%d") == date_today.strftime("%Y-%m-%d"):
day = datetime.now().strftime("%A").lower()
switch_day = rotating_work_type.rotate_every_weekend
if day == switch_day:
new_date = date_today + timedelta(days=7)
update_rotating_work_type_assign(rotating_work_type, new_date)
return
def work_type_rotate_every(rotating_work_type):
"""
This method for rotate work type based on every month
"""
date_today = datetime.now()
switch_date = rotating_work_type.next_change_date
day_date = rotating_work_type.rotate_every
if switch_date.strftime("%Y-%m-%d") == date_today.strftime("%Y-%m-%d"):
if day_date == switch_date.strftime("%d").lstrip("0"):
new_date = date_today.replace(month=date_today.month + 1)
update_rotating_work_type_assign(rotating_work_type, new_date)
elif day_date == "last":
year = date_today.strftime("%Y")
month = date_today.strftime("%m")
last_day = calendar.monthrange(int(year), int(month) + 1)[1]
new_date = datetime(int(year), int(month) + 1, last_day)
update_rotating_work_type_assign(rotating_work_type, new_date)
return
def rotate_work_type():
"""
This method will identify the based on condition to the rotating shift assign
and redirect to the chunk method to execute.
"""
from base.models import RotatingWorkTypeAssign
rotating_work_types = RotatingWorkTypeAssign.objects.filter(is_active=True)
for rotating_work_type in rotating_work_types:
based_on = rotating_work_type.based_on
if based_on == "after":
work_type_rotate_after(rotating_work_type)
elif based_on == "weekly":
work_type_rotate_weekend(rotating_work_type)
elif based_on == "monthly":
work_type_rotate_every(rotating_work_type)
return
def update_rotating_shift_assign(rotating_shift, new_date):
"""
Here will update the employee work information and send notification
"""
from django.contrib.auth.models import User
next_shift_index = 0
employee = rotating_shift.employee_id
employee_work_info = employee.employee_work_info
rotating_shift_id = rotating_shift.rotating_shift_id
shift1 = rotating_shift_id.shift1
shift2 = rotating_shift_id.shift2
additional_shifts = rotating_shift_id.additional_shifts()
if additional_shifts is None:
total_rotate_shifts = [shift1, shift2]
else:
total_rotate_shifts = [shift1, shift2] + list(additional_shifts)
next_shift_index = rotating_shift.additional_data.get("next_shift_index")
next_shift = total_rotate_shifts[next_shift_index]
if next_shift_index < len(total_rotate_shifts) - 1:
next_shift_index += 1
else:
next_shift_index = 0 # Wrap around to the beginning of the list
rotating_shift.additional_data["next_shift_index"] = next_shift_index
employee_work_info.shift_id = rotating_shift.next_shift
employee_work_info.save()
rotating_shift.next_change_date = new_date
rotating_shift.current_shift = rotating_shift.next_shift
rotating_shift.next_shift = next_shift
rotating_shift.save()
bot = User.objects.filter(username="Horilla Bot").first()
if bot is not None:
employee = rotating_shift.employee_id
notify.send(
bot,
recipient=employee.employee_user_id,
verb="Your shift has been changed.",
verb_ar="تم تغيير التحول الخاص بك.",
verb_de="Ihre Schicht wurde geändert.",
verb_es="Tu turno ha sido cambiado.",
verb_fr="Votre quart de travail a été modifié.",
icon="infinite",
redirect=reverse("employee-profile"),
)
return
def shift_rotate_after_day(rotating_shift, today):
"""
This method for rotate shift based on after day
"""
switch_date = rotating_shift.next_change_date
if switch_date == today:
new_date = today + timedelta(days=rotating_shift.rotate_after_day)
update_rotating_shift_assign(rotating_shift, new_date)
return
def shift_rotate_weekend(rotating_shift, today):
"""
This method for rotate shift based on weekend
"""
switch_date = rotating_shift.next_change_date
if switch_date == today:
day = today.strftime("%A").lower()
switch_day = rotating_shift.rotate_every_weekend
if day == switch_day:
new_date = today + timedelta(days=7)
update_rotating_shift_assign(rotating_shift, new_date)
return
def shift_rotate_every(rotating_shift, today):
"""
This method for rotate shift based on every month
"""
switch_date = rotating_shift.next_change_date
day_date = rotating_shift.rotate_every
if switch_date == today:
if day_date == switch_date.strftime("%d").lstrip("0"):
new_date = today.replace(month=today.month + 1)
update_rotating_shift_assign(rotating_shift, new_date)
elif day_date == "last":
year = today.year
month = today.month
last_day = calendar.monthrange(int(year), int(month) + 1)[1]
new_date = datetime(int(year), int(month) + 1, last_day)
update_rotating_shift_assign(rotating_shift, new_date)
return
def rotate_shift():
"""
This method will identify the based on condition to the rotating shift assign
and redirect to the chunk method to execute.
"""
from base.models import RotatingShiftAssign
rotating_shifts = RotatingShiftAssign.objects.filter(is_active=True)
today = datetime.now().date()
r_shifts = rotating_shifts.filter(start_date__lte=today)
rotating_shifts_modified = None
for r_shift in r_shifts:
emp_shift = rotating_shifts.filter(
employee_id=r_shift.employee_id, start_date__lte=today
).exclude(id=r_shift.id)
rotating_shifts_modified = rotating_shifts.exclude(
id__in=emp_shift.values_list("id", flat=True)
)
emp_shift.update(is_active=False)
for rotating_shift in rotating_shifts_modified:
based_on = rotating_shift.based_on
# after day condition
if based_on == "after":
shift_rotate_after_day(rotating_shift, today)
# weekly condition
elif based_on == "weekly":
shift_rotate_weekend(rotating_shift, today)
# monthly condition
elif based_on == "monthly":
shift_rotate_every(rotating_shift, today)
return
def switch_shift():
"""
This method change employees shift information regards to the shift request
"""
from django.contrib.auth.models import User
from base.models import ShiftRequest
today = date.today()
shift_requests = ShiftRequest.objects.filter(
canceled=False, approved=True, requested_date__exact=today, shift_changed=False
)
if shift_requests:
for request in shift_requests:
work_info = request.employee_id.employee_work_info
# updating requested shift to the employee work information.
work_info.shift_id = request.shift_id
work_info.save()
request.approved = True
request.shift_changed = True
request.save()
bot = User.objects.filter(username="Horilla Bot").first()
if bot is not None:
employee = request.employee_id
notify.send(
bot,
recipient=employee.employee_user_id,
verb="Shift Changes notification",
verb_ar="التحول تغيير الإخطار",
verb_de="Benachrichtigung über Schichtänderungen",
verb_es="Notificación de cambios de turno",
verb_fr="Notification des changements de quart de travail",
icon="refresh",
redirect=reverse("employee-profile"),
)
return
def undo_shift():
"""
This method undo previous employees shift information regards to the shift request
"""
from django.contrib.auth.models import User
from base.models import ShiftRequest
today = date.today()
# here will get all the active shift requests
shift_requests = ShiftRequest.objects.filter(
canceled=False,
approved=True,
requested_till__lt=today,
is_active=True,
shift_changed=True,
)
if shift_requests:
for request in shift_requests:
work_info = request.employee_id.employee_work_info
work_info.shift_id = request.previous_shift_id
work_info.save()
# making the instance in-active
request.is_active = False
request.save()
bot = User.objects.filter(username="Horilla Bot").first()
if bot is not None:
employee = request.employee_id
notify.send(
bot,
recipient=employee.employee_user_id,
verb="Shift changes notification, Requested date expired.",
verb_ar="التحول يغير الإخطار ، التاريخ المطلوب انتهت صلاحيته.",
verb_de="Benachrichtigung über Schichtänderungen, gewünschtes Datum abgelaufen.",
verb_es="Notificación de cambios de turno, Fecha solicitada vencida.",
verb_fr="Notification de changement d'équipe, la date demandée a expiré.",
icon="refresh",
redirect=reverse("employee-profile"),
)
return
def switch_work_type():
"""
This method change employees work type information regards to the work type request
"""
from django.contrib.auth.models import User
from base.models import WorkTypeRequest
today = date.today()
work_type_requests = WorkTypeRequest.objects.filter(
canceled=False,
approved=True,
requested_date__exact=today,
work_type_changed=False,
)
for request in work_type_requests:
work_info = request.employee_id.employee_work_info
# updating requested work type to the employee work information.
work_info.work_type_id = request.work_type_id
work_info.save()
request.approved = True
request.work_type_changed = True
request.save()
bot = User.objects.filter(username="Horilla Bot").first()
if bot is not None:
employee = request.employee_id
notify.send(
bot,
recipient=employee.employee_user_id,
verb="Work Type Changes notification",
verb_ar="إخطار تغييرات نوع العمل",
verb_de="Benachrichtigung über Änderungen des Arbeitstyps",
verb_es="Notificación de cambios de tipo de trabajo",
verb_fr="Notification de changement de type de travail",
icon="swap-horizontal",
redirect=reverse("employee-profile"),
)
return
def undo_work_type():
"""
This method undo previous employees work type information regards to the work type request
"""
from django.contrib.auth.models import User
from base.models import WorkTypeRequest
today = date.today()
# here will get all the active work type requests
work_type_requests = WorkTypeRequest.objects.filter(
canceled=False,
approved=True,
requested_till__lt=today,
is_active=True,
work_type_changed=True,
)
for request in work_type_requests:
work_info = request.employee_id.employee_work_info
# updating employee work information's work type to previous work type
work_info.work_type_id = request.previous_work_type_id
work_info.save()
# making the instance is in-active
request.is_active = False
request.save()
bot = User.objects.filter(username="Horilla Bot").first()
if bot is not None:
employee = request.employee_id
notify.send(
bot,
recipient=employee.employee_user_id,
verb="Work type changes notification, Requested date expired.",
verb_ar="إعلام بتغيير نوع العمل ، انتهاء صلاحية التاريخ المطلوب.",
verb_de="Benachrichtigung über Änderungen des Arbeitstyps, angefordertes Datum abgelaufen.",
verb_es="Notificación de cambios de tipo de trabajo, fecha solicitada vencida.",
verb_fr="Notification de changement de type de travail, la date demandée a expiré.",
icon="swap-horizontal",
redirect=reverse("employee-profile"),
)
return
def recurring_holiday():
from .models import Holidays
recurring_holidays = Holidays.objects.filter(recurring=True)
today = datetime.now()
# Looping through all recurring holiday
for recurring_holiday in recurring_holidays:
start_date = recurring_holiday.start_date
end_date = recurring_holiday.end_date
new_start_date = date(start_date.year + 1, start_date.month, start_date.day)
new_end_date = date(end_date.year + 1, end_date.month, end_date.day)
# Checking that end date is not none
if end_date is None:
# checking if that start date is day before today
if start_date == (today - timedelta(days=1)).date():
recurring_holiday.start_date = new_start_date
elif end_date == (today - timedelta(days=1)).date():
recurring_holiday.start_date = new_start_date
recurring_holiday.end_date = new_end_date
recurring_holiday.save()
if not any(
cmd in sys.argv
for cmd in ["makemigrations", "migrate", "compilemessages", "flush", "shell"]
):
scheduler = BackgroundScheduler()
# Add jobs with next_run_time set to the end of the previous job
try:
scheduler.add_job(rotate_shift, "interval", hours=4, id="job1")
except:
pass
try:
scheduler.add_job(
rotate_work_type,
"interval",
hours=4,
id="job2",
)
except:
pass
try:
scheduler.add_job(
undo_shift,
"interval",
hours=4,
id="job3",
)
except:
pass
try:
scheduler.add_job(
switch_shift,
"interval",
hours=4,
id="job4",
)
except:
pass
try:
scheduler.add_job(
undo_work_type,
"interval",
hours=4,
id="job6",
)
except:
pass
try:
scheduler.add_job(
switch_work_type,
"interval",
hours=4,
id="job5",
)
except:
pass
scheduler.add_job(recurring_holiday, "interval", hours=4)
scheduler.start()