Fakultas Ilmu Komputer UI
Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Fasilkom UI Open Source Software
Kape
Commits
5a1097bd
Commit
5a1097bd
authored
Apr 26, 2017
by
Zamil Majdy
Browse files
Merge from StudentTranscript
parents
ff4d6c37
9c6a4670
Changes
21
Hide whitespace changes
Inline
Side-by-side
assets/css/custom.css
View file @
5a1097bd
...
...
@@ -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
assets/js/ProfilePage.jsx
0 → 100644
View file @
5a1097bd
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
>
);
}
}
assets/js/TranscriptPage.jsx
0 → 100644
View file @
5a1097bd
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
}
/>
);
}
}
assets/js/__test__/ProfilePage-test.jsx
0 → 100644
View file @
5a1097bd
/* 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
'
));
});
});
assets/js/components/Course.jsx
0 → 100644
View file @
5a1097bd
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
>
);
}
}
assets/js/components/CourseList.jsx
0 → 100644
View file @
5a1097bd
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
>
);
}
assets/js/index.jsx
View file @
5a1097bd
...
...
@@ -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
'
]);
...
...
core/lib/permissions.py
View file @
5a1097bd
...
...
@@ -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
)
...
...
core/migrations/0007_auto_20170424_0720.py
0 → 100644
View file @
5a1097bd
# -*- 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
),
),
]
core/migrations/0008_auto_20170424_0725.py
0 → 100644
View file @
5a1097bd
# -*- 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
),
),
]
core/migrations/0009_auto_20170424_0909.py
0 → 100644
View file @
5a1097bd
# -*- 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'
,
),
]
core/migrations/0010_student_photo.py
0 → 100644
View file @
5a1097bd
# -*- 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
),
),
]
core/migrations/0011_merge_20170425_2214.py
0 → 100644
View file @
5a1097bd
# -*- 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
=
[
]
core/models/accounts.py
View file @
5a1097bd
...
...
@@ -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
):