diff --git a/assets/js/EditProfile.jsx b/assets/js/EditProfile.jsx index 28fd238de8d69401b1e9986944f86ea1e52b1984..198955f553727cceff37cea6bab9580191782ada 100644 --- a/assets/js/EditProfile.jsx +++ b/assets/js/EditProfile.jsx @@ -423,11 +423,12 @@ export default class ProfilePage extends React.Component { /> </Form.Field> <Form.Field> - <label htmlFor="linkedin_url">URL Profile Github</label> - <input - onChange={this.handleChange} - placeholder="https://github.com/bob" - name="github_url" + <label htmlFor="github_url">URL Profile Github</label> + <input + onChange={this.handleChange} + placeholder={this.state.github_url === null ? "https://github.com/bob" : this.state.github_url} + defaultValue={this.state.github_url === null ? null : this.state.github_url} + name="github_url" /> </Form.Field> <Form.Field> @@ -460,9 +461,10 @@ export default class ProfilePage extends React.Component { </Form.Field> <Form.Field> <label htmlFor="skills">Skills</label> - <input - onChange={this.handleChange} - placeholder="Isi sesuai dengan keahlian anda" + <input + onChange={this.handleChange} + placeholder="Isi sesuai dengan keahlian anda" + defaultValue={this.state.skills === null ? 'Competitive Programming' : this.state.skills} name="skills" /> </Form.Field> @@ -601,12 +603,8 @@ export default class ProfilePage extends React.Component { /> </Form.Field> <Form.Field> - <label htmlFor="phone_number">Expected Salary</label> - <input - onChange={this.handleChange} - placeholder="2000000" - name="expected_salary" - /> + <label htmlFor="expected_salary">Expected Salary</label> + <input onChange={this.handleChange} placeholder="2000000" name="expected_salary" /> </Form.Field> <Form.Field> <label htmlFor="intro">Intro</label> diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py deleted file mode 100644 index 5d4a9e472b949e9b0ecbeebcb2fbca9689676eee..0000000000000000000000000000000000000000 --- a/core/migrations/0001_initial.py +++ /dev/null @@ -1,194 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.17 on 2019-11-15 11:27 -from __future__ import unicode_literals - -import core.lib.validators -import core.models.accounts -from django.conf import settings -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='Application', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('cover_letter', models.TextField(blank=True, null=True)), - ('status', models.IntegerField(default=0)), - ], - ), - migrations.CreateModel( - name='Company', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', models.DateTimeField(auto_now_add=True)), - ('updated', models.DateTimeField(auto_now=True)), - ('description', models.TextField()), - ('status', models.IntegerField(default=0)), - ('logo', models.FileField(blank=True, null=True, upload_to=core.models.accounts.get_company_logo_file_path, validators=[core.lib.validators.validate_image_file_extension])), - ('address', models.CharField(blank=True, max_length=1000, null=True)), - ('category', models.CharField(default='Belum ada kategori perusahaan', max_length=140)), - ('website', models.CharField(default='Belum ada link website', max_length=100)), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - options={ - 'ordering': ['-updated'], - }, - ), - 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='', max_length=100)), - ('content', models.TextField()), - ], - options={ - 'ordering': ['created'], - }, - ), - migrations.CreateModel( - name='Student', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', models.DateTimeField(auto_now_add=True)), - ('updated', models.DateTimeField(auto_now=True)), - ('npm', models.IntegerField(unique=True, validators=[core.lib.validators.validate_npm])), - ('resume', models.FileField(blank=True, null=True, upload_to=core.models.accounts.get_student_resume_file_path, validators=[django.core.validators.FileExtensionValidator(['pdf'])])), - ('sertifikat', models.FileField(blank=True, null=True, upload_to=core.models.accounts.get_student_sertifikat_file_path, validators=[core.lib.validators.validate_document_file_extension])), - ('phone_number', models.CharField(blank=True, db_index=True, max_length=100, null=True, validators=[django.core.validators.RegexValidator('^0\\d{1,11}$')])), - ('gender', models.CharField(blank=True, max_length=30, null=True)), - ('birth_place', models.CharField(blank=True, max_length=30, null=True)), - ('birth_date', models.DateField(blank=True, null=True)), - ('major', models.CharField(blank=True, max_length=30, null=True)), - ('batch', models.CharField(blank=True, max_length=4, null=True)), - ('show_transcript', models.BooleanField(default=False)), - ('photo', models.FileField(blank=True, null=True, upload_to=core.models.accounts.get_student_photo_file_path, validators=[django.core.validators.FileExtensionValidator(['jpg', 'jpeg', 'png'])])), - ('self_description', models.CharField(blank=True, db_index=True, max_length=500, null=True)), - ('portfolio_link', models.URLField(blank=True, null=True)), - ('linkedin_url', models.URLField(blank=True, null=True)), - ('hackerrank_url', models.URLField(blank=True, null=True)), - ('website_url', models.URLField(blank=True, null=True)), - ('work_experience', models.CharField(blank=True, max_length=500, null=True)), - ('region', models.CharField(blank=True, max_length=30, null=True)), - ('alamat', models.CharField(blank=True, max_length=50, null=True)), - ('skills', models.CharField(blank=True, max_length=50, null=True)), - ('recommendations', models.CharField(blank=True, max_length=500, null=True)), - ('ui_ux_portofolio', models.FileField(blank=True, null=True, upload_to=core.models.accounts.get_student_ui_ux_portofolio_file_path, validators=[core.lib.validators.validate_document_file_extension])), - ('latest_work', models.CharField(blank=True, max_length=100, null=True)), - ('latest_work_desc', models.TextField(blank=True, null=True)), - ('github_url', models.URLField(blank=True, null=True)), - ('intro', models.CharField(blank=True, max_length=50, null=True)), - ('expected_salary', models.CharField(blank=True, max_length=10, null=True)), - ('job_seeking_status', models.CharField(blank=True, max_length=30, null=True)), - ('student_gpa', models.FloatField(blank=True, db_column='student_gpa', default=1.0, null=True, validators=[core.lib.validators.validate_student_gpa])), - ('volunteer', models.CharField(blank=True, max_length=100, null=True)), - ('awards', models.CharField(blank=True, max_length=100, null=True)), - ('projects', models.CharField(blank=True, max_length=100, null=True)), - ('certification', models.CharField(blank=True, max_length=100, null=True)), - ('languages', models.CharField(blank=True, max_length=100, null=True)), - ('seminar', models.CharField(blank=True, max_length=100, null=True)), - ('interests', models.CharField(blank=True, max_length=100, null=True)), - ('dependants', models.IntegerField(blank=True, db_column='dependants', default=0, null=True)), - ], - options={ - 'ordering': ['-updated'], - }, - ), - migrations.CreateModel( - name='Supervisor', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', models.DateTimeField(auto_now_add=True)), - ('updated', models.DateTimeField(auto_now=True)), - ('nip', models.IntegerField(unique=True, validators=[django.core.validators.MinValueValidator(100000000), django.core.validators.MaxValueValidator(9999999999)])), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - options={ - 'ordering': ['-updated'], - }, - ), - migrations.CreateModel( - name='Vacancy', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('verified', models.BooleanField(default=False)), - ('open_time', models.DateTimeField()), - ('description', models.TextField(blank=True)), - ('requirements', models.TextField(blank=True)), - ('responsibilities', models.TextField(blank=True)), - ('close_time', models.DateTimeField()), - ('created', models.DateTimeField(auto_now_add=True)), - ('updated', models.DateTimeField(auto_now=True)), - ('name', models.CharField(max_length=100)), - ('amount', models.IntegerField(null=True)), - ('max_accepted_applicants', models.IntegerField(default=0)), - ('benefits', models.TextField(blank=True)), - ('working_period', models.CharField(max_length=100, null=True)), - ('tag', models.TextField(blank=True)), - ('salary', models.IntegerField(default=0)), - ('recruiter_activity', models.CharField(blank=True, max_length=10, null=True)), - ('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vacancies', to='core.Company')), - ], - options={ - 'ordering': ['-updated'], - }, - ), - migrations.CreateModel( - name='VacancyMilestone', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=100)), - ('detail', models.TextField()), - ('expected_start', models.DateField()), - ('expected_finish', models.DateField()), - ('vacancy', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='milestones', to='core.Vacancy')), - ], - ), - migrations.CreateModel( - name='ReasonRejected', - fields=[ - ('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='core.Application')), - ('reason', models.TextField(default='Tidak memenuhi kualifikasi perusahaan.')), - ], - ), - migrations.AddField( - model_name='student', - name='applied_vacancies', - field=models.ManyToManyField(blank=True, related_name='applied_vacancies', through='core.Application', to='core.Vacancy'), - ), - migrations.AddField( - model_name='student', - name='bookmarked_vacancies', - field=models.ManyToManyField(blank=True, related_name='bookmarked_vacancies', to='core.Vacancy'), - ), - migrations.AddField( - model_name='student', - name='user', - field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='application', - name='student', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Student'), - ), - migrations.AddField( - model_name='application', - name='vacancy', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Vacancy'), - ), - migrations.AlterUniqueTogether( - name='application', - unique_together=set([('student', 'vacancy')]), - ), - ] diff --git a/core/tests/test_accounts.py b/core/tests/test_accounts.py index 6c1dab9705ff5c1698a6ef3205691f0923770fc4..39f82499fafff308ecbc94af43563bf7e1f2b440 100755 --- a/core/tests/test_accounts.py +++ b/core/tests/test_accounts.py @@ -93,12 +93,54 @@ class RegisterTests(APITestCase): right_checksum.full_clean() - class ProfileUpdateTests(APITestCase): def _create_test_file_pdf(self, path): file = open(path, 'rb') return {'pdf_sertifikat':file} + @requests_mock.Mocker() + def test_student_profile_update_validator(self, m): + mock_csui_oauth_verify(m) + mock_csui_ldap_student(m) + mock_csui_siak_student(m) + + 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) + "/profile/" + response = self.client.patch(url, {'self_description': 'I am very happy'}, format='multipart') + self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual(response.data.get('self_description'), 'I am very happy') + + url = '/api/students/' + str(student_id) + "/profile/" + response = self.client.patch(url, {'linkedin_url': 'https://www.linkedin.com/in/jojo/'}, format='multipart') + self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual(response.data.get('linkedin_url'), 'https://www.linkedin.com/in/jojo/') + + url = '/api/students/' + str(student_id) + "/profile/" + response = self.client.patch(url, {'linkedin_url': 'https://www.linkedin.com/jojo/'}, format='multipart') + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.data.get('linkedin_url'), None) + + response = self.client.patch(url, {'hackerrank_url': 'https://www.hackerrank.com/james'}, format='multipart') + self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual(response.data.get('hackerrank_url'), 'https://www.hackerrank.com/james') + + response = self.client.patch(url, {'hackerrank_url': 'https://www.hackerank.com/james'}, format='multipart') + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.data.get('hackerrank_url'), None) + + response = self.client.patch(url, {'github_url': 'https://github.com/bob'}, format='multipart') + self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual(response.data.get('github_url'), 'https://github.com/bob') + + response = self.client.patch(url, {'github_url': 'https://gitlab.com/bob'}, format='multipart') + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.data.get('github_url'), None) + + @requests_mock.Mocker() def test_student_profile_update(self, m): mock_csui_oauth_verify(m) diff --git a/core/views/accounts.py b/core/views/accounts.py index 805758b4f863b3f9589403442c78cd0997d8a743..ede14400940412f2cf3851372ded43aeb3ef529c 100755 --- a/core/views/accounts.py +++ b/core/views/accounts.py @@ -1,4 +1,5 @@ import requests +import re from django.contrib.auth import authenticate, login from django.contrib.auth.models import User from django.shortcuts import get_object_or_404 @@ -63,6 +64,10 @@ class StudentViewSet(viewsets.ModelViewSet): --- """ user = self.get_object() + github_url_pattern = "^(https:\/\/www\.|https:\/\/)(github.com\/)([a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38})(\/)?$" + linkedin_url_pattern = "^(https:\/\/www\.)(linkedin.com)(\/in)(\/[a-zA-Z0-9]*)(\/)?$" + hackerrank_url_pattern = "^(https:\/\/www\.)(hackerrank.com)(\/[a-zA-Z0-9]*)(\/)?$" + if 'pdf_sertifikat' in request.data.keys(): if request.data['pdf_sertifikat'].content_type == 'application/pdf': user.sertifikat = request.data['pdf_sertifikat'] @@ -79,6 +84,18 @@ class StudentViewSet(viewsets.ModelViewSet): else: return Response({}, status=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE) + if 'github_url' in request.data.keys(): + if not re.search(github_url_pattern, request.data['github_url']): + return Response({'Error': "Pastikan link github yang anda tulis benar. (Berpola : https://github.com/<username>"}, status=status.HTTP_400_BAD_REQUEST) + + if 'linkedin_url' in request.data.keys(): + if not re.search(linkedin_url_pattern, request.data['linkedin_url']): + return Response({'Error': "Pastikan link linkedin yang anda tulis benar. (Berpola : https://linkedin.com/in/<username>"}, status=status.HTTP_400_BAD_REQUEST) + + if 'hackerrank_url' in request.data.keys(): + if not re.search(hackerrank_url_pattern, request.data['hackerrank_url']): + return Response({'Error': "Pastikan link hackerrank yang anda tulis benar. (Berpola : https://hackerrank.com/<username>"}, status=status.HTTP_400_BAD_REQUEST) + serializer = self.serializer_class(user, data=request.data, partial=True) if serializer.is_valid(): serializer.save() diff --git a/package.json b/package.json index e0f08a8b33ac235ec477d97485f5e44c947ccdb1..547fcaab888272c66ba60013a27eed9129565c75 100755 --- a/package.json +++ b/package.json @@ -61,6 +61,8 @@ "mock-cookie": "^0.3.2", "react-hot-loader": "^3.0.0-beta.5", "react-test-utils": "0.0.1", + "webpack": "^4.41.2", + "webpack-bundle-tracker": "0.0.93", "webpack-cli": "^3.3.10", "webpack-dev-server": "^3.9.0" },