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
PMPL
Class Project
Kape
Commits
e9608ee6
Commit
e9608ee6
authored
Oct 08, 2019
by
Ilham Darmawan Candra Purnama
Browse files
Fitur Pencarian untuk Lowongan, Daftar Lamaran, dan Bookmark
parent
08286292
Changes
9
Hide whitespace changes
Inline
Side-by-side
assets/css/custom.css
View file @
e9608ee6
...
...
@@ -289,4 +289,27 @@ card .formRegis{
.ui.segment.kop
{
line-height
:
5px
;
}
.search-container
{
width
:
fit-content
;
margin
:
auto
;
}
.search-form
{
border
:
none
;
border-bottom
:
solid
#0d5aa7
2px
;
text-decoration
:
none
;
outline
:
none
;
font-size
:
14pt
;
}
.search-button
{
background-color
:
#0d5aa7
;
color
:
white
;
border
:
none
;
padding
:
10px
;
font-size
:
14pt
;
border-radius
:
5px
;
margin-left
:
10px
;
}
\ No newline at end of file
assets/js/components/Pagination.jsx
View file @
e9608ee6
...
...
@@ -2,6 +2,7 @@ import React from 'react';
import
{
Menu
,
Container
,
Icon
,
Loader
}
from
'
semantic-ui-react
'
;
import
Server
from
'
../lib/Server
'
;
import
ModalAlert
from
'
../components/ModalAlert
'
;
import
Pane
from
"
./Pane
"
;
export
default
class
Pagination
extends
React
.
Component
{
...
...
@@ -29,38 +30,85 @@ export default class Pagination extends React.Component {
dir
:
0
,
start
:
true
,
finish
:
false
,
search
:
''
,
};
this
.
handleNext
=
this
.
handleNext
.
bind
(
this
);
this
.
handlePrev
=
this
.
handlePrev
.
bind
(
this
);
this
.
getItemsData
=
this
.
getItemsData
.
bind
(
this
);
this
.
handleMovement
=
this
.
handleMovement
.
bind
(
this
);
this
.
handleSearchChange
=
this
.
handleSearchChange
.
bind
(
this
);
this
.
handleSearchSubmit
=
this
.
handleSearchSubmit
.
bind
(
this
);
this
.
refresh
=
this
.
refresh
.
bind
(
this
);
this
.
content
=
this
.
content
.
bind
(
this
);
this
.
pageMenu
=
this
.
pageMenu
.
bind
(
this
);
this
.
getItemsData
();
}
getItemsData
=
()
=>
Server
.
get
(
this
.
state
.
url
,
false
).
then
((
data
)
=>
{
this
.
setState
({
current
:
this
.
state
.
current
+
this
.
state
.
dir
});
this
.
setState
(
{
items
:
data
.
results
,
next
:
`
${
this
.
props
.
url
}
?page=
${
this
.
state
.
current
+
1
}
`
,
prev
:
`
${
this
.
props
.
url
}
?page=
${
this
.
state
.
current
-
1
}
`
,
loading
:
false
,
getItemsData
=
()
=>
{
const
prefix
=
this
.
state
.
dir
===
0
?
'
?
'
:
'
&
'
;
const
searchPrefix
=
this
.
state
.
search
!==
''
?
prefix
:
''
;
const
searchValue
=
this
.
state
.
search
!==
''
?
`search=
${
this
.
state
.
search
}
`
:
''
;
return
Server
.
get
(
`
${
this
.
state
.
url
}${
searchPrefix
}${
searchValue
}
`
,
false
).
then
((
data
)
=>
{
console
.
log
(
'
GET ITEM DATA
'
);
this
.
setState
({
current
:
this
.
state
.
current
+
this
.
state
.
dir
,
});
this
.
setState
(
{
items
:
data
.
results
,
next
:
`
${
this
.
props
.
url
}
?page=
${
this
.
state
.
current
+
1
}
`
,
prev
:
`
${
this
.
props
.
url
}
?page=
${
this
.
state
.
current
-
1
}
`
,
loading
:
false
,
});
let
first
=
true
;
let
last
=
true
;
if
(
data
.
previous
)
{
first
=
false
;
}
if
(
data
.
next
)
{
last
=
false
;
}
this
.
setState
({
first
,
last
});
},
error
=>
error
.
then
((
r
)
=>
{
this
.
modalAlert
.
open
(
this
.
props
.
error
,
r
.
detail
);
this
.
setState
({
loading
:
false
});
}));
};
handleSearchChange
(
e
)
{
this
.
setState
({
search
:
e
.
target
.
value
});
if
(
e
.
target
.
value
===
''
)
{
this
.
setState
({
items
:
[],
current
:
1
,
next
:
''
,
prev
:
''
,
url
:
this
.
props
.
url
,
loading
:
true
,
dir
:
0
,
start
:
true
,
finish
:
false
,
},
function
()
{
this
.
getItemsData
();
});
let
first
=
true
;
let
last
=
true
;
if
(
data
.
previous
)
{
first
=
false
;
}
if
(
data
.
next
)
{
last
=
false
;
}
this
.
setState
({
first
,
last
});
},
error
=>
error
.
then
((
r
)
=>
{
this
.
modalAlert
.
open
(
this
.
props
.
error
,
r
.
detail
);
this
.
setState
({
loading
:
false
});
}));
}
handleSearchSubmit
()
{
this
.
setState
({
items
:
[],
current
:
1
,
next
:
''
,
prev
:
''
,
url
:
this
.
props
.
url
,
loading
:
true
,
dir
:
0
,
start
:
true
,
finish
:
false
,
},
function
()
{
this
.
getItemsData
();
});
}
refresh
()
{
this
.
forceUpdate
();
...
...
@@ -112,6 +160,10 @@ export default class Pagination extends React.Component {
render
=
()
=>
(
<
div
>
<
Loader
active
=
{
this
.
state
.
loading
}
/>
<
div
className
=
"search-container"
>
<
input
type
=
"text"
placeholder
=
"Cari Lowongan"
onChange
=
{
this
.
handleSearchChange
}
className
=
"search-form"
/>
<
button
type
=
"submit"
onClick
=
{
this
.
handleSearchSubmit
}
className
=
"search-button"
>
Cari
</
button
>
</
div
>
<
ModalAlert
ref
=
{
(
modal
)
=>
{
this
.
modalAlert
=
modal
;
}
}
/>
{
!
this
.
state
.
loading
&&
this
.
content
()
}
{
!
this
.
state
.
loading
&&
this
.
pageMenu
()
}
...
...
core/migrations/0020_merge_20191007_0648.py
0 → 100644
View file @
e9608ee6
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2019-10-06 23:48
from
__future__
import
unicode_literals
from
django.db
import
migrations
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'core'
,
'0017_vacancy_amount'
),
(
'core'
,
'0019_merge_20191006_0852'
),
(
'core'
,
'0014_auto_20191004_1340'
),
]
operations
=
[
]
core/migrations/0021_auto_20191007_0648.py
0 → 100644
View file @
e9608ee6
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2019-10-06 23:48
from
__future__
import
unicode_literals
import
django.core.validators
from
django.db
import
migrations
,
models
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'core'
,
'0020_merge_20191007_0648'
),
]
operations
=
[
migrations
.
AlterField
(
model_name
=
'company'
,
name
=
'website'
,
field
=
models
.
CharField
(
default
=
b
'Belum ada link website'
,
max_length
=
100
),
),
migrations
.
AlterField
(
model_name
=
'student'
,
name
=
'phone_number'
,
field
=
models
.
CharField
(
blank
=
True
,
db_index
=
True
,
max_length
=
12
,
null
=
True
,
validators
=
[
django
.
core
.
validators
.
RegexValidator
(
b
'^0
\\
d{1,11}$'
)]),
),
]
core/migrations/0025_merge_20191007_1810.py
0 → 100644
View file @
e9608ee6
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2019-10-07 11:10
from
__future__
import
unicode_literals
from
django.db
import
migrations
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'core'
,
'0021_auto_20191007_0648'
),
(
'core'
,
'0024_auto_20191007_1533'
),
]
operations
=
[
]
core/migrations/0026_merge_20191008_0525.py
0 → 100644
View file @
e9608ee6
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2019-10-07 22:25
from
__future__
import
unicode_literals
from
django.db
import
migrations
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'core'
,
'0025_merge_20191007_1810'
),
(
'core'
,
'0025_merge_20191007_2124'
),
(
'core'
,
'0025_merge_20191008_0048'
),
]
operations
=
[
]
core/migrations/0027_merge_20191008_0652.py
0 → 100644
View file @
e9608ee6
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2019-10-07 23:52
from
__future__
import
unicode_literals
from
django.db
import
migrations
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'core'
,
'0026_merge_20191008_0525'
),
(
'core'
,
'0026_merge_20191008_0256'
),
]
operations
=
[
]
core/tests/test_vacancies.py
View file @
e9608ee6
...
...
@@ -43,6 +43,37 @@ class ApplicationTests(APITestCase):
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_200_OK
)
@
requests_mock
.
Mocker
()
def
test_application_search
(
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
.
post
(
'https://api.cs.ui.ac.id/authentication/ldap/v2/'
,
json
=
{
"username"
:
"dummy.mahasiswa"
,
"nama"
:
"Dummy Mahasiswa"
,
"state"
:
1
,
"kode_org"
:
"01.00.12.01:mahasiswa"
,
"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
)
url
=
'/api/login/'
response
=
self
.
client
.
post
(
url
,
{
'username'
:
'dummy.mahasiswa'
,
'password'
:
'lalala'
,
'login-type'
:
'sso-ui'
},
format
=
'json'
)
student_id
=
response
.
data
.
get
(
'student'
).
get
(
'id'
)
url
=
'/api/students/'
+
str
(
student_id
)
+
'/applied-vacancies/?search=engineer'
response
=
self
.
client
.
get
(
url
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_200_OK
)
@
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
)
...
...
@@ -152,6 +183,42 @@ class BookmarkApplicationTests(APITestCase):
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_200_OK
)
@
requests_mock
.
Mocker
()
def
test_search_bookmarked_application
(
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
.
post
(
'https://api.cs.ui.ac.id/authentication/ldap/v2/'
,
json
=
{
"username"
:
"dummy.mahasiswa"
,
"nama"
:
"Dummy Mahasiswa"
,
"state"
:
1
,
"kode_org"
:
"01.00.12.01:mahasiswa"
,
"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
)
url
=
'/api/login/'
response
=
self
.
client
.
post
(
url
,
{
'username'
:
'dummy.mahasiswa'
,
'password'
:
'lalala'
,
'login-type'
:
'sso-ui'
},
format
=
'json'
)
student_id
=
response
.
data
.
get
(
'student'
).
get
(
'id'
)
url
=
'/api/students/'
+
str
(
student_id
)
+
'/bookmarked-vacancies/?search=engineer'
response
=
self
.
client
.
get
(
url
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_200_OK
)
@
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
)
...
...
@@ -202,6 +269,44 @@ class VacancyTest(APITestCase):
response
=
self
.
client
.
get
(
url
,
format
=
'json'
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_200_OK
)
@
requests_mock
.
Mocker
()
def
test_search_vacancy
(
self
,
m
):
superuser
=
User
.
objects
.
create_user
(
'dummy.company'
,
'dummy.company@company.com'
,
'lalala123'
)
company
=
Company
.
objects
.
create
(
user
=
superuser
,
description
=
"This is a test company"
)
Vacancy
.
objects
.
create
(
company
=
company
,
open_time
=
datetime
.
now
(),
close_time
=
datetime
.
now
(),
name
=
"Software Engineer"
)
Vacancy
.
objects
.
create
(
company
=
company
,
open_time
=
datetime
.
now
(),
close_time
=
datetime
.
now
(),
name
=
"Data Engineer"
)
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"
,
"state"
:
1
,
"kode_org"
:
"01.00.12.01:mahasiswa"
,
"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
)
url
=
'/api/login/'
self
.
client
.
post
(
url
,
{
'username'
:
'dummy.mahasiswa'
,
'password'
:
'lalala'
,
'login-type'
:
'sso-ui'
},
format
=
'json'
)
url
=
'/api/vacancies/?search=software'
response
=
self
.
client
.
get
(
url
,
format
=
'json'
)
self
.
assertEqual
(
1
,
Company
.
objects
.
all
().
count
())
self
.
assertEqual
(
2
,
Vacancy
.
objects
.
all
().
count
())
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_200_OK
)
def
test_unverified_vacancy_list
(
self
):
superuser
=
User
.
objects
.
create_superuser
(
'dummy.company'
,
'dummy.company@company.com'
,
'lalala123'
)
self
.
client
.
force_authenticate
(
user
=
superuser
)
...
...
core/views/vacancies.py
View file @
e9608ee6
import
requests
from
django.utils
import
timezone
from
django.conf
import
settings
from
django.db.models
import
Q
from
rest_framework
import
viewsets
,
status
from
rest_framework.decorators
import
detail_route
,
permission_classes
from
rest_framework.exceptions
import
ValidationError
...
...
@@ -37,8 +38,12 @@ class VacancyViewSet(MultiSerializerViewSetMixin, viewsets.ModelViewSet):
return
super
(
VacancyViewSet
,
self
).
get_permissions
()
def
list
(
self
,
request
,
*
args
,
**
kwargs
):
vacancies
=
Vacancy
.
objects
.
all
()
verified
=
request
.
query_params
[
'verified'
]
if
'verified'
in
request
.
query_params
else
"True"
search
=
request
.
query_params
[
'search'
]
if
'search'
in
request
.
query_params
else
None
if
search
is
not
None
:
vacancies
=
Vacancy
.
objects
.
filter
(
Q
(
name__icontains
=
search
)
|
Q
(
company__user__username__icontains
=
search
))
else
:
vacancies
=
Vacancy
.
objects
.
all
()
companies
=
[
int
(
x
)
for
x
in
request
.
query_params
.
getlist
(
'company'
,
[])]
if
verified
.
lower
()
in
(
"yes"
,
"true"
,
"t"
,
"1"
):
vacancies
=
vacancies
.
filter
(
verified
=
True
)
...
...
@@ -57,6 +62,7 @@ class VacancyViewSet(MultiSerializerViewSetMixin, viewsets.ModelViewSet):
page
=
self
.
paginate_queryset
(
vacancies
)
if
page
is
not
None
:
return
self
.
get_paginated_response
(
VacancySerializer
(
page
,
many
=
True
,
context
=
{
'request'
:
request
}).
data
)
vacancies
=
Vacancy
.
objects
.
all
()
return
Response
(
VacancySerializer
(
vacancies
,
many
=
True
,
context
=
{
'request'
:
request
}).
data
)
def
name_position_validator
(
self
,
names
):
...
...
@@ -185,7 +191,11 @@ class StudentApplicationViewSet(viewsets.GenericViewSet):
"""
student
=
get_object_or_404
(
Student
.
objects
.
all
(),
pk
=
student_id
)
vacancy_ids
=
Application
.
objects
.
filter
(
student
=
student
).
values
(
'vacancy'
)
vacancies
=
Vacancy
.
objects
.
filter
(
id__in
=
vacancy_ids
)
search
=
request
.
query_params
[
'search'
]
if
'search'
in
request
.
query_params
else
None
if
search
is
not
None
:
vacancies
=
Vacancy
.
objects
.
filter
(
Q
(
id__in
=
vacancy_ids
)
&
(
Q
(
name__icontains
=
search
)
|
Q
(
company__user__username__icontains
=
search
)))
else
:
vacancies
=
Vacancy
.
objects
.
filter
(
id__in
=
vacancy_ids
)
page
=
self
.
paginate_queryset
(
vacancies
)
if
page
is
not
None
:
return
self
.
get_paginated_response
(
VacancySerializer
(
page
,
many
=
True
,
context
=
{
'request'
:
request
}).
data
)
...
...
@@ -355,7 +365,11 @@ class BookmarkedVacancyByStudentViewSet(viewsets.GenericViewSet):
---
"""
student
=
get_object_or_404
(
Student
.
objects
.
all
(),
pk
=
student_id
)
vacancies
=
student
.
bookmarked_vacancies
.
all
()
search
=
request
.
query_params
[
'search'
]
if
'search'
in
request
.
query_params
else
None
if
search
is
not
None
:
vacancies
=
student
.
bookmarked_vacancies
.
filter
(
Q
(
name__icontains
=
search
)
|
Q
(
company__user__username__icontains
=
search
))
else
:
vacancies
=
student
.
bookmarked_vacancies
.
all
()
page
=
self
.
paginate_queryset
(
vacancies
)
if
page
is
not
None
:
return
self
.
get_paginated_response
(
VacancySerializer
(
page
,
many
=
True
,
context
=
{
'request'
:
request
}).
data
)
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment