Fakultas Ilmu Komputer UI

Commit ac2da493 authored by Giovan Isa Musthofa's avatar Giovan Isa Musthofa
Browse files

Merge branch 'staging' into 'master'

Staging

See merge request !125
parents 65572c63 b2552c77
Pipeline #50093 failed with stages
in 23 minutes and 8 seconds
.data
\ No newline at end of file
......@@ -22,7 +22,7 @@ DRF Unit Tests:
- export DEBUG=True
- pip install -r requirements-dev.txt
script:
- coverage run --include='main/*,stok_darah/*,donor/*,rest_framework_authlib/*,acara_donor/*' manage.py test
- coverage run --include='main/*,stok_darah/*,donor/*,rest_framework_authlib/*,acara_donor/*,edukasi/*' manage.py test
- coverage report --show-missing
- coverage xml
artifacts:
......
......@@ -10,3 +10,4 @@ db.sqlite3
.coverage
coverage.xml
.idea/
media/
\ No newline at end of file
......@@ -28,4 +28,6 @@ ENV DJANGO_SETTINGS_MODULE 'dblood.settings.production'
ENV DEBUG 'False'
ENV STATIC_URL '/api/static/'
RUN ["python", "manage.py", "installtasks"]
CMD ["gunicorn", "dblood.wsgi", "--bind", "0.0.0.0:8000"]
......@@ -47,7 +47,7 @@ PS C:\Users\Giovan\Projects\mantan-aab-d-blood\drf> Set-ExecutionPolicy -Scope C
5. Run unit tests and measure coverage
```
(venv) PS C:\Users\Giovan\Projects\mantan-aab-d-blood\drf> coverage run --include='main/*,stok_darah/*,donor/*,rest_framework_authlib/*' manage.py test
(venv) PS C:\Users\Giovan\Projects\mantan-aab-d-blood\drf> coverage run --include='main/*,stok_darah/*,donor/*,rest_framework_authlib/*,acara_donor/*,edukasi/*' manage.py test
(venv) PS C:\Users\Giovan\Projects\mantan-aab-d-blood\drf> coverage report --show-missing
```
......
from django.contrib import admin
from django.conf import settings
from django.utils.timezone import localtime
from django.utils.html import mark_safe
from acara_donor.models import AcaraDonor
from donor.models import JadwalDonor
from acara_donor.filters import AcaraDonorFilter
@admin.register(AcaraDonor)
class AcaraDonorAdmin(admin.ModelAdmin):
list_filter = ('status', AcaraDonorFilter, 'alamat_lokasi_donor')
readonly_fields = ('nomor',
list_display = ('nama_institusi', 'alamat_lokasi_donor',
'display_waktu', 'status')
list_filter = ('status', AcaraDonorFilter, 'kecamatan', 'kategori')
search_fields = ('waktu_mulai', 'alamat_lokasi_donor')
readonly_fields = ('id',
'user',
'nama_institusi',
'alamat_institusi',
'no_telp_kantor',
'email_kantor',
'no_telp_kantor',
'nama_koor',
'email_koor',
'no_telp_koor')
'no_telp_koor',
'keterangan',
'foto_lokasi',
'show_image')
fieldsets = (
(None, {
'fields': ('id',
'user',
'status')
}),
('INFORMASI ACARA DONOR', {
'fields': ('kategori',
'alamat_lokasi_donor',
'kecamatan',
('waktu_mulai', 'waktu_berakhir'),
'perkiraan_jumlah_donor',
'keterangan',
'show_image')
}),
('INFORMASI PENYELENGGARA', {
'fields': ('nama_institusi', 'alamat_institusi',
'email_kantor', 'no_telp_kantor')
}),
('KONTAK KOORDINATOR', {
'fields': ('nama_koor', 'email_koor', 'no_telp_koor')
}),
)
def display_waktu(self, obj):
"""Create a string for waktu acara donor."""
waktu_mulai = localtime(obj.waktu_mulai)
waktu_berakhir = localtime(obj.waktu_berakhir)
return f"{waktu_mulai.date().strftime('%d/%m/%Y')} \
({waktu_mulai.hour:02d}:{waktu_mulai.minute:02d} \
- {waktu_berakhir.hour:02d}:{waktu_berakhir.minute:02d})"
def show_image(self, obj):
"""Create an image preview for foto lokasi acara donor."""
return mark_safe('<img src="%s" style="width=auto; height=auto; \
max-height: 1000px; max-width: 1000px;" />'
% (obj.foto_lokasi.url))
display_waktu.short_description = 'Waktu'
show_image.short_description = 'Preview foto lokasi'
class Media:
js = [settings.MEDIA_URL+'/js/acara_donor_custom_admin.js', ]
def save_model(self, request, obj, form, change):
if(obj.status):
JadwalDonor.objects.create(kecamatan=obj.kecamatan,
location=obj.alamat_lokasi_donor,
time_start=obj.waktu_mulai,
time_end=obj.waktu_berakhir,
quota=obj.perkiraan_jumlah_donor,
category=obj.kategori)
super().save_model(request, obj, form, change)
import factory
import dateutil.tz
from datetime import timedelta
from django.utils import timezone
from random import choice, randint
from acara_donor.models import AcaraDonor
from donor.models import JadwalDonor
from main.factories import UserFactory
......@@ -11,17 +14,27 @@ class AcaraDonorFactory(factory.DjangoModelFactory):
class Meta:
model = AcaraDonor
nomor = factory.Faker('uuid4')
user = factory.SubFactory(UserFactory)
status = factory.Faker('boolean', chance_of_getting_true=66)
nama_institusi = factory.Faker('company')
alamat_institusi = factory.Faker('address', locale=LOCALE)
alamat_lokasi_donor = factory.Faker('address', locale=LOCALE)
no_telp_kantor = factory.Faker('phone_number')
email_kantor = factory.Faker('company_email', locale=LOCALE)
no_telp_kantor = factory.Sequence(lambda n: '+6281-1128-%04d' % n)
nama_koor = factory.Faker('name', locale=LOCALE)
email_koor = factory.Faker('free_email', locale=LOCALE)
no_telp_koor = factory.Faker('phone_number')
waktu_donor = factory.Faker('future_datetime', tzinfo=dateutil.tz.gettz('Asia/Jakarta'))
no_telp_koor = factory.Sequence(lambda n: '021-1128-%04d' % n)
kategori = factory.LazyAttribute(
lambda _: choice(JadwalDonor.Category.choices)[0]) # NOSONAR
alamat_lokasi_donor = factory.Faker('address', locale=LOCALE)
kecamatan = factory.LazyAttribute(
lambda _: choice(JadwalDonor.Kecamatan.choices)[0]) # NOSONAR
waktu_mulai = factory.LazyAttribute(
lambda _: timezone.now() + timedelta(days=randint(1, 66), # NOSONAR
hours=randint(1, 23))) # NOSONAR
waktu_berakhir = factory.LazyAttribute(
lambda t: t.waktu_mulai + timedelta(hours=randint(2, 6))) # NOSONAR
perkiraan_jumlah_donor = factory.fuzzy.FuzzyInteger(low=33, high=666)
keterangan = factory.Faker('text')
......@@ -7,7 +7,7 @@ from django.utils.translation import gettext_lazy as _
class AcaraDonorFilter(admin.SimpleListFilter):
title = _('waktu donor')
parameter_name = 'waktu_donor'
parameter_name = 'waktu_mulai'
def lookups(self, request, model_admin):
return (
......@@ -24,33 +24,34 @@ class AcaraDonorFilter(admin.SimpleListFilter):
def __last_day_of_month(self, any_day):
next_month = any_day.replace(day=28) + datetime.timedelta(days=4)
return next_month - datetime.timedelta(days=next_month.day) + datetime.timedelta(days=1)
return next_month - datetime.timedelta(days=next_month.day) \
+ datetime.timedelta(days=1)
def queryset(self, request, queryset):
today = localtime(now()).replace(hour=0, minute=0, second=0, microsecond=0)
tomorrow = today + datetime.timedelta(days=1)
today = localtime(now()).replace(
hour=0, minute=0, second=0, microsecond=0)
next_seven_days = today + datetime.timedelta(days=8)
this_month = today.replace(day=1)
end_of_this_month = self.__last_day_of_month(today)
start_of_next_month = today.replace(year=today.year, month=today.month + 1, day=1)
start_of_next_month = today.replace(year=today.year,
month=today.month + 1, day=1)
end_of_next_month = self.__last_day_of_month(start_of_next_month)
this_year = today.replace(month=1, day=1)
next_year = today.replace(year=today.year + 1, month=1, day=1)
if self.value() == 'today':
return queryset.filter(waktu_donor__range=[today, tomorrow])
return queryset.filter(waktu_mulai__date=today)
if self.value() == 'next_seven_days':
return queryset.filter(waktu_donor__range=[today, next_seven_days])
return queryset.filter(waktu_mulai__range=[today, next_seven_days])
if self.value() == 'this_month':
return queryset.filter(waktu_donor__range=[this_month, end_of_this_month])
return queryset.filter(
waktu_mulai__range=[this_month, end_of_this_month])
if self.value() == 'next_month':
return queryset.filter(waktu_donor__range=[start_of_next_month, end_of_next_month])
return queryset.filter(waktu_mulai__range=[start_of_next_month,
end_of_next_month])
if self.value() == 'this_year':
return queryset.filter(waktu_donor__range=[this_year, next_year])
return queryset.filter(waktu_mulai__year=today.year)
import random
from django.core.management.base import BaseCommand
from django.core.management.base import BaseCommand, CommandError
from main.models import User
from acara_donor.factories import AcaraDonorFactory
from main.factories import UserFactory
class Command(BaseCommand):
help = 'Seeds the database.'
help = 'Seeds the database with dummy acara donor.'
def add_arguments(self, parser):
parser.add_argument('--users_emails', nargs='+', type=str)
def handle(self, *args, **options):
list_user = []
for _ in range(33):
user = UserFactory()
user.save()
list_user.append(user)
for _ in range(123):
i = random.randint(0, 32) # NOSONAR
AcaraDonorFactory(user=list_user[i])
self.stdout.write(self.style.SUCCESS("Successfully seeds the database with dummy acara donor."))
if not options['users_emails']:
for _ in range(33):
user = UserFactory()
user.save()
list_user.append(user)
else:
for email in options['users_emails']:
try:
list_user.append(User.objects.get(email=email))
except User.DoesNotExist:
raise CommandError(f"User {email} does not exist.")
for i in range(len(list_user)):
amount = random.randint(0, 32) # NOSONAR
for _ in range(amount):
AcaraDonorFactory(user=list_user[i])
self.stdout.write(
self.style.SUCCESS(
"Successfully seeds the database with dummy acara donor."))
# Generated by Django 3.0.5 on 2020-05-04 08:29
# Generated by Django 3.0.5 on 2020-06-02 00:45
from django.conf import settings
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
......@@ -19,7 +18,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='AcaraDonor',
fields=[
('nomor', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.BooleanField(null=True)),
('nama_institusi', models.CharField(max_length=95)),
('alamat_institusi', models.CharField(max_length=140)),
......@@ -28,14 +27,18 @@ class Migration(migrations.Migration):
('nama_koor', models.CharField(max_length=70)),
('email_koor', models.EmailField(max_length=254)),
('no_telp_koor', models.CharField(max_length=20)),
('kategori', models.CharField(choices=[('Terbuka', 'Public'), ('Tertutup', 'Private')], max_length=20)),
('alamat_lokasi_donor', models.CharField(max_length=140)),
('waktu_donor', models.DateTimeField()),
('kecamatan', models.CharField(choices=[('Beji', 'Beji'), ('Pancoran Mas', 'Pancoran Mas'), ('Cipayung', 'Cipayung'), ('Sukmajaya', 'Sukmajaya'), ('Cilodong', 'Cilodong'), ('Limo', 'Limo'), ('Cinere', 'Cinere'), ('Cimanggis', 'Cimanggis'), ('Tapos', 'Tapos'), ('Sawangan', 'Sawangan'), ('Bojong Sari', 'Bojong Sari')], max_length=20)),
('waktu_mulai', models.DateTimeField()),
('waktu_berakhir', models.DateTimeField()),
('perkiraan_jumlah_donor', models.IntegerField(validators=[django.core.validators.MinValueValidator(11)])),
('keterangan', models.TextField(blank=True)),
('foto_lokasi', models.ImageField(blank=True, upload_to='images')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ['waktu_donor'],
'ordering': ['-waktu_mulai'],
},
),
]
# Generated by Django 3.0.5 on 2020-05-04 20:32
# Generated by Django 3.0.5 on 2020-06-02 13:55
from django.db import migrations
from django.db import migrations, models
class Migration(migrations.Migration):
......@@ -10,8 +10,9 @@ class Migration(migrations.Migration):
]
operations = [
migrations.AlterModelOptions(
name='acaradonor',
options={'ordering': ['-waktu_donor']},
migrations.AlterField(
model_name='acaradonor',
name='status',
field=models.BooleanField(default=None, null=True),
),
]
from django.db import models
from django.core.validators import MinValueValidator
from django.utils.timezone import localtime
from main.models import User
import uuid
from donor.models import JadwalDonor
class AcaraDonor(models.Model):
nomor = models.UUIDField(primary_key=True, default=uuid.uuid4)
user = models.ForeignKey(User, on_delete=models.CASCADE)
status = models.BooleanField(null=True)
status = models.BooleanField(null=True, default=None)
nama_institusi = models.CharField(max_length=95)
alamat_institusi = models.CharField(max_length=140)
email_kantor = models.EmailField()
no_telp_kantor = models.CharField(max_length=20)
nama_koor = models.CharField(max_length=70)
email_koor = models.EmailField()
no_telp_koor = models.CharField(max_length=20)
kategori = models.CharField(max_length=20, blank=False, null=False,
choices=JadwalDonor.Category.choices)
alamat_lokasi_donor = models.CharField(max_length=140)
waktu_donor = models.DateTimeField()
perkiraan_jumlah_donor = models.IntegerField(validators=[MinValueValidator(11)])
kecamatan = models.CharField(max_length=20, blank=False, null=False,
choices=JadwalDonor.Kecamatan.choices)
waktu_mulai = models.DateTimeField()
waktu_berakhir = models.DateTimeField()
perkiraan_jumlah_donor = models.IntegerField(
validators=[MinValueValidator(11)])
keterangan = models.TextField(blank=True)
foto_lokasi = models.ImageField(upload_to='images', blank=True)
def __str__(self):
waktu_donor = self.waktu_donor.strftime("%d/%m/%Y, %H:%M")
return f"{self.nama_institusi}: {self.alamat_lokasi_donor}, {waktu_donor}"
waktu_mulai = localtime(self.waktu_mulai)
waktu_berakhir = localtime(self.waktu_berakhir)
return f"{self.nama_institusi}: {self.alamat_lokasi_donor}, \
{waktu_mulai.date()} \
({waktu_mulai.hour:02d}:{waktu_mulai.minute:02d} \
- {waktu_berakhir.hour:02d}:{waktu_berakhir.minute:02d})"
class Meta:
ordering = ['-waktu_donor']
ordering = ['-waktu_mulai']
......@@ -5,15 +5,26 @@ from acara_donor.models import AcaraDonor
class AcaraDonorSerializer(serializers.ModelSerializer):
def validate(self, data):
queryset = AcaraDonor.objects.filter(nama_institusi=data['nama_institusi'],
alamat_lokasi_donor=data['alamat_lokasi_donor'],
waktu_donor=data['waktu_donor'])
queryset = AcaraDonor.objects.filter(
nama_institusi=data['nama_institusi'],
alamat_lokasi_donor=data['alamat_lokasi_donor'],
waktu_mulai=data['waktu_mulai'])
if queryset.exists():
raise serializers.ValidationError("Acara donor darah ini sudah diajukan.")
if data['waktu_donor'] < timezone.now():
raise serializers.ValidationError("Tanggal acara donor darah tidak boleh di masa lalu.")
raise serializers.ValidationError(
"Acara donor darah ini sudah diajukan.")
if data['waktu_mulai'] < timezone.now():
raise serializers.ValidationError(
"Pelaksanaan acara donor darah tidak boleh di masa lalu.")
if data['waktu_berakhir'] < data['waktu_mulai']:
raise serializers.ValidationError(
"Jam pelaksanaan acara donor darah tidak valid.")
return data
class Meta:
model = AcaraDonor
exclude = ["user", "status"]
exclude = ["user", ]
extra_kwargs = {'foto_lokasi': {'required': False},
'status': {'default': None}}
from io import StringIO
from django.core.management import call_command
from django.core.management import call_command, CommandError
from django.test import TestCase
from main.factories import UserFactory
class SeedAcaraDonorTest(TestCase):
......@@ -9,3 +10,22 @@ class SeedAcaraDonorTest(TestCase):
out = StringIO()
call_command('acara_donor_seeder', stdout=out)
self.assertIn('Success', out.getvalue())
def test_command_with_email_provided_success(self):
user1 = UserFactory(email="tes1@gmail.com")
user2 = UserFactory(email="tes2@gmail.com")
user1.save()
user2.save()
out = StringIO()
call_command(
'acara_donor_seeder', users_emails=["tes1@gmail.com",
"tes2@gmail.com"],
stdout=out)
self.assertIn('Success', out.getvalue())
def test_command_with_email_provided_failed(self):
out = StringIO()
with self.assertRaisesMessage(CommandError,
'User bities@gmail.com does not exist.'):
call_command('acara_donor_seeder',
users_emails=["bities@gmail.com", ], stdout=out)
import datetime
from unittest.mock import patch
from django.test import TestCase
from django.utils import timezone
from django.utils.timezone import localtime
from rest_framework import status
from rest_framework.test import APITestCase
from rest_framework_authlib.tokens import AccessToken
......@@ -17,13 +20,18 @@ class AcaraDonorTest(TestCase):
user = UserFactory()
user.save()
acara_donor = AcaraDonorFactory(user=user)
waktu_donor = acara_donor.waktu_donor.strftime("%d/%m/%Y, %H:%M")
waktu_mulai = localtime(acara_donor.waktu_mulai)
waktu_berakhir = localtime(acara_donor.waktu_berakhir)
self.assertTrue(isinstance(acara_donor, AcaraDonor))
self.assertEqual((str(acara_donor.nama_institusi) + ': ' +
str(acara_donor.alamat_lokasi_donor) + ", " + waktu_donor),
self.assertEqual((f"{acara_donor.nama_institusi}: {acara_donor.alamat_lokasi_donor}, \
{waktu_mulai.date()} \
({waktu_mulai.hour:02d}:{waktu_mulai.minute:02d} \
- {waktu_berakhir.hour:02d}:{waktu_berakhir.minute:02d})"),
str(acara_donor))
@patch('django.utils.timezone.now', return_value=datetime.datetime(
2020, 6, 12, 9, 0, tzinfo=datetime.timezone.utc))
class AcaraDonorSerializerTest(TestCase):
""" Test module for Acara Donor serializer"""
......@@ -31,60 +39,145 @@ class AcaraDonorSerializerTest(TestCase):
self.user = UserFactory()
self.user.save()
self.acara_donor = AcaraDonorFactory(user=self.user)
base_time = timezone.localtime(
datetime.datetime(2020, 6, 12, 9, 18,
tzinfo=datetime.timezone.utc))
self.acara_donor_attr = {"alamat_institusi": "Pacilkom",
"alamat_lokasi_donor": "Sekre Pacil",
"email_kantor": "pacil@cs.ui.ac.id",
"email_koor": "pacilia@gmail.com",
"kategori": "Terbuka",
"kecamatan": "Beji",
"keterangan": "",
"nama_institusi": "Pacilkom",
"nama_koor": "dr. Pacilia",
"no_telp_kantor": "08165342342",
"no_telp_koor": "08167021743",
"nomor": "ea582779-fdfc-444e-ad03-4084e715af0a",
"perkiraan_jumlah_donor": 455,
"waktu_donor": timezone.now() + timezone.timedelta(days=3)}
"waktu_berakhir": base_time
+ timezone.timedelta(hours=5),
"waktu_mulai": base_time
+ timezone.timedelta(hours=3)}
def test_create_acara_donor_failed_due_to_duplication(self,
mock_timezone):
serializer = AcaraDonorSerializer(instance=self.acara_donor,
data=self.acara_donor_attr)
def test_create_acara_donor_failed_due_to_duplication(self):
serializer = AcaraDonorSerializer(instance=self.acara_donor, data=self.acara_donor_attr)
serializer.is_valid()
new_acara_donor = serializer.save()
new_acara_donor.refresh_from_db()
serializer = AcaraDonorSerializer(instance=self.acara_donor, data=self.acara_donor_attr)
serializer = AcaraDonorSerializer(instance=self.acara_donor,
data=self.acara_donor_attr)
self.assertFalse(serializer.is_valid())
def test_create_acara_donor_failed_because_time_has_passed(self,
mock_timezone):
self.acara_donor_attr['waktu_mulai'] = timezone.now() \
- timezone.timedelta(days=3)
serializer = AcaraDonorSerializer(instance=self.acara_donor,
data=self.acara_donor_attr)
self.assertFalse(serializer.is_valid())
def test_create_acara_donor_failed_because_time_has_passed(self):
self.acara_donor_attr['waktu_donor'] = timezone.now() - timezone.timedelta(days=3)
serializer = AcaraDonorSerializer(instance=self.acara_donor, data=self.acara_donor_attr)
def test_create_acara_donor_failed_because_end_time_is_before_start_time(
self, mock_timezone):
self.acara_donor_attr['waktu_mulai'] = timezone.now() \
+ timezone.timedelta(hours=5)
self.acara_donor_attr['waktu_berakhir'] = timezone.now() \
+ timezone.timedelta(hours=3)
serializer = AcaraDonorSerializer(instance=self.acara_donor,
data=self.acara_donor_attr)
self.assertFalse(serializer.is_valid())
@patch('django.utils.timezone.now', return_value=datetime.datetime(
2020, 6, 12, 9, 0, tzinfo=datetime.timezone.utc))
class AcaraDonorCreateViewTest(APITestCase):
""" Test module for Acara Donor API"""
def setUp(self):
self.user = UserFactory()
self.user.save()
self.client.credentials(HTTP_AUTHORIZATION='Bearer ' + str(AccessToken.for_user(self.user)))
def test_create_acara_donor(self):
base_time = timezone.localtime(datetime.datetime(
2020, 6, 12, 9, 18, tzinfo=datetime.timezone.utc))
self.data = {"alamat_institusi": "Pacilkom",
"alamat_lokasi_donor": "Sekre Pacil",
"email_kantor": "pacil@cs.ui.ac.id",
"email_koor": "pacilia@gmail.com",
"foto_lokasi": "",
"kategori": "Terbuka",
"kecamatan": "Beji",
"keterangan": "",
"nama_institusi": "Pacilkom",
"nama_koor": "dr. Pacilia",
"no_telp_kantor": "08165342342",
"no_telp_koor": "08167021743",
"perkiraan_jumlah_donor": 455,
"waktu_berakhir": base_time
+ timezone.timedelta(hours=7),
"waktu_mulai": base_time
+ timezone.timedelta(hours=5)}