""" dashboard.py This module is used to write dashboard related views """ import datetime from django.core import serializers from django.http import JsonResponse from django.utils.translation import gettext_lazy as _ from django.shortcuts import render from horilla.decorators import login_required from recruitment.decorators import manager_can_enter from recruitment.models import Candidate, Recruitment, SkillZone, Stage from base.models import Department, JobPosition from employee.models import EmployeeWorkInformation def stage_type_candidate_count(rec, stage_type): """ This method is used find the count of candidate in recruitment """ candidates_count = 0 for stage_obj in rec.stage_set.filter(stage_type=stage_type): candidates_count = candidates_count + len( stage_obj.candidate_set.filter(is_active=True) ) return candidates_count @login_required @manager_can_enter(perm="recruitment.view_recruitment") def dashboard(request): """ This method is used to render dashboard for recruitment module """ candidates = Candidate.objects.all() stage_chart_count = 0 vacancy_chart = Recruitment.objects.filter(closed=False, is_event_based=False) if vacancy_chart.exists(): dep_vacancy = 1 else: dep_vacancy = 0 employee_info = EmployeeWorkInformation.objects.all() joining_list = [] for rec in employee_info: if rec.date_joining != None: joining_list.append("OK") if joining_list != []: joining = 1 else: joining = 0 jobs = JobPosition.objects.all() all_job = [] for job in jobs: jobpos = job.job_position all_job.append(jobpos) initial = [] for job in jobs: ini = Candidate.objects.filter( job_position_id=job, stage_id__stage_type="initial" ) initial.append(ini.count()) test = [] for job in jobs: tes = Candidate.objects.filter(job_position_id=job, stage_id__stage_type="test") test.append(tes.count()) interview = [] for job in jobs: inter = Candidate.objects.filter( job_position_id=job, stage_id__stage_type="interview" ) interview.append(inter.count()) hired = [] for job in jobs: hire = Candidate.objects.filter( job_position_id=job, stage_id__stage_type="hired" ) hired.append(hire.count()) cancelled = [] for job in jobs: cancelled_candidates = Candidate.objects.filter( job_position_id=job, stage_id__stage_type="cancelled" ) cancelled.append(cancelled_candidates.count()) job_data = list(zip(all_job, initial, test, interview, hired, cancelled)) recruitment_obj = Recruitment.objects.filter(closed=False) ongoing_recruitments = len(recruitment_obj) for rec in recruitment_obj: data = [stage_type_candidate_count(rec, type[0]) for type in Stage.stage_types] for i in data: stage_chart_count += i if stage_chart_count >= 1: stage_chart_count = 1 onboarding_count = Candidate.objects.filter(start_onboard=True) onboarding_count = onboarding_count.count() recruitment_manager_mapping = {} for rec in recruitment_obj: recruitment_title = rec.title managers = [] for manager in rec.recruitment_managers.all(): name = manager.employee_first_name + " " + manager.employee_last_name managers.append(name) recruitment_manager_mapping[recruitment_title] = managers total_vacancy = 0 for openings in recruitment_obj: if openings.vacancy == None: pass else: total_vacancy += openings.vacancy hired_candidates = candidates.filter(hired=True) total_candidates = len(candidates) total_hired_candidates = len(hired_candidates) conversion_ratio = 0 hired_ratio = 0 total_candidate_ratio = 0 acceptance_ratio = 0 if total_candidates != 0: conversion_ratio = f"{((total_hired_candidates / total_candidates) * 100):.1f}" if total_vacancy != 0: hired_ratio = f"{((total_hired_candidates / total_vacancy) * 100):.1f}" total_candidate_ratio = f"{((total_candidates / total_vacancy) * 100):.1f}" if total_hired_candidates != 0: acceptance_ratio = f"{((onboarding_count / total_hired_candidates) * 100):.1f}" skill_zone = SkillZone.objects.filter(is_active=True) return render( request, "dashboard/dashboard.html", { "ongoing_recruitments": ongoing_recruitments, "total_candidate_ratio": total_candidate_ratio, "total_hired_candidates": total_hired_candidates, "conversion_ratio": conversion_ratio, "acceptance_ratio": acceptance_ratio, "onboard_candidates": hired_candidates.filter(start_onboard=True), "job_data": job_data, "total_vacancy": total_vacancy, "recruitment_manager_mapping": recruitment_manager_mapping, "hired_ratio": hired_ratio, "joining": joining, "dep_vacancy": dep_vacancy, "stage_chart_count": stage_chart_count, "onboarding_count": onboarding_count, "total_candidates": total_candidates, "skill_zone": skill_zone, }, ) @login_required @manager_can_enter(perm="recruitment.view_recruitment") def dashboard_pipeline(request): """ This method is used generate recruitment dataset for the dashboard """ recruitment_obj = Recruitment.objects.filter(closed=False) data_set = [] labels = [type[1] for type in Stage.stage_types] for rec in recruitment_obj: data = [stage_type_candidate_count(rec, type[0]) for type in Stage.stage_types] if rec.candidate.all(): data_set.append( { "label": ( rec.title if rec.title is not None else f"""{rec.job_position_id} {rec.start_date}""" ), "data": data, } ) return JsonResponse( {"dataSet": data_set, "labels": labels, "message": _("No data Found...")} ) @login_required @manager_can_enter(perm="recruitment.view_recruitment") def dashboard_hiring(request): """ This method is used generate employee joining status for the dashboard """ selected_year = request.GET.get("id") employee_info = EmployeeWorkInformation.objects.filter( date_joining__year=selected_year ) # Create a list to store the count of employees for each month employee_count_per_month = [0] * 12 # Initialize with zeros for all months # Count the number of employees who joined in each month for the selected year for info in employee_info: if isinstance(info.date_joining, datetime.date): month_index = info.date_joining.month - 1 # Month index is zero-based employee_count_per_month[ month_index ] += 1 # Increment the count for the corresponding month labels = [ _("January"), _("February"), _("March"), _("April"), _("May"), _("June"), _("July"), _("August"), _("September"), _("October"), _("November"), _("December"), ] data_set = [ { "label": _("Employees joined in %(year)s") % {"year": selected_year}, "data": employee_count_per_month, "backgroundColor": "rgba(236, 131, 25)", } ] return JsonResponse({"dataSet": data_set, "labels": labels}) @login_required @manager_can_enter(perm="recruitment.view_recruitment") def dashboard_vacancy(_request): """ This method is used to generate a recruitment vacancy chart for the dashboard """ recruitment_obj = Recruitment.objects.filter(closed=False, is_event_based=False) department = Department.objects.all() label = [] data_set = [{"label": _("Openings"), "data": []}] for dep in department: vacancies_for_department = recruitment_obj.filter( job_position_id__department_id=dep ) for rec in vacancies_for_department: if rec.vacancy is not None: label.append(dep.department) vacancies = [ int(rec.vacancy) if rec.vacancy is not None else 0 for rec in vacancies_for_department ] data_set[0]["data"].append([sum(vacancies)]) return JsonResponse({"dataSet": data_set, "labels": label}) def get_open_position(request): """ This is an ajax method to render the open position to the recruitment Returns: obj: it returns the list of job positions """ rec_id = request.GET["recId"] recruitment_obj = Recruitment.objects.get(id=rec_id) queryset = recruitment_obj.open_positions.all() job_info = serializers.serialize("json", queryset) rec_info = serializers.serialize("json", [recruitment_obj]) return JsonResponse({"openPositions": job_info, "recruitmentInfo": rec_info}) @login_required @manager_can_enter(perm="recruitment.view_recruitment") def candidate_status(_request): """ This method is used to generate a CAndidate status chart for the dashboard """ not_sent_candidates = Candidate.objects.filter( offer_letter_status="not_sent" ).count() sent_candidates = Candidate.objects.filter(offer_letter_status="sent").count() accepted_candidates = Candidate.objects.filter( offer_letter_status="accepted" ).count() rejected_candidates = Candidate.objects.filter( offer_letter_status="rejected" ).count() joined_candidates = Candidate.objects.filter(offer_letter_status="joined").count() data_set = [] labels = ["Not Sent", "Sent", "Accepted", "Rejected", "Joined"] data = [ not_sent_candidates, sent_candidates, accepted_candidates, rejected_candidates, joined_candidates, ] for i in range(len(data)): data_set.append({"label": labels[i], "data": data[i]}) # for i in range(len(data)): # if data[i] != 0: # data_set.append({ # "label": labels[i], # "data": data[i] # }) # # Remove labels corresponding to data points with value 0 # labels = [label for label, d in zip(labels, data) if d != 0] return JsonResponse({"dataSet": data_set, "labels": labels})