Fakultas Ilmu Komputer UI

Commit 5a1097bd authored by Zamil Majdy's avatar Zamil Majdy
Browse files

Merge from StudentTranscript

parents ff4d6c37 9c6a4670
......@@ -163,3 +163,21 @@ card .formRegis{
color: black;
}
.biodata h5{
line-height: 30%;
}
.button-profile{
margin-top:30px;
}
.ui.segment.profile-form{
padding-bottom: 37px;
margin-top:4%;
}
.profilePage{
margin-bottom:40px;
margin-left:5%;
margin-right:5%;
}
\ No newline at end of file
import React from 'react';
import { Segment, Image, Header, Icon, Container, Button, Form } from 'semantic-ui-react';
import Server from './lib/Server';
export default class ProfilePage extends React.Component {
static propTypes = {
route: React.PropTypes.object.isRequired,
params: React.PropTypes.object.isRequired,
};
constructor(props) {
super(props);
/* istanbul ignore next */
this.state = {
id: '',
npm: '',
name: '',
major: '',
batch: '',
email: '',
cityOfBirth: '',
dateOfBirth: '',
resume: '',
phone: '',
showTranscript: '',
};
this.getProfile = this.getProfile.bind(this);
this.handleChange = this.handleChange.bind(this);
this.getProfile();
}
getProfile() {
if (this.props.route.own) {
// check api format in /api#!/login
this.state = {
id: this.props.route.data.student.id,
npm: this.props.route.data.student.npm,
name: this.props.route.data.student.name,
major: this.props.route.data.student.major,
batch: this.props.route.data.student.batch,
email: this.props.route.data.student.user.email,
cityOfBirth: this.props.route.data.student.birth_place,
dateOfBirth: this.props.route.data.student.birth_date,
resume: this.props.route.data.student.resume,
phone: this.props.route.data.student.phone_number,
showTranscript: this.props.route.data.student.show_transcript,
};
return Promise.resolve(this.state);
} else {
return Server.get(`/students/${this.props.params.id}/`).then((data) => {
this.setState({
id: data.id,
name: data.name,
npm: data.npm,
resume: data.resume,
major: data.major,
batch: data.batch,
email: data.user.email,
cityOfBirth: data.birth_place,
dateOfBirth: data.birth_date,
phone: data.phone_number,
showTranscript: data.show_transcript,
});
}, error => error.then(() => {
// this.modalAlert.open('Gagal Mengambil ', r.error);
this.state.name = 'Gagal mendapatkan informasi';
}));
}
}
handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
};
updateForm(show) {
if (show) {
return (
<Segment className="profile-form">
<Header as="h3" textAlign="center">
<Icon name="edit" />
<Header.Content>
Edit Profile Page
</Header.Content>
</Header>
<Form size="small" onSubmit={this.handleSubmit}>
<Form.Field>
<label htmlFor="photo">Profile Picture</label>
<input onChange={this.handleChange} placeholder="Profile Photo.jpg" name="photo" type="File" />
</Form.Field>
<Form.Field>
<label htmlFor="email">Email</label>
<input onChange={this.handleChange} placeholder="jojon@email.com" name="email" />
</Form.Field>
<Form.Field>
<label htmlFor="phone">No. Hp</label>
<input onChange={this.handleChange} placeholder="08123456789" name="phone" />
</Form.Field>
<Form.Field>
<label htmlFor="resume">Resume</label>
<input onChange={this.handleChange} placeholder="Resume" name="resume" type="File" />
</Form.Field>
<Button type="submit" size="small" primary floated="right">Submit</Button>
</Form>
</Segment>
);
}
return (<div />);
}
render() {
return (
<div className="profilePage">
<Segment className="biodata-section">
<Header as="h2" icon textAlign="center">
<Image src="http://semantic-ui.com/images/wireframe/square-image.png" size="small" shape="circular" />
</Header>
<Container textAlign="center" className="profile-biodata">
<div className="biodata">
<h3> { this.state.name } </h3>
<h5> { this.state.major }, { this.state.batch } </h5>
<h5> { this.state.email } </h5>
<h5> { this.state.phone } </h5>
<h5> { this.state.cityOfBirth}, { this.state.dateOfBirth } </h5>
</div>
<div className="button-profile">
<Button primary size="small">Resume</Button>
{ this.state.showTranscript ? <Button primary size="small">Transkrip</Button> : <div /> }
</div>
</Container>
</Segment >
{ this.updateForm(this.props.route.own) }
</div>
);
}
}
import React from 'react';
import CourseList from './components/CourseList';
export default class TranscriptPage extends React.Component {
constructor(props) {
super(props);
/* istanbul ignore next */
this.state = {
data: {"transcript":[{
"url": "http://api.cs.ui.ac.id/siakngcs/riwayat-mahasiswa/99731/",
"npm": "1406622616",
"kelas": {
"url": "http://api.cs.ui.ac.id/siakngcs/kelas/473569/",
"kd_kls": "473569",
"nm_kls": "MD 1 - B",
"nm_mk_cl": {
"url": "http://api.cs.ui.ac.id/siakngcs/matakuliah/817/",
"kd_mk": "CSF1600100",
"nm_mk": "Matematika Diskret 1",
"kd_org": "06.00.12.01",
"kd_kur": "06.00.12.01-2012",
"jml_sks": 3
},
"kd_kur_cl": "06.00.12.01-2012",
"kd_mk_cl": "CSF1600100",
"periode": {
"url": "http://api.cs.ui.ac.id/siakngcs/periode/25/",
"term": 1,
"tahun": 2014
},
"pengajar": [
{
"url":
"http://api.cs.ui.ac.id/siakngcs/dosen/196111251992031001/",
"nomor": "196111251992031001",
"nama": "Prof. Drs. T. Basaruddin M.Sc., Ph.D",
"id_skema": 1,
"nm_skema": "Skema Inti",
"maks_sks": 8
}
]
},
"kd_kls": "473569",
"kd_kur": "06.00.12.01-2012",
"kd_mk": "CSF1600100",
"kd_org": "01.00.12.01",
"term": 1,
"tahun": 2014,
"nilai": "A"
}],"name":"Si jagoan neon" }};//ambil dari database
}
render() {
return (
<CourseList data={this.state.data.transcript} name={this.state.data.name}/>
);
}
}
/* eslint-disable no-unused-expressions */
import React from 'react';
import ReactTestUtils from 'react-addons-test-utils';
import fetchMock from 'fetch-mock';
import ProfilePage from '../ProfilePage';
describe('ProfilePage', () => {
const studentSession = {
url: 'http://localhost:8000/api/users/9/',
username: 'muhammad.reza42',
email: 'muhammad.reza42@ui.ac.id',
is_staff: false,
company: null,
supervisor: null,
student: {
id: 3,
user: {
url: 'http://localhost:8000/api/users/9/',
username: 'muhammad.reza42',
email: 'muhammad.reza42@ui.ac.id',
is_staff: false,
},
name: 'Muhammad R.',
created: '2017-03-28T13:33:46.147241Z',
updated: '2017-03-28T13:33:46.148248Z',
npm: 1406543593,
resume: null,
phone_number: null,
birth_place: null,
birth_date: null,
major: null,
batch: null,
show_resume: false,
bookmarked_vacancies: [
3,
2,
],
applied_vacancies: [
3,
1,
],
},
};
const brokenSession = {
url: 'http://localhost:8000/api/users/9/',
username: 'muhammad.reza42',
email: 'muhammad.reza42@ui.ac.id',
is_staff: false,
company: null,
supervisor: null,
student: null,
};
const response = {
id: 3,
name: 'Muhammad R.',
user: {
url: 'http://localhost:8000/api/users/9/',
username: 'muhammad.reza42',
email: 'muhammad.reza42@ui.ac.id',
is_staff: false,
},
npm: 1406543593,
resume: null,
phone_number: null,
birth_place: null,
birth_date: null,
major: null,
batch: null,
show_transcript: false,
};
it('renders without problem', () => {
const profile = ReactTestUtils.renderIntoDocument(
<ProfilePage route={{ own: true, data: studentSession }} params={{}} />);
expect(profile.state.name).to.equal(studentSession.student.name);
});
it('get profile for company without problem', () => {
fetchMock.get('*', response);
const profile = ReactTestUtils.renderIntoDocument(
<ProfilePage route={{ own: false, data: studentSession }} params={{ id: 3 }} />);
profile.getProfile().then(()=> expect(profile.state.name).to.equal(response.name));
fetchMock.restore();
});
it('renders without problem when error getting data', () => {
fetchMock.get('*', 400);
const profile = ReactTestUtils.renderIntoDocument(
<ProfilePage route={{ own: false, data: studentSession }} params={{ id: 3 }} />);
profile.getProfile().then(()=> expect(profile.state.name).to.equal('Gagal mendapatkan informasi'));
});
});
import React from 'react';
import { Table } from 'semantic-ui-react';
export default class Course extends React.Component {
static propTypes = {
courseName: React.PropTypes.string.isRequired,
grade: React.PropTypes.string.isRequired,
};
render() {
return (
<Table.Row>
<Table.Cell>{this.props.courseName}</Table.Cell>
<Table.Cell>{this.props.grade}</Table.Cell>
</Table.Row>
);
}
}
import React from 'react';
import { Grid, Segment, Table } from 'semantic-ui-react';
import Course from './Course';
export default class CourseList extends React.Component {
static propTypes = {
data: React.PropTypes.array.isRequired,
name: React.PropTypes.array.isRequired,
};
constructor(props) {
super(props);
/* istanbul ignore next */
this.state = { course: this.props.data };
}
generateCourse() {
return this.state.course.map((course, index) =>
<Course
key={index}
courseName={course.kelas.nm_kls}
grade={course.nilai}
/>,
);
}
render = () => (
<Grid.Column centered>
<Segment>
<h2>Nama : {this.props.name}</h2>
<Table unstackable>
<Table.Header>
<Table.Row>
<Table.HeaderCell>Mata Kuliah</Table.HeaderCell>
<Table.HeaderCell>Nilai</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{ this.generateCourse() }
</Table.Body>
</Table>
</Segment>
</Grid.Column>
);
}
......@@ -79,8 +79,6 @@ export default class App extends React.Component {
};
render() {
// const student = this.authorization(['admin', 'student']);
// const supervisor = this.authorization(['admin', 'supervisor']);
const company = this.authorization(['admin', 'company']);
const commonUser = this.authorization(['admin', 'student', 'company']);
......
......@@ -18,6 +18,10 @@ def is_admin_or_supervisor(user):
return user.is_superuser or hasattr(user, "supervisor")
def is_admin_or_supervisor_or_company(user):
return user.is_superuser or hasattr(user, "supervisor") or hasattr(user, "company")
class IsAdminOrSelfOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
......@@ -96,6 +100,30 @@ class IsAdminOrCompany(permissions.BasePermission):
return hasattr(user, "company") and user.company == company
class IsAdminOrSupervisorOrCompany(permissions.BasePermission):
def has_permission(self, request, view):
return is_admin_or_supervisor_or_company(request.user)
class IsAdminOrSupervisorOrCompanyOrSelf(permissions.IsAuthenticated):
def has_object_permission(self, request, view, obj):
user = request.user
if user.is_superuser or hasattr(user, "company") or hasattr(user, "supervisor"):
return True
if hasattr(user, "student"):
if isinstance(obj, Student):
student = obj
elif hasattr(obj, "student"):
student = obj.student
else:
raise APIException(
"Checking student permission on object {} not associated with Student"
.format(type(obj.__name__))
)
return hasattr(user, "student") and user.student == student
return False
class IsAdminOrVacancyOwner(permissions.BasePermission):
def has_permission(self, request, view):
return is_admin_or_company(request.user)
......
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-04-24 07:20
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0006_auto_20170328_1950'),
]
operations = [
migrations.AddField(
model_name='student',
name='batch',
field=models.CharField(blank=True, max_length=4, null=True),
),
migrations.AddField(
model_name='student',
name='birth_date',
field=models.DateField(blank=True, null=True),
),
migrations.AddField(
model_name='student',
name='birth_place',
field=models.TextField(blank=True, max_length=100, null=True),
),
migrations.AddField(
model_name='student',
name='major',
field=models.CharField(blank=True, max_length=100, null=True),
),
migrations.AddField(
model_name='student',
name='show_resume',
field=models.BooleanField(default=False),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-04-24 07:25
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0007_auto_20170424_0720'),
]
operations = [
migrations.AlterField(
model_name='student',
name='birth_place',
field=models.CharField(blank=True, max_length=30, null=True),
),
migrations.AlterField(
model_name='student',
name='major',
field=models.CharField(blank=True, max_length=30, null=True),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-04-24 09:09
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0008_auto_20170424_0725'),
]
operations = [
migrations.RenameField(
model_name='student',
old_name='show_resume',
new_name='show_transcript',
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-04-24 13:34
from __future__ import unicode_literals
import core.models.accounts
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0009_auto_20170424_0909'),
]
operations = [
migrations.AddField(
model_name='student',
name='photo',
field=models.FileField(blank=True, null=True, upload_to=core.models.accounts.get_student_photo_file_path),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-04-25 22:14
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0010_student_photo'),
('core', '0007_auto_20170425_1550'),
]
operations = [
]
......@@ -12,6 +12,12 @@ def get_student_resume_file_path(instance, filename):
return os.path.join("student-resume/", filename)
def get_student_photo_file_path(instance, filename):
extension = filename.split('.')[-1].lower()
filename = "%s.%s" % (uuid.uuid4(), extension)
return os.path.join("student-photo/", filename)
def get_company_logo_file_path(instance, filename):
extension = filename.split('.')[-1].lower()
filename = "%s.%s" % (uuid.uuid4(), extension)
......@@ -51,11 +57,21 @@ class Student(models.Model):
bookmarked_vacancies = models.ManyToManyField('core.Vacancy', related_name="bookmarked_vacancies", blank=True)
applied_vacancies = models.ManyToManyField('core.Vacancy', related_name="applied_vacancies",
blank=True, through='core.Application')
birth_place = models.CharField(max_length=30, blank=True, null=True)
birth_date = models.DateField(blank=True, null=True)
major = models.CharField(max_length=30, blank=True, null=True)
batch = models.CharField(max_length=4, blank=True, null=True)
show_transcript = models.BooleanField(default=False)
photo = models.FileField(upload_to=get_student_photo_file_path, null=True, blank=True)
@property
def name(self):