Fakultas Ilmu Komputer UI

vacancies.py 29.6 KB
Newer Older
Arga Ghulam Ahmad's avatar
Arga Ghulam Ahmad committed
1
2
from datetime import datetime, time

3
from django.db.models import Q
Arga Ghulam Ahmad's avatar
Arga Ghulam Ahmad committed
4
from django.utils import timezone
5
from rest_framework import viewsets, status
6
from rest_framework.decorators import action
7
from rest_framework.exceptions import ValidationError
8
from rest_framework.generics import get_object_or_404
Zamil Majdy's avatar
Zamil Majdy committed
9
from rest_framework.pagination import PageNumberPagination
10
from rest_framework.permissions import IsAuthenticated
11
from rest_framework.response import Response
12
from core.views import views_constants
Zamil Majdy's avatar
Zamil Majdy committed
13

14
from core.lib.mixins import MultiSerializerViewSetMixin
Zamil Majdy's avatar
Zamil Majdy committed
15
from core.lib.permissions import IsAdminOrStudent, IsAdminOrCompany, IsAdminOrVacancyOwner, AsAdminOrSupervisor, \
16
    VacancyApprovalPermission, IsAdminOrVacancyOwnerOrAuthenticatedReadOnly
17
from core.models import Student, Company
18
from core.models.vacancies import Vacancy, Application, ReasonRejected
Refo Ilmiya Akbar's avatar
Refo Ilmiya Akbar committed
19
from core.serializers.vacancies import VacancySerializer, ApplicationSerializer, ApplicationStatusSerializer, \
20
    VacancyVerifiedSerializer, SupervisorStudentApplicationSerializer, \
21
    VacancyMilestoneSerializer
Zamil Majdy's avatar
Zamil Majdy committed
22
from core.views.accounts import StudentViewSet
23

24
25
26

def date_validator(open_time, close_time):
    if open_time < str(datetime.today()):
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
27
28
29
        return {views_constants.IS_VALID: False,
                views_constants.ERROR: views_constants.ERROR_OPEN_TIME_GREATER_THAN_CURRENT_TIME,
                views_constants.STATUS: status.HTTP_400_BAD_REQUEST}
30
    elif close_time < open_time:
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
31
32
33
        return {views_constants.IS_VALID: False,
                views_constants.ERROR: views_constants.ERROR_OPEN_TIME_GREATER_THAN_CLOSE_TIME,
                views_constants.STATUS: status.HTTP_400_BAD_REQUEST}
34
    elif close_time == open_time:
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
35
36
37
        return {views_constants.IS_VALID: False,
                views_constants.ERROR: views_constants.ERROR_OPEN_TIME_EQUALS_CLOSE_TIME,
                views_constants.STATUS: status.HTTP_400_BAD_REQUEST}
38
    else:
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
39
        return {views_constants.IS_VALID: True, views_constants.ERROR: '', views_constants.STATUS: status.HTTP_200_OK}
40
41


42
class VacancyViewSet(MultiSerializerViewSetMixin, viewsets.ModelViewSet):
43
    queryset = Vacancy.objects.all()
44
    serializer_class = VacancySerializer
45
    permission_classes = [IsAdminOrCompany]
46
    pagination_class = PageNumberPagination
47

48
    def get_permissions(self):
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
49
        if self.action in [views_constants.ACTION_RETRIEVE, views_constants.ACTION_LIST]:
50
51
52
            return [IsAuthenticated()]
        return super(VacancyViewSet, self).get_permissions()

53
    def list(self, request, *args, **kwargs):
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
54
55
56
57
58
59
        search = request.query_params[views_constants.SEARCH] \
            if views_constants.SEARCH in request.query_params else None
        verified = request.query_params[views_constants.VERIFIED] \
            if views_constants.VERIFIED in request.query_params else "True"
        order_by = request.GET.get(views_constants.SORT, None)
        order = request.GET.get(views_constants.ORDERING, views_constants.ORDER_ASCENDING)
60

Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
61
62
63
        opened_only = str(request.query_params[views_constants.OPENED_ONLY]) \
            if views_constants.OPENED_ONLY in request.query_params else "False"
        companies = [int(x) for x in request.query_params.getlist(views_constants.COMPANY, [])]
64

65
66
67
68
        if search is not None:
            vacancies = Vacancy.objects.filter(Q(name__icontains=search) | Q(company__user__username__icontains=search))
        else:
            vacancies = Vacancy.objects.all()
69

70
71
72
73
        # Filter vacancies here
        vacancies = self.verifiedFilter(vacancies, verified)
        vacancies = self.openedJobFilter(vacancies, opened_only)
        vacancies = self.companyFilter(vacancies, companies)
74
        vacancies = self.sort(order_by, vacancies, order)
75

76
        page = self.paginate_queryset(vacancies)
root's avatar
root committed
77
78
        print(vacancies)
        print(request)
79
        if page is not None:
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
80
81
            return self.get_paginated_response(
                VacancySerializer(page, many=True, context={views_constants.REQUEST: request}).data)
82
        vacancies = Vacancy.objects.all()
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
83
        return Response(VacancySerializer(vacancies, many=True, context={views_constants.REQUEST: request}).data)
84

85
86
87
88
89
90
    def verifiedFilter(self, vacancies, flag):
        if flag.lower() in ("yes", "true", "t", "1"):
            vacancies = vacancies.filter(verified=True)
        if flag.lower() in {"no", "false", "f", "0"}:
            vacancies = vacancies.filter(verified=False)
        return vacancies
Refo Ilmiya Akbar's avatar
Refo Ilmiya Akbar committed
91

92
93
94
95
96
97
98
99
100
101
    def openedJobFilter(self, vacancies, flag):
        if flag.lower() in ("yes", "true", "t", "1"):
            today = datetime.combine(datetime.now().date(), time())
            vacancies = vacancies.filter(close_time__gte=today)
        return vacancies

    def companyFilter(self, vacancies, companies):
        if len(companies) > 0:
            vacancies = vacancies.filter(company__id__in=companies)
        return vacancies
102

103
    def sort(self, order_by, vacancies, order):
Raden Fikri Ihza Dwi Nanda's avatar
Raden Fikri Ihza Dwi Nanda committed
104
        if (order_by is not None) and (order_by in [views_constants.NAME, views_constants.SALARY, views_constants.MOST_RECENT, views_constants.CLOSE_TIME, views_constants.OPEN_TIME]) and (
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
105
                order in [views_constants.ORDER_ASCENDING, views_constants.ORDER_DESCENDING]):
106
107
            if order_by == views_constants.MOST_RECENT:
                return vacancies.order_by('-created')
108
            order_query = ""
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
109
            if order == views_constants.ORDER_DESCENDING:
110
111
                order_query = "-"
            return vacancies.order_by('{}{}'.format(order_query, order_by))
112
        return vacancies
113

114
115
116
    def name_position_validator(self, names):
        for name in names.split(" "):
            if not name.isalpha():
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
117
                raise ValidationError(views_constants.ERROR_NAME_MUST_ALPHABET)
118
119
        return name

120
    def salary_validator(self, salary):
Kemas Khaidar Ali's avatar
Kemas Khaidar Ali committed
121
122
123
        try:
            int(salary)
        except:
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
124
            raise ValidationError(views_constants.ERROR_SALARY_MUST_NUMBER)
125
126

    def amount_validator(self, amount):
Kemas Khaidar Ali's avatar
Kemas Khaidar Ali committed
127
128
129
        try:
            int(amount)
        except:
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
130
            raise ValidationError(views_constants.ERROR_AMOUNT_MUST_NUMBER)
131

Refo Ilmiya Akbar's avatar
Refo Ilmiya Akbar committed
132
133
134
    def office_address_validator(self, office_address):
        print(office_address)
        print(type(office_address))
135
        if not isinstance(office_address, str):
Refo Ilmiya Akbar's avatar
Refo Ilmiya Akbar committed
136
137
138
            print('DEBUG ERROR')
            raise ValidationError(views_constants.ERROR_INVALID_OFFICE_ADDRESS)

139
    def responsibilities_validator(self, responsibilities):
140
        if not isinstance(responsibilities, str):
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
141
            raise ValidationError(views_constants.ERROR_RESPONSIBILITIES_MUST_STRING)
142
143

    def recruiter_activity_validator(self, recruiter_activity):
Refo Ilmiya Akbar's avatar
Refo Ilmiya Akbar committed
144
        enum_recruiter_activity = ['Selalu', 'Sering', 'Kadang', 'Jarang', 'Tidak Pernah']
145
        if (not isinstance(recruiter_activity, str)) or (recruiter_activity not in enum_recruiter_activity):
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
146
            raise ValidationError(views_constants.ERROR_INVALID_RECRUITER_RESPONSE)
147
148
149

    def date_range_validator(self, open_time, close_time):
        validity = date_validator(open_time, close_time)
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
150
151
        if not validity[views_constants.IS_VALID]:
            raise ValidationError(validity[views_constants.ERROR])
152
153
154
155

    def __get_company(self, company_id):
        company_set = Company.objects.filter(id=company_id)
        if len(company_set) == 0:
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
156
            raise ValidationError(views_constants.ERROR_COMPANY_DOES_NOT_EXIST)
157
158
159
160
        return company_set[0]

    def __parse_vacancy_data(self, raw_data):
        data = {
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
161
162
163
164
165
            views_constants.OPEN_TIME: raw_data[views_constants.OPEN_TIME],
            views_constants.CLOSE_TIME: raw_data[views_constants.CLOSE_TIME],
            views_constants.NAME: raw_data[views_constants.NAME],
            views_constants.SALARY: raw_data.get(views_constants.SALARY, 0),
            views_constants.TAG: raw_data.get(views_constants.TAG, ''),
Refo Ilmiya Akbar's avatar
Refo Ilmiya Akbar committed
166
            views_constants.OFFICE_ADDRESS: raw_data.get(views_constants.OFFICE_ADDRESS, ''),
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
167
168
169
170
171
172
173
            views_constants.REQUIREMENTS: raw_data.get(views_constants.REQUIREMENTS, ''),
            views_constants.RESPONSIBILITIES: raw_data.get(views_constants.RESPONSIBILITIES, ''),
            views_constants.BENEFITS: raw_data.get(views_constants.BENEFITS, ''),
            views_constants.AMOUNT: raw_data.get(views_constants.AMOUNT, 0),
            views_constants.RECRUITER_ACTIVITY: raw_data.get(views_constants.RECRUITER_ACTIVITY, ''),
            views_constants.DESCRIPTION: raw_data[views_constants.DESCRIPTION],
            views_constants.WORKING_PERIOD: raw_data[views_constants.WORKING_PERIOD],
Refo Ilmiya Akbar's avatar
Refo Ilmiya Akbar committed
174
            views_constants.MAX_ACCEPTED_APPLICANTS: raw_data[views_constants.MAX_ACCEPTED_APPLICANTS],
175
176
177
178
        }
        return data

    def __validate_vacancy_data(self, data):
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
179
180
181
182
183
184
185
186
187
        self.date_range_validator(data[views_constants.OPEN_TIME], data[views_constants.CLOSE_TIME])
        self.name_position_validator(data[views_constants.NAME])
        self.salary_validator(data[views_constants.SALARY])
        if data.get(views_constants.AMOUNT):
            self.amount_validator(data[views_constants.AMOUNT])
        if data.get(views_constants.RESPONSIBILITIES):
            self.responsibilities_validator(data[views_constants.RESPONSIBILITIES])
        if data.get(views_constants.RECRUITER_ACTIVITY):
            self.recruiter_activity_validator(data[views_constants.RECRUITER_ACTIVITY])
Refo Ilmiya Akbar's avatar
Refo Ilmiya Akbar committed
188
189
        if data.get(views_constants.OFFICE_ADDRESS):
            self.office_address_validator(data[views_constants.OFFICE_ADDRESS])
Refo Ilmiya Akbar's avatar
Refo Ilmiya Akbar committed
190

191
192
193
194
    def create(self, request):
        print("[LOG] data: " + str(request.data))
        try:
            data = self.__parse_vacancy_data(request.data)
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
195
            data[views_constants.COMPANY] = self.__get_company(request.data[views_constants.COMPANY])
196
197
198
199
200
            self.__validate_vacancy_data(data)
            vacancy = Vacancy(**data)
            vacancy.save()
        except ValidationError as e:
            print("[LOG] error: " + str(e))
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
201
            return Response({views_constants.ERROR: str(e.detail[0])}, status=status.HTTP_400_BAD_REQUEST)
Yusuf Sholeh's avatar
Yusuf Sholeh committed
202
203
        except ValueError as e:
            print("[LOG] error: " + str(e))
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
204
            return Response({views_constants.ERROR: str(e)}, status=status.HTTP_400_BAD_REQUEST)
205
206
207
        return Response(status=status.HTTP_200_OK)

    def partial_update(self, request, pk):
208
209
210
211
212
213
214
215
216
217
        print("[LOG] data: " + str(request.data))
        try:
            data = self.__parse_vacancy_data(request.data)
            self.__validate_vacancy_data(data)
            vacancy = Vacancy.objects.get(pk=pk)
            for (key, value) in data.items():
                setattr(vacancy, key, value)
            vacancy.save()
        except ValidationError as e:
            print("[LOG] error: " + str(e))
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
218
            return Response({views_constants.ERROR: str(e.detail[0])}, status=status.HTTP_400_BAD_REQUEST)
219
220
        return Response(status=status.HTTP_200_OK)

221
    @action(detail=True, permission_classes=[IsAdminOrCompany])
222
223
224
225
    def count(self, request, pk=None):
        vacancy = self.get_object()
        count = Application.objects.filter(vacancy=vacancy).count()
        count_new = Application.objects.filter(vacancy=vacancy, status=Application.NEW).count()
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
226
        return Response({views_constants.COUNT: count, views_constants.COUNT_NEW: count_new}, status=status.HTTP_200_OK)
227

228
229
    @action(detail=True, methods=[views_constants.METHOD_PATCH], permission_classes=[VacancyApprovalPermission],
            serializer_class=VacancyVerifiedSerializer)
230
231
232
233
234
235
    def verify(self, request, pk=None):
        vacancy = self.get_object()
        serializer = self.get_serializer_class()(vacancy, data=request.data, partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_200_OK)
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
236
        return Response({views_constants.ERROR: views_constants.ERROR_BAD_REQUEST}, status=status.HTTP_400_BAD_REQUEST)
237

238

Zamil Majdy's avatar
Zamil Majdy committed
239
240
241
242
class ApplicationViewSet(MultiSerializerViewSetMixin, viewsets.GenericViewSet):
    queryset = Application.objects.all()
    serializer_class = ApplicationSerializer
    serializer_action_classes = {
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
243
244
245
        views_constants.ACTION_UPDATE: ApplicationStatusSerializer,
        views_constants.ACTION_PARTIAL_UPDATE: ApplicationStatusSerializer,
        views_constants.ACTION_LIST: SupervisorStudentApplicationSerializer
Zamil Majdy's avatar
Zamil Majdy committed
246
247
248
249
250
    }
    permission_classes = [IsAdminOrVacancyOwner]
    pagination_class = PageNumberPagination

    def list(self, request):
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
251
        applications = Application.objects.order_by(views_constants.ORDER_STUDENT)
Zamil Majdy's avatar
Zamil Majdy committed
252
253
254
255
        page = self.paginate_queryset(applications)
        serializer = self.get_serializer_class()
        if page is not None:
            return self.get_paginated_response(
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
256
257
                serializer(applications, many=True, context={views_constants.REQUEST: request}).data)
        return Response(serializer(applications, many=True, context={views_constants.REQUEST: request}).data)
Zamil Majdy's avatar
Zamil Majdy committed
258

259
    @action(detail=True, methods=[views_constants.METHOD_GET], permission_classes=[IsAdminOrStudent])
260
261
    def count(self, request, pk=None):
        count = Application.objects.filter(vacancy_id=pk).count()
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
262
        return Response({views_constants.COUNT: count}, status=status.HTTP_200_OK)
263

Zamil Majdy's avatar
Zamil Majdy committed
264
265
266
267
268
    def partial_update(self, request, pk=None):
        application = self.get_object()
        serializer = self.get_serializer_class()(application, data=request.data, partial=True)
        if serializer.is_valid():
            serializer.save()
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
269
270
            if request.data[views_constants.STATUS] == 3:
                if request.data[views_constants.REASON] == "" or request.data[views_constants.REASON] == " ":
271
272
                    ReasonRejected.objects.create(application=application)
                else:
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
273
                    ReasonRejected.objects.create(application=application, reason=request.data[views_constants.REASON])
Zamil Majdy's avatar
Zamil Majdy committed
274
275
276
277
            return Response(serializer.data, status=status.HTTP_202_ACCEPTED)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

278
    @action(detail=True, methods=[views_constants.METHOD_GET], permission_classes=[IsAdminOrVacancyOwner])
Zamil Majdy's avatar
Zamil Majdy committed
279
280
    def transcript(self, request, pk):
        """
Zamil Majdy's avatar
Zamil Majdy committed
281
        Get student's academic transcript on application {application_id}
Zamil Majdy's avatar
Zamil Majdy committed
282
283
284
        ---
        """
        application = self.get_object()
Zamil Majdy's avatar
Zamil Majdy committed
285
        return StudentViewSet.transcript(StudentViewSet(), request, application.student.pk)
Zamil Majdy's avatar
Zamil Majdy committed
286
287

    def get_permissions(self):
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
288
        if self.action == views_constants.ACTION_LIST:
Zamil Majdy's avatar
Zamil Majdy committed
289
290
291
292
293
            return [AsAdminOrSupervisor()]
        return super(ApplicationViewSet, self).get_permissions()


class StudentApplicationViewSet(viewsets.GenericViewSet):
294
    serializer_class = ApplicationSerializer
295
    permission_classes = [IsAdminOrStudent]
296
    pagination_class = PageNumberPagination
297

298
    def list(self, request, student_id):
299
        """
300
        Get list of a student {student_id}'s applied vacancies
301
302
        ---
        """
303
        student = get_object_or_404(Student.objects.all(), pk=student_id)
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
304
305
        vacancy_ids = Application.objects.filter(student=student).values(views_constants.VACANCY)
        search = request.query_params[views_constants.SEARCH] if views_constants.SEARCH in request.query_params else None
306
        if search is not None:
Refo Ilmiya Akbar's avatar
Refo Ilmiya Akbar committed
307
308
            vacancies = Vacancy.objects.filter(
                Q(id__in=vacancy_ids) & (Q(name__icontains=search) | Q(company__user__username__icontains=search)))
309
310
        else:
            vacancies = Vacancy.objects.filter(id__in=vacancy_ids)
311
312
        page = self.paginate_queryset(vacancies)
        if page is not None:
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
313
314
            return self.get_paginated_response(VacancySerializer(page, many=True, context={views_constants.REQUEST: request}).data)
        return Response(VacancySerializer(vacancies, many=True, context={views_constants.REQUEST: request}).data)
315
316

    def create(self, request, student_id):
317
318
319
320
321
        """
        Create a new application for student {student_id}
        ---
        parameters:
            - name: body
322
              description: JSON object containing an integer 'vacancy_id' and a string 'cover_letter'
323
324
325
326
              required: true
              type: string
              paramType: body
        """
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
327
328
        cover_letter = request.data.get(views_constants.COVER_LETTER)
        vacancy = get_object_or_404(Vacancy.objects.all(), pk=request.data.get(views_constants.VACANCY_ID))
329
        if vacancy.close_time < timezone.now():
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
330
            return Response({views_constants.ERROR: views_constants.ERROR_VACANCY_CLOSED},
Refo Ilmiya Akbar's avatar
Refo Ilmiya Akbar committed
331
                            status=status.HTTP_400_BAD_REQUEST)
332
        student = get_object_or_404(Student.objects.all(), pk=student_id)
333
        if Application.objects.filter(vacancy=vacancy, student=student).exists():
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
334
            raise ValidationError(views_constants.ERROR_VACANCY_ALREADY_APPLIED)
335
336
        application = Application(vacancy=vacancy, student=student, cover_letter=cover_letter)
        application.save()
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
337
        return Response(ApplicationSerializer(application, context={views_constants.REQUEST: request}).data)
338
339

    def destroy(self, request, student_id, pk):
340
341
342
343
        """
        Remove a application {id} for student {student_id}
        ---
        """
344
345
        vacancy = get_object_or_404(Vacancy.objects.all(), pk=pk)
        student = get_object_or_404(Student.objects.all(), pk=student_id)
346
        application = get_object_or_404(Application.objects.all(), student=student, vacancy=vacancy)
347
        application.delete()
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
348
        return Response(ApplicationSerializer(application, context={views_constants.REQUEST: request}).data)
349

350
    @action(detail=True, methods=[views_constants.METHOD_GET], permission_classes=[IsAdminOrStudent])
351
352
353
354
355
356
357
358
    def reason(self, request, student_id, pk):
        """
        Get the reason for rejection of application {id} for student {student_id}
        ---
        """
        vacancy = get_object_or_404(Vacancy.objects.all(), pk=pk)
        student = get_object_or_404(Student.objects.all(), pk=student_id)
        application = get_object_or_404(Application.objects.all(), student=student, vacancy=vacancy)
359
360
        reason_rejected = get_object_or_404(ReasonRejected.objects.all(), application=application)
        print(reason_rejected.reason)
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
361
        return Response({views_constants.REASON: reason_rejected.reason}, status=status.HTTP_200_OK)
362

363

364
365
366
class StatusError(Exception):
    pass

367

368
369
class UnauthorizeError(Exception):
    pass
370

371

372
373
class CompanyApplicationViewSet(viewsets.GenericViewSet):
    queryset = Application.objects.all()
374
    permission_classes = [IsAdminOrCompany]
375
    pagination_class = PageNumberPagination
376
377
378
379
380
381

    def list(self, request, company_id):
        """
        Get list of company {company_id}'s applications
        ---
        """
382
383
384
385
        try:
            company = self.__get_company_list_by_company_id(request, company_id)
            vacancies = self.__get_vacancy_list_filter_by_company(company)
            applications = self.__get_application_list_filter_by_vacancies(vacancies)
386
387
            applications = self.__filter_application_list_by_status(request, applications)
            return self.__get_paginated_application_list(request, applications)
388
        except UnauthorizeError:
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
389
            return Response({views_constants.ERROR: views_constants.ERROR_FORBIDDEN}, status=status.HTTP_403_FORBIDDEN)
390
        except (StatusError, ValueError):
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
391
            return Response({views_constants.ERROR: views_constants.ERROR_INVALID_STATUS_CODE},
392
                            status=status.HTTP_400_BAD_REQUEST)
393

394
    @action(detail=True, methods=[views_constants.METHOD_GET])
395
    def by_vacancy(self, request, company_id, pk=None):
Zamil Majdy's avatar
Zamil Majdy committed
396
397
398
399
        """
        Get list of company {company_id}'s applications by vacancy {id}
        ---
        """
400
401
402
403
404
        try:
            if pk is None:
                return list(self, request, company_id)
            company = self.__get_company_list_by_company_id(request, company_id)
            vacancy = self.__get_vacancy_list_by_pk(pk, company)
405
406
407
            applications = self.__get_application_by_vacancy(vacancy)
            applications = self.__filter_application_list_by_status(request, applications)
            return self.__get_paginated_application_list(request, applications)
408
        except UnauthorizeError:
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
409
            return Response({views_constants.ERROR: views_constants.ERROR_FORBIDDEN}, status=status.HTTP_403_FORBIDDEN)
410
        except (StatusError, ValueError):
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
411
            return Response({views_constants.ERROR: views_constants.ERROR_INVALID_STATUS_CODE},
412
                            status=status.HTTP_400_BAD_REQUEST)
413
414

    def __get_company_list_by_company_id(self, request, company_id):
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
415
        company = get_object_or_404(Company.objects.all().order_by(views_constants.ORDER_UPDATED), pk=company_id)
416
417
        if not self.__validating_user(request, company):
            raise UnauthorizeError
Refo Ilmiya Akbar's avatar
Refo Ilmiya Akbar committed
418
        return company
419
420
421

    def __validating_user(self, request, company):
        return request.user.is_superuser or request.user == company.user
Refo Ilmiya Akbar's avatar
Refo Ilmiya Akbar committed
422

423
    def __get_vacancy_list_by_pk(self, pk, company):
424
        vacancy = get_object_or_404(Vacancy.objects.all(), pk=pk)
425
426
427
428
429
430
431
432
433
        if not self.__validating_vacancy(vacancy, company):
            raise UnauthorizeError
        return vacancy

    def __validating_vacancy(self, vacancy, company):
        return vacancy.company == company

    def __get_vacancy_list_filter_by_company(self, company):
        return Vacancy.objects.filter(company=company)
434

435
436
437
    def __get_application_list_filter_by_vacancies(self, vacancies):
        return Application.objects.filter(vacancy__in=vacancies)

438
    def __get_application_by_vacancy(self, vacancy):
439
        return Application.objects.filter(vacancy=vacancy)
440

441
    def __get_status_from_request_param(self, request):
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
442
        return request.query_params.get(views_constants.STATUS, None)
Refo Ilmiya Akbar's avatar
Refo Ilmiya Akbar committed
443

444
    def __validating_application_status(self, status):
Refo Ilmiya Akbar's avatar
Refo Ilmiya Akbar committed
445
        list_status = {0: "NEW", 1: "READ", 2: "BOOKMARKED", 3: "REJECTED", 4: "ACCEPTED", 6: "FINISHED"}
446
447
448
        if status not in list_status:
            raise StatusError
        return True
449

450
451
452
453
454
455
456
457
458
459
460
461
    def __filter_application_list_by_status(self, request, applications):
        status = self.__get_status_from_request_param(request)
        if status is not None:
            status = int(status)
            self.__validating_application_status(status)
            return applications.filter(status=status)
        return applications

    def __get_paginated_application_list(self, request, applications):
        page = self.paginate_queryset(applications)
        if page is not None:
            return self.get_paginated_response(
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
462
463
                ApplicationSerializer(page, many=True, context={views_constants.REQUEST: request}).data)
        return Response(ApplicationSerializer(applications, many=True, context={views_constants.REQUEST: request}).data)
464
465


466
class CompanyVacanciesViewSet(viewsets.GenericViewSet):
467
    queryset = Vacancy.objects.all()
468
    pagination_class = PageNumberPagination
469
    permission_classes = [IsAdminOrCompany]
470
471
472
473
474
475

    def list(self, request, company_id):
        """
        Get list of company {company_id}'s vacancies
        ---
        """
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
476
        company = get_object_or_404(Company.objects.all().order_by(views_constants.ORDER_UPDATED), pk=company_id)
477
        if not request.user.is_superuser and request.user != company.user:
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
478
            return Response({views_constants.ERROR: views_constants.ERROR_FORBIDDEN}, status=status.HTTP_403_FORBIDDEN)
479
        vacancies = Vacancy.objects.filter(company=company)
480
481
        page = self.paginate_queryset(vacancies)
        if page is not None:
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
482
483
            return self.get_paginated_response(VacancySerializer(page, many=True, context={views_constants.REQUEST: request}).data)
        return Response(VacancySerializer(vacancies, many=True, context={views_constants.REQUEST: request}).data)
484
485


486
487
488
489
490
class BookmarkedVacancyByStudentViewSet(viewsets.GenericViewSet):
    serializer_class = VacancySerializer
    permission_classes = [IsAdminOrStudent]

    def list(self, request, student_id):
491
492
493
494
        """
        Get list of a student {student_id}'s bookmarked vacancies
        ---
        """
495
        student = get_object_or_404(Student.objects.all(), pk=student_id)
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
496
        search = request.query_params[views_constants.SEARCH] if views_constants.SEARCH in request.query_params else None
497
        if search is not None:
Refo Ilmiya Akbar's avatar
Refo Ilmiya Akbar committed
498
499
            vacancies = student.bookmarked_vacancies.filter(
                Q(name__icontains=search) | Q(company__user__username__icontains=search))
500
501
        else:
            vacancies = student.bookmarked_vacancies.all()
502
503
        page = self.paginate_queryset(vacancies)
        if page is not None:
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
504
505
            return self.get_paginated_response(VacancySerializer(page, many=True, context={views_constants.REQUEST: request}).data)
        return Response(VacancySerializer(vacancies, many=True, context={views_constants.REQUEST: request}).data)
506
507

    def create(self, request, student_id):
508
509
510
511
512
513
514
515
516
517
        """
        Bookmarks a vacancy for student {student_id}
        ---
        parameters:
            - name: body
              description: JSON object containing only one string: vacancy_id
              required: true
              type: string
              paramType: body
        """
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
518
        vacancy = get_object_or_404(Vacancy.objects.all(), pk=request.data[views_constants.VACANCY_ID])
519
520
        student = get_object_or_404(Student.objects.all(), pk=student_id)
        student.bookmarked_vacancies.add(vacancy)
Zamil Majdy's avatar
Zamil Majdy committed
521
        return Response(
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
522
            self.serializer_class(student.bookmarked_vacancies, many=True, context={views_constants.REQUEST: request}).data)
523
524

    def destroy(self, request, student_id, pk):
525
526
527
528
        """
        Remove bookmark {id} for student {student_id}
        ---
        """
529
530
531
        vacancy = get_object_or_404(Vacancy.objects.all(), pk=pk)
        student = get_object_or_404(Student.objects.all(), pk=student_id)
        student.bookmarked_vacancies.remove(vacancy)
Zamil Majdy's avatar
Zamil Majdy committed
532
        return Response(
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
533
            self.serializer_class(student.bookmarked_vacancies, many=True, context={views_constants.REQUEST: request}).data)
534

535
536
537
538
539
540
541
542
543
544
545
546
547
548
549

class VacancyMilestoneViewSet(viewsets.GenericViewSet):
    serializer_class = VacancyMilestoneSerializer
    permission_classes = [IsAdminOrVacancyOwnerOrAuthenticatedReadOnly]

    def list(self, request, vacancy_id):
        """
        Get list of a vacancy {vacancy_id}'s milestone plans
        ---
        """
        vacancy = get_object_or_404(Vacancy.objects.all(), pk=vacancy_id)
        milestones = vacancy.milestones.all()
        page = self.paginate_queryset(milestones)
        if page is not None:
            return self.get_paginated_response(
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
550
551
                self.serializer_class(page, many=True, context={views_constants.REQUEST: request}).data)
        return Response(self.serializer_class(milestones, many=True, context={views_constants.REQUEST: request}).data)
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568

    def create(self, request, vacancy_id):
        """
        Create a new milestone for vacancy {vacancy_id}
        ---
        parameters:
            - name: body
              description: JSON object containing 'name' string, 'detail' string, 'expected_start' date string, and
                           'expected_finish' date string
              required: true
              type: string
              paramType: body
        """
        vacancy = get_object_or_404(Vacancy.objects.all(), pk=vacancy_id)
        milestone_serializer = self.serializer_class(data=request.data)
        if milestone_serializer.is_valid():
            milestone = milestone_serializer.save(vacancy=vacancy)
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
569
            return Response(self.serializer_class(milestone, context={views_constants.REQUEST: request}).data,
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
                            status=status.HTTP_200_OK)
        return Response(milestone_serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def update(self, request, vacancy_id, pk):
        """
        Create existing milestone {pk} for vacancy {vacancy_id}
        ---
        parameters:
            - name: body
              description: JSON object containing 'name' string, 'detail' string, 'expected_start' date string, and
                           'expected_finish' date string
              required: true
              type: string
              paramType: body
        """
        vacancy = get_object_or_404(Vacancy.objects.all(), pk=vacancy_id)
        old_milestone = get_object_or_404(vacancy.milestones.all(), pk=pk)
        milestone_serializer = self.serializer_class(old_milestone, data=request.data)
        if milestone_serializer.is_valid():
            milestone = milestone_serializer.save(vacancy=vacancy)
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
590
            return Response(self.serializer_class(milestone, context={views_constants.REQUEST: request}).data,
591
592
593
594
595
596
597
598
599
600
601
602
                            status=status.HTTP_200_OK)
        return Response(milestone_serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def destroy(self, request, vacancy_id, pk):
        """
        Remove existing milestone {pk} from vacancy {vacancy_id}
        ---
        """
        vacancy = get_object_or_404(Vacancy.objects.all(), pk=vacancy_id)
        milestone = get_object_or_404(vacancy.milestones.all(), pk=pk)
        milestone.delete()
        return Response(
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
603
            self.serializer_class(vacancy.milestones.all(), many=True, context={views_constants.REQUEST: request}).data)
604
605


606
607
608
609
610
611
612
613
614
class AcceptOfferByStudentViewSet(MultiSerializerViewSetMixin, viewsets.GenericViewSet):
    queryset = Application.objects.all()
    permission_classes = [IsAdminOrStudent]

    def partial_update(self, request, student_id, pk=None):
        """
        Get list of a student {student_id}'s cancel offered vacancies
        ---
        """
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
615
        student = get_object_or_404(Student.objects.all().order_by(views_constants.ORDER_UPDATED), pk=student_id)
616
        apps = Application.objects.filter(student=student)
617

618
619
        for a in apps:
            if a.vacancy_id != int(pk):
Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
620
                serializer = ApplicationStatusSerializer(a, data={views_constants.STATUS: 5}, partial=True)
621
622
                if serializer.is_valid():
                    serializer.save()
623

Ilham Darmawan Candra Purnama's avatar
Ilham Darmawan Candra Purnama committed
624
        return Response(SupervisorStudentApplicationSerializer(apps, many=True, context={views_constants.REQUEST: request}).data)