Fakultas Ilmu Komputer UI

Commit 571c5e68 authored by Yaumi's avatar Yaumi
Browse files

refactor code conflict

parents e9a0c90b c320f26c
......@@ -100,8 +100,11 @@ Dev Docker Image:
- .docker-image
- .development
allow_failure: true
when: manual
Prod Docker Image:
extends:
- .docker-image
- .production
\ No newline at end of file
- .production
when: manual
allow_failure: true
\ No newline at end of file
......@@ -18,7 +18,7 @@ BASE_PERIODE = [
]
def genereatePeriodeChoices():
def generate_periode_choices():
periode = BASE_PERIODE
now = timezone.now()
# Month
......
......@@ -3,7 +3,7 @@ from crispy_forms.layout import Layout, Submit, Row, Column, Reset, ButtonHolder
from crispy_forms.bootstrap import InlineCheckboxes
from django import forms
from administration.choices import genereatePeriodeChoices
from administration.choices import generate_periode_choices
from administration.models import VerificationSetting
from app.models import Category
from authentication.models import User
......@@ -88,7 +88,7 @@ class RegistrasiAdminForm(forms.ModelForm):
class PeriodForm(forms.Form):
period = forms.ChoiceField(
choices=genereatePeriodeChoices(), label="Periode", required=False)
choices=generate_periode_choices(), label="Periode", required=False)
start_date = forms.DateField(
widget=DateInput, label="Waktu mulai", required=False)
end_date = forms.DateField(
......@@ -102,14 +102,16 @@ class PeriodForm(forms.Form):
for field_name, field in self.fields.items():
field.widget.attrs['class'] = 'form-control'
period_css_class = 'form-group col-md-4 mb-0'
self.helper = FormHelper()
self.helper.attrs["name"] = "filter-form"
self.helper.form_method = 'get'
self.helper.layout = Layout(
Row(
Column('period', css_class='form-group col-md-4 mb-0'),
Column('start_date', css_class='form-group col-md-4 mb-0'),
Column('end_date', css_class='form-group col-md-4 mb-0'),
Column('period', css_class=period_css_class),
Column('start_date', css_class=period_css_class),
Column('end_date', css_class=period_css_class),
css_class='form-row'
),
InlineCheckboxes('categories'),
......@@ -130,10 +132,9 @@ class PeriodForm(forms.Form):
if start_date is None and end_date is not None:
self.add_error("start_date",
"masukan waktu mulai")
if start_date is not None and end_date is not None:
if start_date > end_date:
self.add_error("end_date",
"waktu selesai sebelum waktu mulai")
if start_date is not None and end_date is not None and start_date > end_date:
self.add_error("end_date",
"waktu selesai sebelum waktu mulai")
class EditAdminStatusForm(forms.ModelForm):
......
......@@ -97,20 +97,6 @@
<!-- Card Header - Dropdown -->
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold text-primary">Statistik</h6>
<!-- <div class="dropdown no-arrow">
<a class="dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<em class="fas fa-ellipsis-v fa-sm fa-fw text-gray-400"></em>
</a>
<div class="dropdown-menu dropdown-menu-right shadow animated--fade-in"
aria-labelledby="dropdownMenuLink">
<div class="dropdown-header">Dropdown Header:</div>
<a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">Something else here</a>
</div>
</div> -->
</div>
<!-- Card Body -->
<div class="card-body" style="overflow:auto!important; display:inline-block!important; height: 750px;">
......@@ -128,7 +114,7 @@
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" id="dataTableRiwayat" width="100%" cellspacing="0"
<table class="table table-bordered" id="dataTableRiwayat"
aria-describedby="titleTabelRiwayat">
<thead>
<tr>
......
......@@ -17,7 +17,7 @@
</div>
<div class="card-body">
<div class="table-responsive">
<table aria-describedby="table-description" class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
<table aria-describedby="table-description" class="table table-bordered" id="dataTable">
<thead>
<tr>
<th scope="col">Nama</th>
......
......@@ -21,7 +21,7 @@
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
<table class="table table-bordered" id="dataTable">
<thead>
<tr>
<th scope="col">Judul</th>
......
......@@ -186,7 +186,7 @@
</div>
<div class="card-body">
<div class="table-responsive">
<table aria-describedby="table-description" class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
<table aria-describedby="table-description" class="table table-bordered" id="dataTable">
<thead>
<tr>
<th scope="col">Judul Verifikasi</th>
......@@ -235,29 +235,6 @@
<em class="fas fa-angle-up"></em>
</a>
<!-- <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.5.0.min.js"
integrity="sha256-xNzN2a4ltkB44Mc/Jz3pT4iU1cmeR0FkXs4pru/JxaQ=" crossorigin="anonymous"></script>
<script src="{% static 'js/sb-admin-2.min.js' %}"></script>
<script src="{% static 'vendor/datatables/jquery.dataTables.min.js' %}"></script>
<script src="{% static 'vendor/datatables/dataTables.bootstrap4.min.js' %}"></script>
<script src="{% static 'js/demo/datatables-demo.js' %}"></script> -->
</body>
</html>
\ No newline at end of file
......@@ -61,7 +61,7 @@
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" id="dataTableRiwayat" width="100%" cellspacing="0" aria-describedby="titleTabelRiwayat">
<table class="table table-bordered" id="dataTableRiwayat" aria-describedby="titleTabelRiwayat">
<thead>
<tr>
<th scope="col">Judul Materi</th>
......
......@@ -10,7 +10,9 @@ from app.models import Category, Materi, LaporanMateri
from authentication.models import User
from bs4 import BeautifulSoup
from datetime import datetime
from datetime import datetime, timedelta
from .forms import PeriodForm
EDIT_ENDPOINT = "/edit"
ERROR_403_MESSAGE = 'Kamu harus login untuk mengakses halaman ini'
......@@ -1659,3 +1661,50 @@ class EditKontributorStatusTests(TestCase):
self.assertEqual(response.status_code, 403)
class PeriodFormTests(TestCase):
def test_validation_error_when_start_date_is_none(self):
data = {
'start_date': None,
'end_date': datetime.now(),
}
form = PeriodForm(data)
self.assertFalse(form.is_valid())
self.assertEqual(
form["start_date"].errors,
['masukan waktu mulai']
)
def test_validation_error_when_end_date_is_none(self):
data = {
'start_date': datetime.now(),
'end_date': None,
}
form = PeriodForm(data)
self.assertFalse(form.is_valid())
self.assertEqual(
form["end_date"].errors,
['masukan waktu selesai']
)
def test_validation_error_when_start_date_greater_than_end_date(self):
current_time = datetime.now()
data = {
'start_date': current_time + timedelta(days = 1),
'end_date': current_time,
}
form = PeriodForm(data)
self.assertFalse(form.is_valid())
self.assertEqual(
form["end_date"].errors,
['waktu selesai sebelum waktu mulai']
)
def test_form_valid_when_data_valid(self):
current_time = datetime.now()
data = {
'start_date': current_time - timedelta(days = 1),
'end_date': current_time,
}
form = PeriodForm(data)
self.assertTrue(form.is_valid())
......@@ -19,6 +19,8 @@ from django.core import management
ADMINISTRATION_MANAGEMENT = "/administration/kelola-admin/"
ADMINISTRATION_REPORT = "/administration/laporan-materi/"
ADMINISTRATION_VERIFICATION_SETTING = "/administration/setting/verification/"
ADMINISTRATION_CATEGORY_SETTING = "/administration/setting/category/"
class VerificationView(TemplateView):
......@@ -115,7 +117,7 @@ class VerificationSettingView(TemplateView):
form = VerificationSettingForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect("/administration/setting/verification/")
return HttpResponseRedirect(ADMINISTRATION_VERIFICATION_SETTING)
else:
context = self.get_context_data(**kwargs)
context["form"] = form
......@@ -146,7 +148,7 @@ class CategorySettingView(TemplateView):
form = CategoryForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect("/administration/setting/category/")
return HttpResponseRedirect(ADMINISTRATION_CATEGORY_SETTING)
else:
context = self.get_context_data(**kwargs)
context["form"] = form
......@@ -351,7 +353,7 @@ class EditVerificationView(TemplateView):
request.POST, instance=verification_object)
if form.is_valid():
form.save()
return HttpResponseRedirect("/administration/setting/verification/")
return HttpResponseRedirect(ADMINISTRATION_VERIFICATION_SETTING)
else:
context = self.get_context_data(**kwargs)
context["form"] = form
......@@ -383,7 +385,7 @@ class EditCategoryView(TemplateView):
request.POST, instance=category_object)
if form.is_valid():
form.save()
return HttpResponseRedirect("/administration/setting/category/")
return HttpResponseRedirect(ADMINISTRATION_CATEGORY_SETTING)
else:
context = self.get_context_data(**kwargs)
context["form"] = form
......@@ -483,15 +485,15 @@ def delete_contributor(request, *args, **kwargs):
def delete_verification(request, *args, **kwargs):
if not request.user.is_authenticated or not request.user.is_admin:
raise PermissionDenied(request)
queryObject = get_object_or_404(VerificationSetting,
query_object = get_object_or_404(VerificationSetting,
pk=kwargs["pk_verification"])
queryObject.archived = True
queryObject.description = "Telah dihapus pada " + \
query_object.archived = True
query_object.description = "Telah dihapus pada " + \
str(timezone.now().strftime("%m/%d/%Y, %H:%M:%S")) + " WIB"
queryObject.archived_by = request.user
queryObject.save()
query_object.archived_by = request.user
query_object.save()
messages.success(request, "Point verifikasi berhasil dihapus")
return HttpResponseRedirect("/administration/setting/verification/")
return HttpResponseRedirect(ADMINISTRATION_VERIFICATION_SETTING)
def delete_category(request, *args, **kwargs):
......@@ -499,16 +501,16 @@ def delete_category(request, *args, **kwargs):
raise PermissionDenied(request)
category_name = get_object_or_404(Category,
pk=kwargs["pk_category"]).name
queryObject = get_object_or_404(Category,
query_object = get_object_or_404(Category,
pk=kwargs["pk_category"])
queryObject.archived = True
queryObject.description = "Telah dihapus pada " + \
query_object.archived = True
query_object.description = "Telah dihapus pada " + \
str(timezone.now().strftime("%m/%d/%Y, %H:%M:%S")) + " WIB"
queryObject.archived_by = request.user
queryObject.save()
query_object.archived_by = request.user
query_object.save()
messages.success(request, "Kategori " +
category_name + " berhasil dihapus")
return HttpResponseRedirect("/administration/setting/category/")
return HttpResponseRedirect(ADMINISTRATION_CATEGORY_SETTING)
def generatedummy(request):
if request.user.is_authenticated is False or request.user.is_admin is False:
......
......@@ -10,7 +10,6 @@ def year_choices():
class UploadMateriForm(forms.ModelForm):
categories = forms.ModelMultipleChoiceField(queryset=Category.objects.all(),widget=forms.CheckboxSelectMultiple(attrs={'style' : 'column-count:2'}),required=True)
#categories.widget.attrs["style"] = "column-count:2"
release_year = forms.TypedChoiceField(coerce=int, choices=year_choices, initial=datetime.date.today().year)
yt_video_id = forms.CharField(label="Youtube Video Id", \
help_text="This is not required.<br>\
......
......@@ -24,7 +24,7 @@ class Command(BaseCommand):
counter = 0
category_name = f"Kategori dummy {counter}"
dummy_category = []
for i in range(num_of_category):
for _ in range(num_of_category):
while (Category.objects.filter(name=category_name).exists()):
counter += 1
category_name = f"Kategori dummy {counter}"
......@@ -38,7 +38,7 @@ class Command(BaseCommand):
counter = 0
criterion_name = f"Kriteria dummy {counter}"
dummy_criteria = []
for i in range(num_of_criteria):
for _ in range(num_of_criteria):
while (VerificationSetting.objects.filter(title=criterion_name).exists()):
counter += 1
criterion_name = f"Kriteria dummy {counter}"
......@@ -67,8 +67,7 @@ class Command(BaseCommand):
counter = 0
dummy_user = []
email = f"admin-dummy-{counter}@email.com"
end_date = timezone.now()
for i in range(num_of_user):
for _ in range(num_of_user):
while (User.objects.filter(email=email).exists()):
counter += 1
email = f"admin-dummy-{counter}@email.com"
......@@ -140,8 +139,6 @@ class Command(BaseCommand):
verif_report = VerificationReport(
report=report_field, materi=materi, user=admin, status=materi.get_status_display(), timestamp=timestamp)
verif_report.save()
else:
pass
return materi
......
......@@ -8,7 +8,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile
from django.utils import timezone
from app.models import Materi, Comment, Like, DownloadStatistics, ViewStatistics, DummyLike, DummyViewStatistics, DummyDownloadStatistics, DummyComment
from app.management.commands.utils import SECONDS_IN_DAY, get_time_before, get_random_datetime, generate_list_of_random_datetime, generate_random_string, getRandomColor, getLoremWithLength
from app.management.commands.utils import SECONDS_IN_DAY, get_time_before, get_random_datetime, generate_list_of_random_datetime, generate_random_string, get_random_color, get_lorem_with_length
class Command(BaseCommand):
......@@ -50,7 +50,7 @@ class Command(BaseCommand):
def _comment_materi(self, timestamp, materi):
item = Comment(materi=materi, timestamp=timestamp,
profile=getRandomColor(), comment=getLoremWithLength(240))
profile=get_random_color(), comment=get_lorem_with_length(240))
item.save()
DummyComment(item=item).save()
......@@ -64,16 +64,7 @@ class Command(BaseCommand):
s_date = materi_published_date[0]
s_date = s_date.replace(day=s_date.day+1, hour=0,
minute=0, second=0, microsecond=0)
# reports = []
while (s_date < today):
# report = {
# "s_date": s_date,
# "visit": 0,
# "view": 0,
# "download": 0,
# "like": 0,
# "comment": 0,
# }
today_materi = [i for i in materi if i.published_date < s_date]
visiting_user = options["baseline"]
visiting_user += int(options["coef_time"] * r_day) + \
......@@ -82,8 +73,6 @@ class Command(BaseCommand):
(1 + uniform(-options["coef_visit_range"],
options["coef_visit_range"])))
active_user = int(visiting_user * options["coef_read"])
# report["visit"] = visiting_user
# report["view"] = active_user
times = generate_list_of_random_datetime(
s_date, s_date+timedelta(days=1), active_user)
for timestamp in times:
......@@ -91,23 +80,9 @@ class Command(BaseCommand):
self._view_materi(timestamp, selected_materi)
if random() < options["coef_download"]:
self._download_materi(timestamp, selected_materi)
# report["download"] += 1
if random() < options["coef_like"]:
self._like_materi(timestamp, selected_materi)
# report["like"] += 1
if random() < options["coef_comment"]:
self._comment_materi(timestamp, selected_materi)
# report["comment"] += 1
s_date = s_date + timedelta(days=1)
r_day += 1
# reports.append(report)
# for i in range
# for i in reports:
# self.stdout.write(self.style.SUCCESS(f"Today is {i['s_date']}"))
# self.stdout.write(self.style.SUCCESS(f"User visit {i['visit']}"))
# self.stdout.write(self.style.SUCCESS(f"User view {i['view']}"))
# self.stdout.write(self.style.SUCCESS(
# f"User download {i['download']}"))
# self.stdout.write(self.style.SUCCESS(f"User like {i['like']}"))
# self.stdout.write(self.style.SUCCESS(
# f"User comment {i['comment']}"))
......@@ -8,7 +8,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile
from django.utils import timezone, lorem_ipsum
from app.models import DummyLike, DummyViewStatistics, DummyDownloadStatistics, DummyComment
from app.management.commands.utils import SECONDS_IN_DAY, get_time_before, get_random_datetime, generate_list_of_random_datetime, generate_random_string, getRandomColor
from app.management.commands.utils import SECONDS_IN_DAY, get_time_before, get_random_datetime, generate_list_of_random_datetime, generate_random_string, get_random_color
class Command(BaseCommand):
......
......@@ -9,12 +9,12 @@ from django.utils import timezone, lorem_ipsum
SECONDS_IN_DAY = 86400
def getRandomColor():
def get_random_color():
color = "%06x" % randint(0, 0xFFFFFF)
return color
def getLoremWithLength(n):
def get_lorem_with_length(n):
while True:
s = lorem_ipsum.sentence()
if len(s) < n:
......@@ -43,7 +43,7 @@ def get_random_datetime(start_date, end_date, max_delta_seconds=None, min_delta_
def generate_list_of_random_datetime(start, end, n):
res = []
for i in range(n):
for _ in range(n):
res.append(get_random_datetime(start, end))
res.sort()
return res
......@@ -56,4 +56,4 @@ def get_last_year():
def generate_random_string(n):
return(''.join(choice(ascii_letters) for i in range(n)))
return(''.join(choice(ascii_letters) for _ in range(n)))
......@@ -14,6 +14,6 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='comment',
name='profile',
field=models.CharField(default=app.models.getRandomColor, max_length=100),
field=models.CharField(default=app.models.get_random_color, max_length=100),
),
]
......@@ -20,7 +20,7 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('username', models.CharField(max_length=100)),
('profile', models.CharField(default=app.models.getRandomColor, max_length=100)),
('profile', models.CharField(default=app.models.get_random_color, max_length=100)),
('review', models.TextField(default='review')),
('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
('materi', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.materi')),
......
# Generated by Django 3.1 on 2020-10-30 13:26
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('app', '0027_auto_20201030_1648'),
]
operations = [
migrations.CreateModel(
name='ReadLater',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
('materi', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.materi')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'unique_together': {('materi', 'user')},
},
),
]
......@@ -21,7 +21,7 @@ VERIFICATION_STATUS = [
# Create your models here.
def getRandomColor():
def get_random_color():
color = "%06x" % random.randint(0, 0xFFFFFF)
return color
......@@ -140,11 +140,12 @@ class Materi(SoftDeleteModel):
like = False
if Like.objects.filter(materi=self).exists():
like = True
return like
class Comment(models.Model):
username = models.CharField(max_length=100)
profile = models.CharField(max_length=100, default=getRandomColor)
profile = models.CharField(max_length=100, default=get_random_color)
comment = models.CharField(max_length=240, default="comments")
materi = models.ForeignKey(Materi, models.SET_NULL, null=True)
user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)
......@@ -165,7 +166,7 @@ class Comment(models.Model):
class Review(models.Model):
username = models.CharField(max_length=100)
profile = models.CharField(max_length=100, default=getRandomColor)
profile = models.CharField(max_length=100, default=get_random_color)
review = models.TextField(default="review")
materi = models.ForeignKey(Materi, models.SET_NULL, null=True)
user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)
......@@ -272,3 +273,11 @@ class LaporanMateri(models.Model):
laporan = models.TextField(validators=[MinValueValidator(30), MaxValueValidator(120)], default="")
timestamp = models.DateTimeField(default=timezone.now)
is_rejected = models.BooleanField(default=False)
class ReadLater(models.Model):
materi = models.ForeignKey(Materi, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
timestamp = models.DateTimeField(default=timezone.now)
class Meta:
unique_together = ["materi", "user"]
\ No newline at end of file
......@@ -15,7 +15,7 @@ from pydrive.drive import GoogleDrive
from administration.models import VerificationReport
from app.forms import SuntingProfilForm
from app.models import Category, Like, LikeComment, DislikeComment, Materi, Comment, Rating, DownloadStatistics, \
ViewStatistics
ViewStatistics, ReadLater
from app.utils.fileManagementUtil import get_random_filename, remove_image_exifdata
from digipus import settings
import requests
......@@ -343,7 +343,6 @@ class UploadMateriService:
@staticmethod
def validate_yt_video_url(value):
is_yt_id_valid = True
r = requests.get('http://www.youtube.com/watch?v='+value)
if "\"playabilityStatus\":{\"status\":\"ERROR\"" in r.text:
raise ValidationError("Invalid Youtube video ID")
......@@ -470,3 +469,18 @@ class GoogleDriveUploadService:
file1["title"] = title
print("title: %s, mimeType: %s" % (file1["title"], file1["mimeType"]))
file1.Upload()
class ReadLaterService:
@staticmethod
def toggle_read_later(materi_id, current_user):
materi = get_object_or_404(Materi, pk=materi_id)