diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS new file mode 100644 index 0000000000000000000000000000000000000000..a69023229f36573edb56148d0e187d8e397ce110 --- /dev/null +++ b/.gitlab/CODEOWNERS @@ -0,0 +1,4 @@ +# 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 diff --git a/README.md b/README.md index 3020546098563953b463aa84d24bb2958f22f589..3734ba0a5f9c94947bcea0145748b6214280794a 100755 --- a/README.md +++ b/README.md @@ -2,6 +2,13 @@ > Internship matchmaking platform for students and companies. +[](https://gitlab.cs.ui.ac.id/pmpl/class-project/kape/commits/master) +[](https://gitlab.cs.ui.ac.id/pmpl/class-project/kape/commits/master) + +[](https://pmpl.cs.ui.ac.id/sonarqube/dashboard?id=id.ac.ui.cs.foss%3Akape) +[](https://pmpl.cs.ui.ac.id/sonarqube/dashboard?id=id.ac.ui.cs.foss%3Akape) +[](https://pmpl.cs.ui.ac.id/sonarqube/dashboard?id=id.ac.ui.cs.foss%3Akape) + ## Table of Contents - [Install](#install) diff --git a/assets/css/custom.css b/assets/css/custom.css index 9b7cf50a1356d41530baa447984f2fb2abfd873a..0e008c2673700930a05ff861dcf3123793183ff4 100755 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -289,4 +289,8 @@ card .formRegis{ .ui.segment.kop { line-height: 5px; +} + +.jumlahLamaran { + font-weight: bold; } \ No newline at end of file diff --git a/assets/js/ProfilePage.jsx b/assets/js/ProfilePage.jsx index a9c0fc9f29dbfc420d46ed7f1090672f04d5e391..697c6a6845f6cbcd22a3532c934dea691b29c491 100644 --- a/assets/js/ProfilePage.jsx +++ b/assets/js/ProfilePage.jsx @@ -38,6 +38,7 @@ export default class ProfilePage extends React.Component { }, bagikanTranskrip: '', acceptedNo: 0, + readNo: 0, refresh: 1, loading: false, }; @@ -69,6 +70,7 @@ export default class ProfilePage extends React.Component { photo: data.photo, show_transcript: data.show_transcript, acceptedNo: data.accepted_no, + readNo: data.read_no, bagikanTranskrip: data.show_transcript, refresh: this.state.refresh + 1, }); @@ -239,6 +241,16 @@ export default class ProfilePage extends React.Component { </Grid.Column> </Grid> </Segment> + + <Segment basic vertical> + <Grid> + <p className="jumlahLamaran"> Jumlah lamaran diterima: {this.state.acceptedNo || '0' }</p> + </Grid> + <Grid> + <p className="jumlahLamaran"> Jumlah lamaran dibaca perusahaan: {this.state.readNo || '0' }</p> + </Grid> + </Segment> + </div> <Container textAlign="center"> diff --git a/assets/js/__test__/ProfilePage-test.jsx b/assets/js/__test__/ProfilePage-test.jsx index a98b3452c0d522446eed60d064e2b328e2249c59..41e711d3b16ce6b80e58dd62b7f36e4a7367c260 100644 --- a/assets/js/__test__/ProfilePage-test.jsx +++ b/assets/js/__test__/ProfilePage-test.jsx @@ -212,5 +212,12 @@ describe('ProfilePage', () => { expect(profile.state.form.photo).to.equal('abc'); }); - + it('renders count application without problem', () => { + fetchMock.get('*', response); + const profile = ReactTestUtils.renderIntoDocument( + <ProfilePage route={{ own: true, data: studentSession }} user={{ data: studentSession }} params={{}} />); + profile.getProfile().then(()=> expect(profile.state.acceptedNo).to.not.equal('')); + profile.getProfile().then(()=> expect(profile.state.readNo).to.not.equal('')); + fetchMock.restore(); + }); }); diff --git a/core/serializers/accounts.py b/core/serializers/accounts.py index e57747ce0e78f08912ea597cb139affa390f56ad..0007e52a4771bf8a3d2bd541301e139fc6330960 100644 --- a/core/serializers/accounts.py +++ b/core/serializers/accounts.py @@ -15,17 +15,23 @@ class StudentSerializer(serializers.ModelSerializer): user = BasicUserSerializer() name = serializers.ReadOnlyField() accepted_no = serializers.SerializerMethodField() + read_no = serializers.SerializerMethodField() class Meta: model = Student fields = ['id', 'name', 'user', 'npm', 'resume', 'phone_number', 'birth_place', 'birth_date', 'major', 'batch', \ - 'show_transcript', 'photo', 'accepted_no'] + 'show_transcript', 'photo', 'accepted_no', 'read_no'] def get_accepted_no(self, obj): apps = Application.objects.filter(student=obj, status=4) companies = apps.values('vacancy__company').distinct() return companies.count() + def get_read_no(self, obj): + apps = Application.objects.filter(student=obj, status=1) + companies = apps.values('vacancy__company').distinct() + return companies.count() + class StudentUpdateSerializer(serializers.ModelSerializer): email = serializers.EmailField() diff --git a/core/tests/test_accounts.py b/core/tests/test_accounts.py index 35458deb5cb4174f6856ffcdfd95439d6ba6e3a6..25281a7cc774e3a409d4eebb22cabca899f6d795 100644 --- a/core/tests/test_accounts.py +++ b/core/tests/test_accounts.py @@ -8,6 +8,7 @@ from core.models.accounts import Company, Supervisor, Student class LoginTests(APITestCase): @requests_mock.Mocker() def test_succesful_student_login_relogin(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", @@ -16,7 +17,7 @@ class LoginTests(APITestCase): "kodeidentitas": "1234567890", "nama_role": "mahasiswa" }, status_code=200) - m.get('https://api.cs.ui.ac.id/siakngcs/mahasiswa/1234567890/', json={ + 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": [{ @@ -35,6 +36,7 @@ class LoginTests(APITestCase): @requests_mock.Mocker() def test_successful_supervisor_login_relogin(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.dosen", "nama": "Dummy Dosen", @@ -106,6 +108,7 @@ class ProfileUpdateTests(APITestCase): @requests_mock.Mocker() def test_student_profile_update(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", @@ -114,7 +117,7 @@ class ProfileUpdateTests(APITestCase): "kodeidentitas": "1234567890", "nama_role": "mahasiswa" }, status_code=200) - m.get('https://api.cs.ui.ac.id/siakngcs/mahasiswa/1234567890/', json={ + 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": [{ diff --git a/core/tests/test_vacancies.py b/core/tests/test_vacancies.py index 78cdc5a2a71e0fcf48249f1a28df4277fb5221d7..1667ac14aaed6ec90c9f50562ea9c96ea363b47d 100644 --- a/core/tests/test_vacancies.py +++ b/core/tests/test_vacancies.py @@ -12,6 +12,7 @@ from core.models.vacancies import Vacancy, Application class ApplicationTests(APITestCase): @requests_mock.Mocker() def test_application_list(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", @@ -20,7 +21,7 @@ class ApplicationTests(APITestCase): "kodeidentitas": "1234567890", "nama_role": "mahasiswa" }, status_code=200) - m.get('https://api.cs.ui.ac.id/siakngcs/mahasiswa/1234567890/', json={ + 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": [{ @@ -42,6 +43,7 @@ class ApplicationTests(APITestCase): @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) m.post('https://api.cs.ui.ac.id/authentication/ldap/v2/', json={ "username": "dummy.mahasiswa", "nama": "Dummy Mahasiswa", @@ -50,7 +52,7 @@ class ApplicationTests(APITestCase): "kodeidentitas": "1234567890", "nama_role": "mahasiswa" }, status_code=200) - m.get('https://api.cs.ui.ac.id/siakngcs/mahasiswa/1234567890/', json={ + 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": [{ @@ -84,6 +86,7 @@ class ApplicationTests(APITestCase): class BookmarkApplicationTests(APITestCase): @requests_mock.Mocker() def test_application_list(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", @@ -92,7 +95,7 @@ class BookmarkApplicationTests(APITestCase): "kodeidentitas": "1234567890", "nama_role": "mahasiswa" }, status_code=200) - m.get('https://api.cs.ui.ac.id/siakngcs/mahasiswa/1234567890/', json={ + 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": [{ @@ -114,6 +117,7 @@ class BookmarkApplicationTests(APITestCase): @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) m.post('https://api.cs.ui.ac.id/authentication/ldap/v2/', json={ "username": "dummy.mahasiswa", "nama": "Dummy Mahasiswa", @@ -122,7 +126,7 @@ class BookmarkApplicationTests(APITestCase): "kodeidentitas": "1234567890", "nama_role": "mahasiswa" }, status_code=200) - m.get('https://api.cs.ui.ac.id/siakngcs/mahasiswa/1234567890/', json={ + 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": [{ diff --git a/core/views/accounts.py b/core/views/accounts.py index 0b74350fbc0a57c6d6d6a22562cfd647bc7f99a9..9ea10d861229a65fbb8bef2ac4ff23f8803f9539 100644 --- a/core/views/accounts.py +++ b/core/views/accounts.py @@ -9,6 +9,7 @@ from rest_framework.parsers import FormParser,MultiPartParser from rest_framework.permissions import AllowAny from rest_framework.permissions import IsAdminUser, IsAuthenticated from rest_framework.response import Response +from .sso_login import get_access_token, verify_user, get_riwayat_user, get_summary_user from core.lib.permissions import IsAdminOrStudent, IsAdminOrSelfOrReadOnly, IsAdminOrCompany, IsAdminOrSupervisor, \ IsAdminOrSupervisorOrCompanyOrSelf @@ -81,14 +82,9 @@ class StudentViewSet(viewsets.ModelViewSet): raise PermissionDenied("You are not allowed to see other student's transcript") if student.show_transcript: - s = requests.Session() - credentials = settings.API_CS_CREDENTIALS - s.get('https://api.cs.ui.ac.id/api-auth/login/') - csrf = s.cookies['csrftoken'] - resp = s.post('https://api.cs.ui.ac.id/api-auth/login/', - data={'username': credentials["user"], 'password': credentials["password"], - 'csrfmiddlewaretoken': csrf}) - response = s.get('https://api.cs.ui.ac.id/siakngcs/mahasiswa/' + str(student.npm) + '/riwayat/') + access_token = get_access_token(credentials["user"], credentials["password"]) + ver_user = verify_user(access_token) + response = get_riwayat_user(access_token, ver_user['identity_number']) return Response({'name': student.full_name, 'transcript': response.json()}, status=status.HTTP_200_OK) else: return Response({'name': student.full_name, 'error': 'student does not allow transcript to be shown'}, @@ -146,7 +142,7 @@ class LoginViewSet(viewsets.GenericViewSet): return Response(status=status.HTTP_400_BAD_REQUEST) if login_type == "sso-ui": r = requests.post('https://api.cs.ui.ac.id/authentication/ldap/v2/', - json={"username": username, "password": password}) + data={"username": username, "password": password}) resp = r.json() if resp.get('state') != 0: # create user @@ -167,8 +163,9 @@ class LoginViewSet(viewsets.GenericViewSet): login(request, user) if created: if resp.get('nama_role') == "mahasiswa": - student_detail = requests.get('https://api.cs.ui.ac.id/siakngcs/mahasiswa/{}/'.format(resp.get("kodeidentitas"))) - resp_student_detail = student_detail.json() + access_token = get_access_token(username, password) + ver_user = verify_user(access_token) + resp_student_detail = get_summary_user(access_token, ver_user['identity_number']) student = Student.objects.create( user=user, npm=resp.get("kodeidentitas"), diff --git a/core/views/sso_login.py b/core/views/sso_login.py new file mode 100644 index 0000000000000000000000000000000000000000..669bf2be7327b5e268015c8b4e303734ee64cd47 --- /dev/null +++ b/core/views/sso_login.py @@ -0,0 +1,48 @@ +import requests + +API_MAHASISWA = "https://api.cs.ui.ac.id/siakngcs/mahasiswa/" +API_RIWAYAT_MAHASISWA = API_MAHASISWA+'{npm}/riwayat/' +API_VERIFY_USER = "https://akun.cs.ui.ac.id/oauth/token/verify/" +def get_access_token(username, password): + try: + url = "https://akun.cs.ui.ac.id/oauth/token/" + + payload = "username=" + username + "&password=" + password + "&grant_type=password" + headers = { + 'authorization': "Basic WDN6TmtGbWVwa2RBNDdBU05NRFpSWDNaOWdxU1UxTHd5d3U1V2VwRzpCRVFXQW43RDl6a2k3NEZ0bkNpWVhIRk50Ymg3eXlNWmFuNnlvMU1uaUdSVWNGWnhkQnBobUU5TUxuVHZiTTEzM1dsUnBwTHJoTXBkYktqTjBxcU9OaHlTNGl2Z0doczB0OVhlQ3M0Ym1JeUJLMldwbnZYTXE4VU5yTEFEMDNZeA==", + 'cache-control': "no-cache", + 'content-type': "application/x-www-form-urlencoded" + } + response = requests.request("POST", url, data=payload, headers=headers) + + return response.json()["access_token"] + except Exception as e: + return None + # raise Exception("username atau password sso salah, input : [{}, {}]".format(username, password,)) + +def get_client_id(): + client_id = 'X3zNkFmepkdA47ASNMDZRX3Z9gqSU1Lwywu5WepG' + return client_id + +def verify_user(access_token): + print ("#get identity number") + parameters = {"access_token": access_token, "client_id": get_client_id()} + response = requests.get(API_VERIFY_USER, params=parameters) + print ("response => ", response.json()) + return response.json() + +def get_summary_user(access_token, npm): + print ("#get summary user => ", npm) + parameters = {"access_token": access_token, "client_id": get_client_id()} + response = requests.get(API_MAHASISWA+str(npm), params=parameters) + print ("response => ", response.text) + print ("response => ", response.json()) + return response.json() + +def get_riwayat_user(access_token, npm): + print ("#get riwayat user => ", npm) + parameters = {"access_token": access_token, "client_id": get_client_id()} + response = requests.get(API_RIWAYAT_MAHASISWA.format(npm=npm), params=parameters) + print ("response => ", response.text) + print ("response => ", response.json()) + return response.json() diff --git a/sonar-project.properties b/sonar-project.properties index 48a6c493c8cf0d1ead4cb49babe175535621ae67..c3db74cddc82e00230c7f6527111959ce3a27b3d 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -7,6 +7,7 @@ sonar.projectKey=id.ac.ui.cs.foss:kape sonar.exclusions=/.devcontainer/,/.gitlab/,*.config.js,/.tmp/, sonar.scm.provider=git +sonar.projectVersion=1.0.0 ## Authentication ### sonar.login=[pass token via CLI/CI]