Fakultas Ilmu Komputer UI

Commit 4fc4fbf7 authored by sirinbaisa's avatar sirinbaisa
Browse files

Merge branch 'UserStory2' of https://gitlab.com/PPL2017csui/PPLA1 into UserStory2

parents 1f7f29ef 6ad142f1
......@@ -10,7 +10,7 @@ export default class companyRegister extends React.Component {
<div className="registerForm">
<Card fluid >
<Card.Content>
<Form>
<form encType="multipart/form-data" action="">
<Header as='h2' icon textAlign='center'>
<Icon name='signup' circular />
<Header.Content>
......@@ -36,7 +36,7 @@ export default class companyRegister extends React.Component {
</Form.Field>
<Form.Field required>
<label>Logo</label>
<Input
<Input type="File"
icon={{ name: 'attach', circular: true, link: true }}
placeholder='attach logo'
required
......@@ -60,7 +60,7 @@ export default class companyRegister extends React.Component {
<Input placeholder='Provinsi' required />
</Form.Field >
<Button type='submit' floated='right' color='blue'>Submit</Button>
</Form>
</form>
</Card.Content>
</Card>
</div>
......
import React from 'react';
import Tabs from './components/Tabs';
import Pane from './components/Pane';
import Storage from './lib/Storage';
import VacancyList from './components/VacancyList';
import Server from './lib/Server';
export default class VacancyPage extends React.Component {
......@@ -20,28 +20,20 @@ export default class VacancyPage extends React.Component {
handleItemClick = (e, { name }) => this.setState({ activeItem: name });
// fetchVacancies(url) {
// console.log("maho");
// Server.get(url).then((data) => {
// this.setState({ vacancies: data });
// }, () => {
// });
// }
render() {
const data = [{ data1: 'Software Enggineer' }, { data1: 'System Analyst' }];
const student = Storage.get('user-data').student;
return (
<div className="halamanLowongan">
<Tabs selected={0}>
<Pane label="Semua Lowongan" >
<VacancyList url="/vacancies/" />
<VacancyList key={1} url="/vacancies/" />
</Pane>
<Pane label="Lamaran saya" >
<VacancyList url="/b" />
<VacancyList key={2} url={`/students/${student.id}/applied-vacancies/`} />
</Pane>
<Pane label="Lamaran Ditandai" >
<VacancyList url="/c" />
<VacancyList key={3} url={`/students/${student.id}/bookmarked-vacancies/`} />
</Pane>
</Tabs>
......
......@@ -25,8 +25,8 @@ describe('LoginForm', () => {
it('handle email input without problem', () => {
const formLogin = ReactTestUtils.renderIntoDocument(
<LoginForm url="" />);
expect(formLogin.state.email).to.equal('');
<LoginForm type="sso-ui" />);
expect(formLogin.state.username).to.equal('');
const emailNode = ReactTestUtils.scryRenderedDOMComponentsWithTag(formLogin, 'Input')[0];
// const emailNode = ReactDOM.findDOMNode(formLogin.refs.email);
const email = 'jojon';
......@@ -34,12 +34,12 @@ describe('LoginForm', () => {
ReactTestUtils.Simulate.change(emailNode, { target: { value: email } });
expect(emailNode.value).to.equal(email);
expect(formLogin.state.email).to.equal(email);
expect(formLogin.state.username).to.equal(email);
});
it('handle password input without problem', () => {
const formLogin = ReactTestUtils.renderIntoDocument(
<LoginForm url="" />);
<LoginForm type="sso-ui" />);
const passwordNode = ReactTestUtils.scryRenderedDOMComponentsWithTag(formLogin, 'Input')[1];
// const passwordNode = ReactDOM.findDOMNode(formLogin.refs.password);
......
import React from 'react';
import { Modal, Button, Icon } from 'semantic-ui-react';
import Server from '../lib/Server';
import Storage from '../lib/Storage';
export default class ModalAlert extends React.Component {
static propTypes = {
......@@ -24,17 +25,20 @@ export default class ModalAlert extends React.Component {
close = () => this.setState({ open: false });
handleOpen() {
const requestData = { coverLetter: this.props.coverLetter };
let studentId = Storage.post('user-data.student.id');
Server.post(`/students/${studentId}/application`, requestData).then((data) => {
const studentId = Storage.get('user-data').student.id;
const requestData = {vacancy_id: this.props.id, cover_letter: this.props.coverLetter };
Server.post(`/students/${studentId}/applied-vacancies/`, requestData).then((data) => {
this.setState({
header: 'Pendaftaran Berhasil',
content: this.successResponse + JSON.stringify(data),
content: this.successResponse,
});
}, (error) => {
this.setState({
responseHeader: 'Pendaftaran Gagal',
responseText: this.failedResponse + JSON.stringify(error),
header: 'Pendaftaran Gagal',
content: this.failedResponse,
});
});
}
......
......@@ -64,7 +64,7 @@ export default class ModalPendaftaran extends React.Component {
<h5> Write your Cover Letter </h5>
<Form >
<TextArea placeholder="Tell us more" size='big' />
<TextArea placeholder="Tell us more" size='big' onChange={this.handleChange}/>
</Form>
</div>
......
import React from 'react';
import { Button, Image as ImageComponent, Item, Rating } from 'semantic-ui-react'
import { Button, Image as ImageComponent, Item, Rating } from 'semantic-ui-react';
import ModalPendaftaran from './ModalPendaftaran';
import Storage from '../lib/Storage';
import Server from '../lib/Server';
const defaultImage = "http://semantic-ui.com/images/wireframe/image.png";
const defaultImage = 'http://semantic-ui.com/images/wireframe/image.png';
export default class Lowongan extends React.Component {
static propTypes = {
data: React.PropTypes.object.isRequired,
};
constructor(props) {
super(props);
/* istanbul ignore next */
this.bookmark = this.bookmark.bind(this);
}
bookmark() {
const studentId = Storage.get('user-data').student.id;
const data = { vacancy_id: this.props.data.id };
Server.post(`/students/${studentId}/bookmarked-vacancies/`, data).then((response) => {
console.log(JSON.stringify(response));
}, (error) => {
console.log(JSON.stringify(error));
});
}
render() {
return (
<Item >
<Item.Image size="small" src={this.props.data.company.logo ? this.props.data.company.logo: defaultImage} />
<Item.Image size="small" src={this.props.data.company.logo ? this.props.data.company.logo : defaultImage} />
<Item.Content verticalAlign="middle">
<Item.Extra>
<h3>{ this.props.data.name }</h3>
<div className="bookmark">
<Rating icon='star' size='massive' defaultRating={0} maxRating={1} />
<Rating icon="star" onRate={this.bookmark} size="massive" defaultRating={0} maxRating={1} />
</div>
<h4>{ this.props.data.company.name }</h4>
<h5>{ this.props.data.company.address }</h5>
<ModalPendaftaran
id={this.props.data.id} data={{ header: this.props.data.name,
description: this.props.data.description }} buttonTitle="Daftar" />
description: this.props.data.description }} buttonTitle="Daftar"
/>
</Item.Extra>
</Item.Content>
</Item>
);
}
}
\ No newline at end of file
}
......@@ -11,27 +11,25 @@ export default class VacancyList extends React.Component {
constructor(props) {
super(props);
/* istanbul ignore next */
this.state = { vacancies: [] };
Server.get(this.props.url, false).then((data) => {
this.setState({ vacancies: data });
}, () => {
});
/* istanbul ignore next */
this.state = { vacancies: [] };
this.generateVacancies = this.generateVacancies.bind(this);
}
generateVacancies() {
return this.state.vacancies.map((vacancy, index) =>
return this.state.vacancies.map(vacancy =>
<Lowongan key={vacancy.id} data={vacancy} />,
);
}
render = () => {
return (
<Item.Group relaxed>
{ this.generateVacancies() }
</Item.Group>
);
}
render = () => (
<Item.Group relaxed>
{ this.generateVacancies() }
</Item.Group>
);
}
\ No newline at end of file
......@@ -46,3 +46,10 @@ class LoginSerializer(serializers.HyperlinkedModelSerializer):
model = User
fields = ('url', 'username', 'email', 'is_staff', 'company', 'supervisor', 'student')
class RegisterSerializer(serializers.HyperlinkedModelSerializer):
company = CompanySerializer()
class Meta:
model = User
fields = ('url', 'username', 'email', 'is_staff', 'company')
from rest_framework import serializers
from core.serializers.accounts import CompanySerializer, StudentSerializer
from core.models.vacancies import Vacancy
from core.models.vacancies import Vacancy, Application
class VacancySerializer(serializers.ModelSerializer):
......@@ -16,7 +16,7 @@ class ApplicationSerializer(serializers.ModelSerializer):
student = StudentSerializer()
class Meta:
model = Vacancy
model = Application
fields = '__all__'
......
# __init__.py
from core.tests.test_accounts import LoginTests
from core.tests.test_accounts import LoginTests, RegisterTests
from core.tests.test_vacancies import ApplicationTests, BookmarkApplicationTests
import requests_mock
from rest_framework import status
from rest_framework.test import APITestCase
from rest_framework.test import APIClient, APITestCase
from django.contrib.auth.models import User
from core.models.accounts import Company
from core.models.accounts import Company, Supervisor
class LoginTests(APITestCase):
@requests_mock.Mocker()
def test_succesful_student_login_relogin(self, m):
"""
Ensure we can login
"""
m.post('https://api.cs.ui.ac.id/authentication/ldap/v2/', json={
"username": "dummy.mahasiswa",
......@@ -64,13 +61,35 @@ class LoginTests(APITestCase):
def test_success_company_login(self):
new_user = User.objects.create_user('dummy.login.company', 'dummy.login.company@company.com', 'lalala123')
new_company = Company.objects.create(user=new_user, description="lalalala", verified=True, logo=None, alamat=None)
new_company = Company.objects.create(user=new_user, description="lalalala", verified=True, logo=None, address=None)
url = '/api/login/'
response = self.client.post(url, {'username': 'dummy.login.company', 'password': 'lalala123', 'login-type': 'company'}, format='json')
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_bad_request(self):
def test_bad_request_1(self):
url = '/api/login/'
response = self.client.post(url, {'uesrname': 'lalala'}, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
\ No newline at end of file
response = self.client.post(url, {'username': 'lalala'}, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_bad_request_2(self):
url = '/api/login/'
response = self.client.post(url, {'username': 'lalala', 'password': 'lalalala', 'login-type' : 'lalala'}, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
class RegisterTests(APITestCase):
def test_create_and_recreate(self):
url = '/api/register/'
tc_post = {'username': 'dummy.company', 'password': 'corporatepass', 'name':'tutuplapak', 'description':'menutup lapak', 'email': 'email@email.com'}
response = self.client.post(url, tc_post, format='multipart')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
response = self.client.post(url, tc_post, format='multipart')
self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)
def test_bad_request(self):
url = '/api/register/'
response = self.client.post(url, {'username': 'lalala'}, format='multipart')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
......@@ -48,7 +48,7 @@ class ApplicationTests(APITestCase):
student_id = response.data.get('student').get('id')
new_user = User.objects.create_user('dummy.company', 'dummy.company@company.com', 'lalala123')
new_company = Company.objects.create(user=new_user, description="lalala",verified=True,logo=None,alamat=None)
new_company = Company.objects.create(user=new_user, description="lalala",verified=True,logo=None,address=None)
new_vacancy = Vacancy.objects.create(company=new_company, verified=True, open_time=datetime.fromtimestamp(0), description="lalala", close_time=datetime.today())
url = '/api/students/' + str(student_id) + '/applications/'
......@@ -101,7 +101,7 @@ class BookmarkApplicationTests(APITestCase):
student_id = response.data.get('student').get('id')
new_user = User.objects.create_user('dummy.company', 'dummy.company@company.com', 'lalala123')
new_company = Company.objects.create(user=new_user, description="lalala",verified=True,logo=None,alamat=None)
new_company = Company.objects.create(user=new_user, description="lalala",verified=True,logo=None,address=None)
new_vacancy = Vacancy.objects.create(company=new_company, verified=True, open_time=datetime.fromtimestamp(0), description="lalala", close_time=datetime.today())
url = '/api/students/' + str(student_id) + '/bookmarked-vacancies/'
......
......@@ -6,11 +6,12 @@ from rest_framework.decorators import list_route
from rest_framework.permissions import AllowAny
from rest_framework.permissions import IsAdminUser, IsAuthenticated
from rest_framework.response import Response
from rest_framework.parsers import FormParser,MultiPartParser
from core.lib.permissions import IsAdminOrStudent, IsAdminOrSelfOrReadOnly, IsAdminOrCompany, IsAdminOrSupervisor
from core.models.accounts import Student, Company, Supervisor
from core.serializers.accounts import UserSerializer, StudentSerializer, CompanySerializer, SupervisorSerializer, \
LoginSerializer
LoginSerializer, RegisterSerializer
class UserViewSet(viewsets.ModelViewSet):
......@@ -20,6 +21,9 @@ class UserViewSet(viewsets.ModelViewSet):
@list_route(methods=['get'], permission_classes=[IsAuthenticated])
def me(self, request):
"""
Get current user's details
"""
user = self.request.user
serializer = UserSerializer(user, context={"request": request})
return Response(serializer.data)
......@@ -46,23 +50,13 @@ class StudentViewSet(viewsets.ModelViewSet):
class CompanyViewSet(viewsets.ModelViewSet):
queryset = Company.objects.all()
serializer_class = CompanySerializer
permission_classes = [IsAdminUser]
def get_permissions(self):
if self.action == "update":
return [IsAdminOrSelfOrReadOnly(), IsAdminOrCompany()]
return super(CompanyViewSet, self).get_permissions()
permission_classes = [IsAdminOrSelfOrReadOnly, IsAdminOrCompany]
class SupervisorViewSet(viewsets.ModelViewSet):
queryset = Supervisor.objects.all()
serializer_class = SupervisorSerializer
permission_classes = [IsAdminUser]
def get_permissions(self):
if self.action == "update":
return [IsAdminOrSelfOrReadOnly(), IsAdminOrSupervisor()]
return super(SupervisorViewSet, self).get_permissions()
permission_classes = [IsAdminOrSelfOrReadOnly, IsAdminOrSupervisor]
class LoginViewSet(viewsets.GenericViewSet):
......@@ -72,14 +66,14 @@ class LoginViewSet(viewsets.GenericViewSet):
def create(self, request):
"""
Authenticate user by logging in
Authentication for user by means of logging in
---
parameters:
- name: body
description: JSON object containing three strings: username, password and login-type. login-type should be either 'sso-ui' or 'company'.
required: true
paramType: body
pytype: RequestSerializer
- name: body
description: JSON object containing three strings: username, password and login-type. login-type should be either 'sso-ui' or 'company'.
required: true
type: string
paramType: body
"""
username = request.data.get('username')
password = request.data.get('password')
......@@ -136,3 +130,75 @@ class LoginViewSet(viewsets.GenericViewSet):
return Response(status=status.HTTP_401_UNAUTHORIZED)
else:
return Response(status=status.HTTP_400_BAD_REQUEST)
class CompanyRegisterViewSet(viewsets.GenericViewSet):
permission_classes = (AllowAny,)
serializer_class = RegisterSerializer
queryset = Company.objects.all()
parser_classes = (MultiPartParser, FormParser,)
def create(self, request):
"""
Create a new company user
---
parameters:
- name: username
description: username of the new account
required: true
type: string
- name: password
description: password of the new acoount
required: true
type: string
- name: email
description: email address of the new acoount
required: true
type: string
- name: name
description: the new company's name
required: true
type: string
- name: description
description: description of the new company
required: true
type: string
- name: logo
description: logo of the new company
required: false
type: image
- name: address
description: address of the new acoount
required: false
type: string
"""
username = request.data.get('username')
password = request.data.get('password')
email = request.data.get('email')
company_name = request.data.get('name')
description = request.data.get('description')
logo = request.data.get('logo')
address = request.data.get('address')
if username is not None and password is not None and email is not None and company_name is not None and description is not None:
user, created = User.objects.get_or_create(
username=username,
email=email,
first_name=company_name,
last_name=""
)
if created:
user.set_password(password)
company = Company.objects.create(
user=user,
description=description,
logo=logo,
address=address
)
user.save()
company.save()
serializer = RegisterSerializer(user, context={'request': request})
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(status=status.HTTP_409_CONFLICT)
return Response(status=status.HTTP_400_BAD_REQUEST)
\ No newline at end of file
from rest_framework import viewsets
from rest_framework.exceptions import ValidationError
from rest_framework.generics import get_object_or_404
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
......@@ -21,26 +22,47 @@ class VacancyViewSet(viewsets.ModelViewSet):
class ApplicationViewSet(viewsets.GenericViewSet):
serializer_class = VacancySerializer
serializer_class = ApplicationSerializer
permission_classes = [IsAdminOrStudent]
def list(self, request, student_id):
"""
Get list of a student {student_id}'s application
---
"""
student = get_object_or_404(Student.objects.all(), pk=student_id)
vacancies = self.serializer_class(student.applied_vacancies, many=True, context={'request': request})
return Response(vacancies.data)
vacancies = student.applied_vacancies
return Response(VacancySerializer(vacancies, many=True, context={'request': request}).data)
def create(self, request, student_id):
"""
Create a new application for student {student_id}
---
parameters:
- name: body
description: JSON object containing an integer 'vacancy_id' and a string 'cover_letter'
required: true
type: string
paramType: body
"""
cover_letter = request.data.get('cover_letter')
vacancy = get_object_or_404(Vacancy.objects.all(), pk=request.data.get('vacancy_id'))
student = get_object_or_404(Student.objects.all(), pk=student_id)
if Application.objects.filter(vacancy=vacancy, student=student).exists():
raise ValidationError("You have already applied for the vacancy")
application = Application(vacancy=vacancy, student=student, cover_letter=cover_letter)
application.save()
return Response(ApplicationSerializer(application, context={'request': request}).data)
def destroy(self, request, student_id, pk):
"""
Remove a 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)
application.delete()
return Response(ApplicationSerializer(application, context={'request': request}).data)
......@@ -49,17 +71,35 @@ class BookmarkedVacancyByStudentViewSet(viewsets.GenericViewSet):
permission_classes = [IsAdminOrStudent]
def list(self, request, student_id):
"""
Get list of a student {student_id}'s bookmarked vacancies
---
"""
student = get_object_or_404(Student.objects.all(), pk=student_id)
vacancies = self.serializer_class(student.bookmarked_vacancies, many=True, context={'request': request})
return Response(vacancies.data)
def create(self, request, student_id):
"""
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
"""
vacancy = get_object_or_404(Vacancy.objects.all(), pk=request.data['vacancy_id'])
student = get_object_or_404(Student.objects.all(), pk=student_id)
student.bookmarked_vacancies.add(vacancy)
return Response(self.serializer_class(student.bookmarked_vacancies, many=True, context={'request': request}).data)
def destroy(self, request, student_id, pk):
"""
Remove bookmark {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)
student.bookmarked_vacancies.remove(vacancy)
......