Fakultas Ilmu Komputer UI

Commit d846a0a6 authored by Nabila Fakhirah's avatar Nabila Fakhirah
Browse files

Fixed merge conflict to merge with master

parents 2f41ddcb 1682b6a7
Pipeline #22008 passed with stages
in 25 minutes and 14 seconds
File added
# Code owners file
## Changes to these file(s) require approval from the teaching team
sonar-project.properties @addianto @hafiyyan94
\ No newline at end of file
......@@ -2,6 +2,13 @@
> Internship matchmaking platform for students and companies.
[![pipeline status](https://gitlab.cs.ui.ac.id/pmpl/class-project/kape/badges/master/pipeline.svg)](https://gitlab.cs.ui.ac.id/pmpl/class-project/kape/commits/master)
[![coverage report](https://gitlab.cs.ui.ac.id/pmpl/class-project/kape/badges/master/coverage.svg)](https://gitlab.cs.ui.ac.id/pmpl/class-project/kape/commits/master)
[![Quality Gate Status](https://pmpl.cs.ui.ac.id/sonarqube/api/project_badges/measure?project=id.ac.ui.cs.foss%3Akape&metric=alert_status)](https://pmpl.cs.ui.ac.id/sonarqube/dashboard?id=id.ac.ui.cs.foss%3Akape)
[![Bugs](https://pmpl.cs.ui.ac.id/sonarqube/api/project_badges/measure?project=id.ac.ui.cs.foss%3Akape&metric=bugs)](https://pmpl.cs.ui.ac.id/sonarqube/dashboard?id=id.ac.ui.cs.foss%3Akape)
[![Code Smells](https://pmpl.cs.ui.ac.id/sonarqube/api/project_badges/measure?project=id.ac.ui.cs.foss%3Akape&metric=code_smells)](https://pmpl.cs.ui.ac.id/sonarqube/dashboard?id=id.ac.ui.cs.foss%3Akape)
## Table of Contents
- [Install](#install)
......
......@@ -21,7 +21,7 @@ export default class CompanyProfile extends React.Component {
<div className="biodataCompany">
<h2>{ data.name }</h2>
<h3>{ data.address }t</h3>
<p>{ data.description }</p>
<p>{ data.category } - { data.description }</p>
</div>
</Container>
</Segment>
......
......@@ -23,23 +23,28 @@ export default class ProfilePage extends React.Component {
major: '',
batch: '',
email: '',
region: '',
cityOfBirth: '',
dateOfBirth: '',
resume: '',
phone_number: '',
show_transcript: '',
photo: '',
intro: '',
form: {
picture: '',
email: '',
phone_number: '',
region: '',
resume: '',
show_transcript: '',
intro: '',
},
bagikanTranskrip: '',
acceptedNo: 0,
refresh: 1,
loading: false,
linkedin_url: '',
self_description: '',
};
this.getProfile = this.getProfile.bind(this);
......@@ -64,6 +69,7 @@ export default class ProfilePage extends React.Component {
major: data.major,
batch: data.batch,
email: data.user.email,
region: data.region,
cityOfBirth: data.birth_place,
dateOfBirth: data.birth_date,
phone_number: data.phone_number,
......@@ -72,6 +78,8 @@ export default class ProfilePage extends React.Component {
acceptedNo: data.accepted_no,
bagikanTranskrip: data.show_transcript,
refresh: this.state.refresh + 1,
intro: data.intro,
linkedin_url: data.linkedin_url,
self_description: data.self_description,
});
if (this.props.route.own) {
......@@ -158,12 +166,25 @@ export default class ProfilePage extends React.Component {
</Form.Field>
<Form.Field>
<label htmlFor="self_description">Deskripsi Diri</label>
<input onChange={this.handleChange} placeholder="Saya senang belajar" name="self_description" />
<input onChange={this.handleChange} placeholder="Saya suka belajar" name="self_description" />
</Form.Field>
<Form.Field>
<label htmlFor="linkedin_url">URL Profile LinkedIn</label>
<input onChange={this.handleChange} placeholder="https://www.linkedin.com/in/jojo/" name="linkedin_url" />
</Form.Field>
<Form.Field>
<label htmlFor="region">Region</label>
<input onChange={this.handleChange} placeholder="Indonesia" name="region" />
</Form.Field>
<Form.Field>
<label htmlFor="resume">Resume</label>
<input onChange={this.handleFile} placeholder="Resume" name="resume" type="File" />
</Form.Field>
<Form.Field>
<label htmlFor="intro">Intro</label>
<input onChange={this.handleChange} placeholder="Ceritakan dirimu secara singkat" name="intro" />
</Form.Field>
<Form.Field>
<Checkbox
onChange={this.handleCheckbox}
......@@ -256,8 +277,44 @@ export default class ProfilePage extends React.Component {
</Grid.Column>
</Grid>
</Segment>
<Segment basic vertical>
<Grid>
<Grid.Column width={2}>
<Icon name="linkedin" size="big" />
</Grid.Column>
<Grid.Column width={13}>
<a href={this.state.linkedin_url}> { this.state.linkedin_url || 'N/A' } </a>
</Grid.Column>
</Grid>
</Segment>
</div>
<Segment basic vertical>
<Grid>
<Grid.Column width={2}>
<Icon name="map pin" size="big" />
</Grid.Column>
<Grid.Column width={13}>
<p> { this.state.region || 'N/A' } </p>
</Grid.Column>
</Grid>
</Segment>
<Segment basic vertical>
<Grid>
<Grid.Column width={2}>
<h3>Intro</h3>
</Grid.Column>
<Grid.Column width={13}>
<p> { this.state.intro || 'N/A' } </p>
</Grid.Column>
</Grid>
</Segment>
<Container textAlign="center">
<div className="buttonProfile">
<Button onClick={this.gotoStudentResume} disabled={!this.state.resume} primary size="small">Resume</Button>
......
......@@ -7,6 +7,7 @@ import ApplicationList from './components/ApplicationList';
const cols = [
{ key: 'StudentName', label: 'Nama' },
{ key: 'StudentID', label: 'NPM' },
{ key: 'Major', label: 'Major' },
{ key: 'Perusahaan', label: 'Perusahaan' },
{ key: 'Posisi', label: 'Posisi' },
{ key: 'Status', label: 'Status' },
......
......@@ -24,6 +24,7 @@ const companyUser = {
verified: true,
logo: 'http://localhost:8001/files/company-logo/8a258a48-3bce-4873-b5d1-538b360d0059.png',
address: 'Jl. Kebayoran Baru nomor 13, Jakarta Barat',
category: 'Belum ada kategori perusahaan',
},
supervisor: null,
student: null,
......
......@@ -139,7 +139,7 @@ describe('ProfilePage', () => {
const profile = ReactTestUtils.renderIntoDocument(
<ProfilePage route={{ own: true, data: studentSession }} user={{ data: studentSession }} params={{ id: 3 }} />);
const checkboxNode = ReactTestUtils.scryRenderedDOMComponentsWithTag(profile, 'Input')[5];
const checkboxNode = ReactTestUtils.scryRenderedDOMComponentsWithTag(profile, 'Input')[7];
const checkbox = false;
checkboxNode.value = checkbox;
profile.getProfile().then(()=> expect(profile.state.show_transcript).to.equal(true));
......@@ -213,4 +213,4 @@ describe('ProfilePage', () => {
});
});
});
\ No newline at end of file
import React from 'react';
import ReactTestUtils from 'react-addons-test-utils';
import CompanyRegisterModal from '../../components/CompanyRegisterModal';
describe('CompanyRegisterModal', () => {
function validatePassword(password) {
const lowerCaseLetters = /[a-z]/g;
const upperCaseLetters = /[A-Z]/g;
const numbers = /[0-9]/g;
if(password.length < 8) return "Password less than 8"; else
if(!lowerCaseLetters.test(password)) return "Password at least one lowercase letter"; else
if(!upperCaseLetters.test(password)) return "Password at least one uppercase letter"; else
if(!numbers.test(password)) return "Password at least one number";
else return true
}
it('renders without problem', () => {
const companyRegister = ReactTestUtils.renderIntoDocument(
<CompanyRegisterModal />);
expect(companyRegister).to.exist;
});
it('handle password validation', () => {
const password = '3s24Aasd';
expect(validatePassword(password)).to.equal(true);
});
});
......@@ -23,9 +23,10 @@ export default class ApplicationList extends React.Component {
generateRows() {
return this.props.items.map(item =>
(
<Table.Row key={`${item.npm}_${item.company}_${item.position}_${item.status}_row`}>
<Table.Row key={`${item.npm}_${item.major}_${item.company}_${item.position}_${item.status}_row`}>
<Table.Cell key={`${item.name}_name`}> {item.name} </Table.Cell>
<Table.Cell key={`${item.name}_npm`}> {item.npm} </Table.Cell>
<Table.Cell key={`${item.name}_major`}> {item.major} </Table.Cell>
<Table.Cell key={`${item.name}_company`}>
{item.company_name}
</Table.Cell>
......
import React from 'react';
import { browserHistory } from 'react-router';
import { Modal, Button, Form, Input, TextArea, Header, Icon } from 'semantic-ui-react';
import { Modal, Button, Form, Input, TextArea, Header, Icon, Dropdown } from 'semantic-ui-react';
import ModalAlert from './../components/ModalAlert';
import Server from './../lib/Server';
import Storage from './../lib/Storage';
......@@ -17,6 +17,26 @@ export default class CompanyRegisterModal extends React.Component {
this.handleSubmit = this.handleSubmit.bind(this);
}
categoryOptions = [
{ value: 'Perusahaan Asuransi', text: 'Perusahaan Asuransi' },
{ value: 'Perusahaan Bioteknologi', text: 'Perusahaan Bioteknologi' },
{ value: 'Perusahaan Elektronik', text: 'Perusahaan Elektronik' },
{ value: 'Perusahaan Energi', text: 'Perusahaan Energi' },
{ value: 'Perusahaan Farmasi', text: 'Perusahaan Farmasi' },
{ value: 'Firma Akuntansi', text: 'Firma Akuntansi' },
{ value: 'Agen Periklanan', text: 'Agen Periklanan' },
{ value: 'Perusahaan Internet', text: 'Perusahaan Internet' },
{ value: 'Perusahaan Jasa', text: 'Perusahaan Jasa' },
{ value: 'Perusahaan Komputer', text: 'Perusahaan Komputer' },
{ value: 'Perusahaan Konsultansi', text: 'Perusahaan Konsultansi' },
{ value: 'Perusahaan Logistik', text: 'Perusahaan Logistik' },
{ value: 'Perusahaan Media', text: 'Perusahaan Media' },
{ value: 'Perusahaan Perangkat Keras', text: 'Perusahaan Perangkat Keras' },
{ value: 'Perusahaan Perangkat Lunak', text: 'Perusahaan Perangkat Lunak' },
{ value: 'Perusahaan Teknologi', text: 'Perusahaan Teknologi' },
{ value: 'Perusahaan Telekomunikasi', text: 'Perusahaan Telekomunikasi' },
]
componentWillUpdate() {
this.fixBody();
}
......@@ -34,6 +54,10 @@ export default class CompanyRegisterModal extends React.Component {
this.setState({ [e.target.name]: e.target.value });
};
handleSelectChange = (e, data) => {
this.setState({ [data.name]: data.value });
}
handleFile = (e) => {
this.setState({ [e.target.name]: e.target.files[0] });
};
......@@ -42,6 +66,9 @@ export default class CompanyRegisterModal extends React.Component {
handlePassword = (e) => {
if (e.target.name === 'password') this.passwordField = e.target; else
if (e.target.name === 'password-confirm') this.passwordConfirmField = e.target;
const validatePassword = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}/g;
if(!validatePassword.test(this.passwordField.value)) this.passwordField.setCustomValidity("Must contain at least one number and one uppercase and lowercase letter, and at least 8 or more characters");
else this.passwordField.setCustomValidity("");
const isExist = this.passwordField && this.passwordConfirmField;
if (isExist) {
if (this.passwordField.value !== this.passwordConfirmField.value) {
......@@ -99,6 +126,19 @@ export default class CompanyRegisterModal extends React.Component {
<label htmlFor="name">Nama Perusahaan</label>
<Input onChange={this.handleChange} placeholder="Nama Perusahaan" name="name" required />
</Form.Field>
<Form.Field required>
<label htmlFor="name">Kategori Perusahaan</label>
<Dropdown
placeholder='Kategori Perusahaan'
fluid
name="category"
onChange={this.handleSelectChange}
search
selection
options={this.categoryOptions}
/>
</Form.Field>
<Form.Field required></Form.Field>
<Form.Field required>
<label htmlFor="logo">Logo</label>
<Input
......
from django.contrib import admin
from core.models.accounts import Company, Supervisor, Student
from core.models.feedbacks import Feedback
from core.models.vacancies import Vacancy
admin.site.register(Company)
admin.site.register(Student)
admin.site.register(Supervisor)
admin.site.register(Vacancy)
admin.site.register(Feedback)
import os
import math
from django.core.exceptions import ValidationError
from kape.settings import MAX_UPLOAD_SIZE
......@@ -18,3 +19,29 @@ def validate_document_file_extension(value):
def validate_image_file_extension(value):
validate_file(value, ['.jpeg', '.jpg', '.png', '.JPG', '.JPEG'])
def validate_npm(value):
'''
NPM UI terdiri dari 10 digit, misalnya: 1234567894. Digit terakhir
(disebut checksum) dapat dihitung dari sembilan digit pertama.
Checksum dipakai untuk deteksi error pada nomor ID seperti NPM.
Digit-digit tersebut diberi indeks: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
sesuai posisinya. Kita asosiasikan digit-digit tersebut dengan
variabel-variabel d[0], d[1], d[2] , ..., d[9].
Checksum C (yaitu d[9]) dihitung dengan rumus sebagai berikut:
1) a = 3* (d[0] + d[2] + d[4] + d[6] + d[8])
2) b = (d[1] + d[3] + d[5] + d[7])
3) C = (a + b) % 7
Sebagai contoh, checksum untuk NPM 1234567894 adalah
C = (3*(1 + 3 + 5 + 7 + 9) + (2 + 4 + 6 + 8)) % 7 = 95 % 7 = 4
yang memang ternyata benar.
Source: Lim Yohanes Stefanus, Drs., M.Math., Ph.D
'''
if math.ceil(math.log(value+1, 10)) != 10:
raise ValidationError(u"NPM must be 10 digits")
val_string = str(value)
if sum([3*int(a) for a in val_string[:-1:2]]+[int(a) for a in val_string[1:-1:2]]) % 7 != int(val_string[-1]):
raise ValidationError(u"NPM {} has invalid checksum".format(value))
\ No newline at end of file
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2019-10-04 06:40
from __future__ import unicode_literals
import core.lib.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0013_auto_20170602_1130'),
]
operations = [
migrations.AlterField(
model_name='student',
name='npm',
field=models.IntegerField(unique=True, validators=[core.lib.validators.validate_npm]),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2019-10-05 04:52
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0013_auto_20170602_1130'),
]
operations = [
migrations.AddField(
model_name='company',
name='category',
field=models.CharField(default=b'Belum ada kategori perusahaan', max_length=140),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2019-10-03 13:39
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0013_auto_20170602_1130'),
]
operations = [
migrations.CreateModel(
name='Feedback',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True)),
('title', models.CharField(blank=True, default=b'', max_length=100)),
('content', models.TextField()),
],
options={
'ordering': ['created'],
},
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2019-10-05 08:48
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0013_auto_20170602_1130'),
]
operations = [
migrations.AddField(
model_name='student',
name='linkedin_url',
field=models.URLField(blank=True, null=True),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.17 on 2019-10-05 10:07
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0013_auto_20170602_1130'),
]
operations = [
migrations.AddField(
model_name='student',
name='region',
field=models.CharField(blank=True, max_length=30, null=True),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2019-10-05 12:26
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0014_feedback'),
('core', '0014_company_category'),
]
operations = [
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.17 on 2019-10-05 12:57
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0014_feedback'),
('core', '0014_company_category'),
('core', '0014_student_region'),
]
operations = [
]
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