Fakultas Ilmu Komputer UI

Commit cb1a238f authored by Swastinika Naima Moertadho's avatar Swastinika Naima Moertadho
Browse files

Sebagai perusahaan, saya ingin bisa mencantumkan alasan saya menolak sebuah...

Sebagai perusahaan, saya ingin bisa mencantumkan alasan saya menolak sebuah lamaran sehingga pelamar tersebut bisa mengetahui kekurangannya dan bisa mengevaluasi diri ketika melamar pekerjaan ke depannya.
parent 8867cd2b
File deleted
......@@ -17,6 +17,7 @@ export default class ApproveModal extends React.Component {
modalOpen: false,
rejectLoading: false,
acceptLoading: false,
formValue: '',
finishLoading: false,
};
this.handleOpen = this.handleOpen.bind(this);
......@@ -26,6 +27,7 @@ export default class ApproveModal extends React.Component {
this.gotoStudentProfile = this.gotoStudentProfile.bind(this);
this.gotoStudentResume = this.gotoStudentResume.bind(this);
this.gotoStudentTranscript = this.gotoStudentTranscript.bind(this);
this.formValueHandler = this.formValueHandler.bind(this);
}
componentWillUpdate() {
......@@ -47,6 +49,10 @@ export default class ApproveModal extends React.Component {
this.setState({ modalOpen: false });
};
formValueHandler(event) {
this.setState({ formValue: event });
}
readApplication = () => {
const data = { status: Applicant.APPLICATION_STATUS.READ };
return (
......@@ -58,7 +64,7 @@ export default class ApproveModal extends React.Component {
};
rejectApplication = () => {
const data = { status: Applicant.APPLICATION_STATUS.REJECTED };
const data = { status: Applicant.APPLICATION_STATUS.REJECTED, reason: this.state.formValue };
this.setState({ rejectLoading: true });
Server.patch(`/applications/${this.props.data.id}/`, data).then((status) => {
this.props.updateStatus(this.props.data.id, status.status);
......@@ -71,6 +77,7 @@ export default class ApproveModal extends React.Component {
'Apakah anda yakin untuk menolak lamaran ini?',
'trash',
this.rejectApplication,
true,
);
};
......@@ -107,6 +114,7 @@ export default class ApproveModal extends React.Component {
'Apakah anda yakin untuk menerima lamaran ini?',
'checkmark',
this.acceptApplication,
false,
);
};
......@@ -131,9 +139,7 @@ export default class ApproveModal extends React.Component {
onClose={this.handleClose}
>
<ConfirmationModal
ref={(modal) => {
this.modal = modal;
}}
ref={(modal) => { this.modal = modal; }} formValueHandler={this.formValueHandler}
/>
<Modal.Header>Data Lamaran</Modal.Header>
<Modal.Content>
......
import React from 'react';
import { Modal, Button, Icon, Header } from 'semantic-ui-react';
import { Modal, Button, Icon, Header, Form } from 'semantic-ui-react';
export default class ConfirmationModal extends React.Component {
......@@ -10,11 +10,15 @@ export default class ConfirmationModal extends React.Component {
modalOpen: false,
header: '',
content: '',
formValue: '',
formPlaceholder: 'Tulis alasan penolakan',
icon: 'trash',
isForm: false,
callback: () => {},
};
this.open = this.open.bind(this);
this.handleYes = this.handleYes.bind(this);
this.handleChange = this.handleChange.bind(this);
}
componentWillUpdate() {
......@@ -34,10 +38,17 @@ export default class ConfirmationModal extends React.Component {
modalOpen: false,
});
handleChange = (event) => {
this.setState({ formValue: event.target.value });
this.props.formValueHandler(this.state.formValue);
};
handleYes = () => { this.state.callback(); this.handleClose(); }
open = (header = this.state.header, content = this.state.content, icon = this.state.icon, callback = this.state.callback()) => {
this.setState({ modalOpen: true, header, content, callback, icon });
open = (
header = this.state.header, content = this.state.content, icon = this.state.icon,
callback = this.state.callback(), isForm = this.state.isForm) => {
this.setState({ modalOpen: true, header, content, callback, icon, isForm });
};
render = () => (
......@@ -48,6 +59,8 @@ export default class ConfirmationModal extends React.Component {
<Header icon={this.state.icon} content={this.state.header} />
<Modal.Content>
<p>{this.state.content}</p>
{this.state.isForm ?
<Form.Input style={{ width: '650px', height: '100px' }} type="text" placeholder={this.state.formPlaceholder} value={this.state.value} onChange={this.handleChange} /> : null }
</Modal.Content>
<Modal.Actions>
<Button onClick={this.handleClose} basic color="red" inverted>
......
# -*- coding: utf-8 -*-
# Generated by Django 1.11.17 on 2019-10-12 08:21
# Generated by Django 1.11.17 on 2019-10-12 09:54
from __future__ import unicode_literals
import core.lib.validators
......@@ -76,6 +76,7 @@ class Migration(migrations.Migration):
('photo', models.FileField(blank=True, null=True, upload_to=core.models.accounts.get_student_photo_file_path, validators=[core.lib.validators.validate_image_file_extension])),
('portfolio_link', models.URLField(blank=True, null=True)),
('linkedin_url', models.URLField(blank=True, null=True)),
('hackerrank_url', models.URLField(blank=True, null=True)),
('website_url', models.URLField(blank=True, null=True)),
('work_experience', models.CharField(blank=True, max_length=500, null=True)),
('region', models.CharField(blank=True, max_length=30, null=True)),
......@@ -140,6 +141,13 @@ class Migration(migrations.Migration):
('vacancy', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='milestones', to='core.Vacancy')),
],
),
migrations.CreateModel(
name='ReasonRejected',
fields=[
('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='core.Application')),
('reason', models.TextField(default=b'Tidak memenuhi kualifikasi perusahaan.')),
],
),
migrations.AddField(
model_name='student',
name='applied_vacancies',
......
......@@ -22,7 +22,7 @@ class Vacancy(models.Model):
@property
def apply_before(self):
if(self.close_time<timezone.now()):
return "Pendaftaran ditutup"
return "Pendaftaran ditutup"
return "Daftar sebelum "+self.close_time.strftime('%d')+" "+self.close_time.strftime('%B')+" "+self.close_time.strftime('%Y')
class Meta:
......@@ -47,6 +47,11 @@ class Application(models.Model):
unique_together = (("student", "vacancy"),)
class ReasonRejected(models.Model):
application = models.ForeignKey(Application, on_delete=models.CASCADE, primary_key=True)
reason = models.TextField(default='Tidak memenuhi kualifikasi perusahaan.')
class VacancyMilestone(models.Model):
vacancy = models.ForeignKey(Vacancy, on_delete=models.CASCADE, related_name="milestones", null=False)
name = models.CharField(max_length=100, null=False)
......
from rest_framework import serializers
from core.models import Company
from core.models.vacancies import Vacancy, Application, VacancyMilestone
from core.models.vacancies import Vacancy, Application, VacancyMilestone, ReasonRejected
from core.serializers.accounts import StudentSerializer, CompanySerializer
......@@ -65,6 +65,13 @@ class ApplicationStatusSerializer(serializers.ModelSerializer):
model = Application
fields = ['status']
class ReasonRejectedSerializer(serializers.ModelSerializer):
application = ApplicationSerializer()
class Meta:
model = ReasonRejected
fields = '__all__'
class SupervisorStudentApplicationSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
status_map = ["new", "read", "bookmarked", "rejected", "accepted","aborted" ]
......
......@@ -14,7 +14,9 @@ from core.serializers.vacancies import VacancySerializer
class ApplicationTests(APITestCase):
@requests_mock.Mocker()
def test_application_list(self, m):
m.get('https://akun.cs.ui.ac.id/oauth/token/verify/?client_id=X3zNkFmepkdA47ASNMDZRX3Z9gqSU1Lwywu5WepG', json={"username": 'dummy.mahasiswa', "role": 'mahasiswa', "identity_number": '1234567890'}, status_code=200)
m.get('https://akun.cs.ui.ac.id/oauth/token/verify/?client_id=X3zNkFmepkdA47ASNMDZRX3Z9gqSU1Lwywu5WepG',
json={"username": 'dummy.mahasiswa', "role": 'mahasiswa', "identity_number": '1234567890'},
status_code=200)
m.post('https://api.cs.ui.ac.id/authentication/ldap/v2/', json={
"username": "dummy.mahasiswa",
"nama": "Dummy Mahasiswa",
......@@ -23,14 +25,16 @@ class ApplicationTests(APITestCase):
"kodeidentitas": "1234567890",
"nama_role": "mahasiswa"
}, status_code=200)
m.get('https://api.cs.ui.ac.id/siakngcs/mahasiswa/1234567890?client_id=X3zNkFmepkdA47ASNMDZRX3Z9gqSU1Lwywu5WepG', json={
"kota_lahir": "kota_kota",
"tgl_lahir": "2017-12-31",
"program": [{
"nm_org": "Ilmu Informasi",
"angkatan": "2017"
}]
}, status_code=200)
m.get(
'https://api.cs.ui.ac.id/siakngcs/mahasiswa/1234567890?client_id=X3zNkFmepkdA47ASNMDZRX3Z9gqSU1Lwywu5WepG',
json={
"kota_lahir": "kota_kota",
"tgl_lahir": "2017-12-31",
"program": [{
"nm_org": "Ilmu Informasi",
"angkatan": "2017"
}]
}, status_code=200)
url = '/api/login/'
......@@ -76,7 +80,9 @@ class ApplicationTests(APITestCase):
@requests_mock.Mocker()
def test_application_create_and_delete(self, m):
m.get('https://akun.cs.ui.ac.id/oauth/token/verify/?client_id=X3zNkFmepkdA47ASNMDZRX3Z9gqSU1Lwywu5WepG', json={"username": 'dummy.mahasiswa', "role": 'mahasiswa', "identity_number": '1234567890'}, status_code=200)
m.get('https://akun.cs.ui.ac.id/oauth/token/verify/?client_id=X3zNkFmepkdA47ASNMDZRX3Z9gqSU1Lwywu5WepG',
json={"username": 'dummy.mahasiswa', "role": 'mahasiswa', "identity_number": '1234567890'},
status_code=200)
m.post('https://api.cs.ui.ac.id/authentication/ldap/v2/', json={
"username": "dummy.mahasiswa",
"nama": "Dummy Mahasiswa",
......@@ -85,14 +91,16 @@ class ApplicationTests(APITestCase):
"kodeidentitas": "1234567890",
"nama_role": "mahasiswa"
}, status_code=200)
m.get('https://api.cs.ui.ac.id/siakngcs/mahasiswa/1234567890?client_id=X3zNkFmepkdA47ASNMDZRX3Z9gqSU1Lwywu5WepG', json={
"kota_lahir": "kota_kota",
"tgl_lahir": "2017-12-31",
"program": [{
"nm_org": "Ilmu Informasi",
"angkatan": "2017"
}]
}, status_code=200)
m.get(
'https://api.cs.ui.ac.id/siakngcs/mahasiswa/1234567890?client_id=X3zNkFmepkdA47ASNMDZRX3Z9gqSU1Lwywu5WepG',
json={
"kota_lahir": "kota_kota",
"tgl_lahir": "2017-12-31",
"program": [{
"nm_org": "Ilmu Informasi",
"angkatan": "2017"
}]
}, status_code=200)
url = '/api/login/'
......@@ -197,7 +205,9 @@ class ApplicationTests(APITestCase):
class BookmarkApplicationTests(APITestCase):
@requests_mock.Mocker()
def test_application_list(self, m):
m.get('https://akun.cs.ui.ac.id/oauth/token/verify/?client_id=X3zNkFmepkdA47ASNMDZRX3Z9gqSU1Lwywu5WepG', json={"username": 'dummy.mahasiswa', "role": 'mahasiswa', "identity_number": '1234567890'}, status_code=200)
m.get('https://akun.cs.ui.ac.id/oauth/token/verify/?client_id=X3zNkFmepkdA47ASNMDZRX3Z9gqSU1Lwywu5WepG',
json={"username": 'dummy.mahasiswa', "role": 'mahasiswa', "identity_number": '1234567890'},
status_code=200)
m.post('https://api.cs.ui.ac.id/authentication/ldap/v2/', json={
"username": "dummy.mahasiswa",
"nama": "Dummy Mahasiswa",
......@@ -206,14 +216,16 @@ class BookmarkApplicationTests(APITestCase):
"kodeidentitas": "1234567890",
"nama_role": "mahasiswa"
}, status_code=200)
m.get('https://api.cs.ui.ac.id/siakngcs/mahasiswa/1234567890?client_id=X3zNkFmepkdA47ASNMDZRX3Z9gqSU1Lwywu5WepG', json={
"kota_lahir": "kota_kota",
"tgl_lahir": "2017-12-31",
"program": [{
"nm_org": "Ilmu Informasi",
"angkatan": "2017"
}]
}, status_code=200)
m.get(
'https://api.cs.ui.ac.id/siakngcs/mahasiswa/1234567890?client_id=X3zNkFmepkdA47ASNMDZRX3Z9gqSU1Lwywu5WepG',
json={
"kota_lahir": "kota_kota",
"tgl_lahir": "2017-12-31",
"program": [{
"nm_org": "Ilmu Informasi",
"angkatan": "2017"
}]
}, status_code=200)
url = '/api/login/'
......@@ -264,7 +276,9 @@ class BookmarkApplicationTests(APITestCase):
@requests_mock.Mocker()
def test_application_create_and_delete(self, m):
m.get('https://akun.cs.ui.ac.id/oauth/token/verify/?client_id=X3zNkFmepkdA47ASNMDZRX3Z9gqSU1Lwywu5WepG', json={"username": 'dummy.mahasiswa', "role": 'mahasiswa', "identity_number": '1234567890'}, status_code=200)
m.get('https://akun.cs.ui.ac.id/oauth/token/verify/?client_id=X3zNkFmepkdA47ASNMDZRX3Z9gqSU1Lwywu5WepG',
json={"username": 'dummy.mahasiswa', "role": 'mahasiswa', "identity_number": '1234567890'},
status_code=200)
m.post('https://api.cs.ui.ac.id/authentication/ldap/v2/', json={
"username": "dummy.mahasiswa",
"nama": "Dummy Mahasiswa",
......@@ -273,14 +287,16 @@ class BookmarkApplicationTests(APITestCase):
"kodeidentitas": "1234567890",
"nama_role": "mahasiswa"
}, status_code=200)
m.get('https://api.cs.ui.ac.id/siakngcs/mahasiswa/1234567890?client_id=X3zNkFmepkdA47ASNMDZRX3Z9gqSU1Lwywu5WepG', json={
"kota_lahir": "kota_kota",
"tgl_lahir": "2017-12-31",
"program": [{
"nm_org": "Ilmu Informasi",
"angkatan": "2017"
}]
}, status_code=200)
m.get(
'https://api.cs.ui.ac.id/siakngcs/mahasiswa/1234567890?client_id=X3zNkFmepkdA47ASNMDZRX3Z9gqSU1Lwywu5WepG',
json={
"kota_lahir": "kota_kota",
"tgl_lahir": "2017-12-31",
"program": [{
"nm_org": "Ilmu Informasi",
"angkatan": "2017"
}]
}, status_code=200)
url = '/api/login/'
......@@ -675,7 +691,8 @@ class CompanyListsTests(APITestCase):
def test_company_application_list_with_major(self):
new_user = User.objects.create_user('dummy.company4', 'dummy.company4@company.com', 'lalala123')
new_company = Company.objects.create(user=new_user, description="lalala", status=Company.VERIFIED, logo=None, address=None)
new_company = Company.objects.create(user=new_user, description="lalala", status=Company.VERIFIED, logo=None,
address=None)
self.client.force_authenticate(new_user)
......@@ -683,6 +700,7 @@ class CompanyListsTests(APITestCase):
response = self.client.get(url, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
class SupervisorStudentApplicationTests(APITestCase):
def test_list_student_application(self):
new_user = User.objects.create_user('dummy.supervisor', 'dummy.supervisor@asd.asd', 'lalala123')
......@@ -983,7 +1001,7 @@ class AcceptOneOfferTests(APITestCase):
new_user2 = User.objects.create_user('dummy.company2', 'dummy.company2@company.com', 'lalala123')
new_company2 = Company.objects.create(user=new_user2, description="lalala", status=Company.VERIFIED, logo=None,
address=None)
address=None)
new_vacancy = Vacancy.objects.create(company=new_company, verified=True, open_time=datetime.fromtimestamp(0),
description="lalala", requirements= "requirements", close_time=datetime.today())
......@@ -1021,7 +1039,7 @@ class AcceptOneOfferTests(APITestCase):
self.assertTrue('aborted' in status_response)
def test_student_not_exist_given_auth(self):
new_user3,new_vacancy, new_vacancy2, new_student = self.generateObject()
new_user3, new_vacancy, new_vacancy2, new_student = self.generateObject()
self.client.force_authenticate(new_user3)
......
......@@ -16,7 +16,7 @@ from core.lib.mixins import MultiSerializerViewSetMixin
from core.lib.permissions import IsAdminOrStudent, IsAdminOrCompany, IsAdminOrVacancyOwner, AsAdminOrSupervisor, \
VacancyApprovalPermission, IsAdminOrVacancyOwnerOrAuthenticatedReadOnly
from core.models import Student, Company
from core.models.vacancies import Vacancy, Application, VacancyMilestone
from core.models.vacancies import Vacancy, Application, VacancyMilestone, ReasonRejected
from core.serializers.vacancies import VacancySerializer, ApplicationSerializer, ApplicationStatusSerializer, \
PostVacancySerializer, VacancyVerifiedSerializer, SupervisorStudentApplicationSerializer, \
VacancyMilestoneSerializer
......@@ -186,6 +186,8 @@ class ApplicationViewSet(MultiSerializerViewSetMixin, viewsets.GenericViewSet):
serializer = self.get_serializer_class()(application, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
if request.data['status'] == 3:
ReasonRejected.objects.create(application=application, reason=request.data['reason'])
return Response(serializer.data, status=status.HTTP_202_ACCEPTED)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
......@@ -261,12 +263,15 @@ class StudentApplicationViewSet(viewsets.GenericViewSet):
application.delete()
return Response(ApplicationSerializer(application, context={'request': request}).data)
class StatusError(Exception):
pass
class UnauthorizeError(Exception):
pass
class CompanyApplicationViewSet(viewsets.GenericViewSet):
queryset = Application.objects.all()
permission_classes = [IsAdminOrCompany]
......@@ -295,7 +300,7 @@ class CompanyApplicationViewSet(viewsets.GenericViewSet):
return Response({"error": "forbidden"}, status=status.HTTP_403_FORBIDDEN)
except (StatusError, ValueError):
return Response({"error": "status must be an integer between 0 and 4"}, \
status=status.HTTP_400_BAD_REQUEST)
status=status.HTTP_400_BAD_REQUEST)
@detail_route(methods=["get"])
def by_vacancy(self, request, company_id, pk=None):
......@@ -323,7 +328,7 @@ class CompanyApplicationViewSet(viewsets.GenericViewSet):
return Response({"error": "forbidden"}, status=status.HTTP_403_FORBIDDEN)
except (StatusError, ValueError):
return Response({"error": "status must be an integer between 0 and 4"}, \
status=status.HTTP_400_BAD_REQUEST)
status=status.HTTP_400_BAD_REQUEST)
def __get_company_list_by_company_id(self, request, company_id):
company = get_object_or_404(Company.objects.all().order_by('-updated'), pk=company_id)
......@@ -335,7 +340,7 @@ class CompanyApplicationViewSet(viewsets.GenericViewSet):
return request.user.is_superuser or request.user == company.user
def __get_vacancy_list_by_pk(self, pk, company):
vacancy = get_object_or_404(Vacancy.objects.all(), pk=pk)
vacancy = get_object_or_404(Vacancy.objects.all(), pk=pk)
if not self.__validating_vacancy(vacancy, company):
raise UnauthorizeError
return vacancy
......@@ -361,6 +366,7 @@ class CompanyApplicationViewSet(viewsets.GenericViewSet):
raise StatusError
return True
class CompanyVacanciesViewSet(viewsets.GenericViewSet):
queryset = Vacancy.objects.all()
pagination_class = PageNumberPagination
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment