Skip to content

Commit

Permalink
Merge pull request #30 from CSPCLAB/develop
Browse files Browse the repository at this point in the history
Develop : 면접시간 자동배치 알고리즘 수정
  • Loading branch information
YSHyeonn authored Mar 9, 2024
2 parents bb840be + 518aeff commit a938dcd
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 41 deletions.
153 changes: 116 additions & 37 deletions app/apply/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,44 +49,123 @@ def get_all_resume(self, request, queryset):
def set_interview_time(
self, request, queryset
): # self = resume model , queryset = 체크했던 object들
times = [queryset.time for queryset in InterviewTime.objects.all()]
meta = self.model._meta
# 면접 시간(is_fixed)과 지원자의 면접 시간(fixed_interview_time) 초기화
InterviewTime.objects.all().update(is_fixed=False)
Resume.objects.all().update(fixed_interview_time=None)

# 면접 시간 객체를 datetime 객체로 변환
interview_times = {
interview_time.id: interview_time.time
for interview_time in InterviewTime.objects.all()
}

# 면접시간 별 수요도 체크를 위한 딕셔너리
# 시간당 최대 3명만 배정하기 위해 카운트하는 배열
time_count = {id: 0 for id in interview_times.keys()}
fixed_applicant_count = {id: 0 for id in interview_times.keys()}

# 서류 통과한 사람들에 한해서 면접 시간 배치
applicants = (
Resume.objects.filter(is_pass_document=True)
.annotate(c=Count("interview_time_choice"))
.order_by("c")
)

# 면접 시간 수요도 조사
for applicant in applicants:
for choice in applicant.interview_time_choice.all():
# choice_time = datetime.strptime(choice.time, "%Y/%m/%d %H:%M:%S")
choice_time = choice.time
# 선택된 시간이 면접 시간 목록에 있다면 count 증가
for interview_time_id, interview_time in interview_times.items():
if choice_time == interview_time:
time_count[interview_time_id] += 1

# 수요도가 낮은 시간부터 배정하기 위해 오름차순 정렬
sorted_time_counts = (
InterviewTime.objects.annotate(applicant_count=Count("interview_time"))
.filter(is_fixed=False)
.order_by("applicant_count", "time")
)

q = Resume.objects.annotate(c=Count("interview_time_choice")).order_by("c")

for i in q:
for j in i.interview_time_choice.all():
if j.time in times:
time_num = 0
s = Resume.objects.annotate()
for (
_resume
) in s: # resume에 대해 call하는 부분 문제 어떤방식으로 call해야하나?
if j.time == _resume.fixed_interview_time:
time_num = time_num + 1
if time_num == 1:
plus20 = j.time + datetime.timedelta(minutes=20)
a = Resume.objects.annotate()
for Res in a:
if plus20 == Res.fixed_interview_time:
time_num = time_num + 1
if time_num == 2: # 2개 있을 때
i.fixed_interview_time = plus20 + datetime.timedelta(minutes=20)
i.save()
j.is_fixed = True
j.save()
times.remove(j.time)
break

else: # 1개 있을
i.fixed_interview_time = plus20
i.save()
break

else: # 0개일때
i.fixed_interview_time = j.time
i.save()
break
# 각 지원자에게 가장 수요가 적은 면접 시간을 할당
for interview_time in sorted_time_counts:
for applicant in applicants:
if applicant.fixed_interview_time == None:
# 해당 시간대가 꽉 찼으면 건너뛰기 (이미 3명이 배정됨)
if fixed_applicant_count[interview_time.id] >= 3:
continue

chosen_times = applicant.interview_time_choice.all()
for choice in chosen_times:
choice_time = choice.time
# 선택된 시간이 현재 순회 중인 면접 시간과 같은지 확인
if choice_time == interview_time.time and not choice.is_fixed:
plus20 = choice_time + timedelta(
minutes=20 * fixed_applicant_count[interview_time.id]
)
# print(plus20)
# 지원자에게 면접 시간 할당
applicant.fixed_interview_time = plus20
applicant.save()

# 할당된 시간의 수요를 1 증가시킴
fixed_applicant_count[interview_time.id] += 1

# 시간대가 꽉 찼으면 is_fixed 속성을 True로 설정
if fixed_applicant_count[interview_time.id] == 3:
interview_time.is_fixed = True
interview_time.save()

# break
else:
# 선택한 모든 시간이 이미 할당되었을 경우
# 지원자에게는 면접 시간을 할당하지 않고 다음 지원자로 넘어감
continue


# @admin.action(description="면접 시간 자동 배치")
# def set_interview_time(
# self, request, queryset
# ): # self = resume model , queryset = 체크했던 object들
# times = [queryset.time for queryset in InterviewTime.objects.all()]
# meta = self.model._meta

# q = Resume.objects.annotate(c=Count("interview_time_choice")).order_by("c")

# for i in q:
# for j in i.interview_time_choice.all():
# if j.time in times:
# time_num = 0
# s = Resume.objects.annotate()
# for (
# _resume
# ) in s: # resume에 대해 call하는 부분 문제 어떤방식으로 call해야하나?
# if j.time == _resume.fixed_interview_time:
# time_num = time_num + 1
# if time_num == 1:
# plus20 = j.time + datetime.timedelta(minutes=20)
# a = Resume.objects.annotate()
# for Res in a:
# if plus20 == Res.fixed_interview_time:
# time_num = time_num + 1
# if time_num == 2: # 2개 있을 때
# i.fixed_interview_time = plus20 + datetime.timedelta(minutes=20)
# i.save()
# j.is_fixed = True
# j.save()
# times.remove(j.time)
# break

# else: # 1개 있을
# i.fixed_interview_time = plus20
# i.save()
# break

# else: # 0개일때
# i.fixed_interview_time = j.time
# i.save()
# break


@admin.action(description="면접 장소 일괄 지정")
Expand Down
7 changes: 4 additions & 3 deletions app/apply/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.db import models
from django.utils import timezone
from user.models import Applicant
from .utils import *


class TermType(models.TextChoices):
Expand Down Expand Up @@ -42,8 +43,8 @@ def check_process(self):


class InterviewTime(models.Model):
time = models.DateTimeField()
is_fixed = models.BooleanField(default=False)
time = models.DateTimeField(validators=[validate_interview_time])
is_fixed = models.BooleanField(default=False) # 면접 시간 모두 배정되었는지 여부

def __str__(self):
return self.time.strftime("%Y/%m/%d %H:%M:%S")
Expand Down Expand Up @@ -71,7 +72,7 @@ class Resume(models.Model):
InterviewTime, related_name="interview_time"
)
fixed_interview_time = models.DateTimeField(null=True, blank=True)
interview_requirement = models.TextField(default="")
interview_requirement = models.TextField(default="") # 면접 요구 사항
interview_place = models.ForeignKey(
InterviewPlace, on_delete=models.CASCADE, null=True, blank=True
)
Expand Down
10 changes: 10 additions & 0 deletions app/apply/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _


def validate_interview_time(value):
if value.minute != 0 or value.second != 0:
raise ValidationError(
_("시간은 정각이어야 합니다."),
params={"value": value},
)
6 changes: 5 additions & 1 deletion app/image/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@

class Image(models.Model):
title = models.CharField(null=False, max_length=50)
image = models.ImageField(null=False, upload_to=image_rename)
image = models.ImageField(
null=False,
upload_to=image_rename,
validators=[validate_file_size],
)
uploaded_at = models.DateTimeField(auto_now_add=True)

class Meta:
Expand Down
9 changes: 9 additions & 0 deletions app/image/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import os
from django.utils.text import slugify
from django.core.exceptions import ValidationError


def validate_file_size(value):
max_file_size = 20 * 1024 * 1024 # 20MB
if value.size > max_file_size:
raise ValidationError(
f"파일 크기는 {max_file_size // (1024 * 1024)}MB를 초과할 수 없습니다."
)


def image_rename(instance, filename):
Expand Down

0 comments on commit a938dcd

Please sign in to comment.