diff --git a/administration/migrations/0001_initial.py b/administration/migrations/0001_initial.py index de7061bb5b822b1bdb73537ed6f3ed02053028ff..e9366599d40d7e904ee324478b2256d478b5cfbb 100644 --- a/administration/migrations/0001_initial.py +++ b/administration/migrations/0001_initial.py @@ -1,7 +1,9 @@ -# Generated by Django 3.0.3 on 2020-05-08 14:42 +# Generated by Django 3.1 on 2020-10-30 15:29 -import django.contrib.postgres.fields.jsonb +from django.conf import settings from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone class Migration(migrations.Migration): @@ -9,28 +11,40 @@ class Migration(migrations.Migration): initial = True dependencies = [ + ('app', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ + migrations.CreateModel( + name='VerificationSetting', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=250)), + ('description', models.TextField(default='')), + ('archived', models.BooleanField(default=False)), + ('archived_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ], + ), migrations.CreateModel( name='VerificationReport', fields=[ - ('id', models.AutoField(auto_created=True, - primary_key=True, serialize=False, verbose_name='ID')), - ('report', django.contrib.postgres.fields.jsonb.JSONField()), - ('timestamp', models.DateTimeField(auto_now_add=True)), - ('status', models.CharField(choices=[('PENDING', 'Diproses'), ('APPROVE', 'Diterima'), ( - 'DISAPPROVE', 'Ditolak'), ('REVISION', 'Perbaikan')], default='PENDING', max_length=30)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('report', models.JSONField()), + ('timestamp', models.DateTimeField(default=django.utils.timezone.now)), + ('status', models.CharField(choices=[('PENDING', 'Diproses'), ('APPROVE', 'Diterima'), ('DISAPPROVE', 'Ditolak'), ('REVISION', 'Perbaikan'), ('BLOCKED', 'Diblokir')], default='PENDING', max_length=30)), + ('materi', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.materi')), + ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), ], ), migrations.CreateModel( - name='VerificationSetting', + name='DeletionHistory', fields=[ - ('id', models.AutoField(auto_created=True, - primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(max_length=250)), - ('description', models.TextField(default='')), - ('archived', models.BooleanField(default=False)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('deleted_user_name', models.CharField(max_length=150)), + ('deleted_user_role', models.CharField(max_length=150)), + ('timestamp', models.DateTimeField(default=django.utils.timezone.now)), + ('deletor_admin', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), ], ), ] diff --git a/administration/migrations/0002_verificationreport_materi.py b/administration/migrations/0002_verificationreport_materi.py deleted file mode 100644 index a1b8db8f58940d8965b879efd43c80f06b4bca72..0000000000000000000000000000000000000000 --- a/administration/migrations/0002_verificationreport_materi.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.0.3 on 2020-05-08 14:42 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('app', '0001_initial'), - ('administration', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='verificationreport', - name='materi', - field=models.ForeignKey( - null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.Materi'), - ), - ] diff --git a/administration/migrations/0003_verificationreport_user.py b/administration/migrations/0003_verificationreport_user.py deleted file mode 100644 index 236da3cd510b6203fbca8812fbcf4a33dc0da15c..0000000000000000000000000000000000000000 --- a/administration/migrations/0003_verificationreport_user.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 3.0.3 on 2020-05-08 14:42 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('administration', '0002_verificationreport_materi'), - ] - - operations = [ - migrations.AddField( - model_name='verificationreport', - name='user', - field=models.ForeignKey( - null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/administration/migrations/0004_auto_20200517_1713.py b/administration/migrations/0004_auto_20200517_1713.py deleted file mode 100644 index 8189cd965de7bc2ddf6453176ec3ec75c07fc7f9..0000000000000000000000000000000000000000 --- a/administration/migrations/0004_auto_20200517_1713.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.0.3 on 2020-05-17 10:13 - -from django.db import migrations, models -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - ('administration', '0003_verificationreport_user'), - ] - - operations = [ - migrations.AlterField( - model_name='verificationreport', - name='timestamp', - field=models.DateTimeField(default=django.utils.timezone.now), - ), - ] diff --git a/administration/migrations/0005_deletionhistory.py b/administration/migrations/0005_deletionhistory.py deleted file mode 100644 index a23c37e1428e4b164cfbfa9b53a58b092cb63790..0000000000000000000000000000000000000000 --- a/administration/migrations/0005_deletionhistory.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 3.0.3 on 2020-06-03 12:57 - -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), - ('administration', '0004_auto_20200517_1713'), - ] - - operations = [ - migrations.CreateModel( - name='DeletionHistory', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('deleted_user_name', models.CharField(max_length=150)), - ('deleted_user_role', models.CharField(max_length=150)), - ('timestamp', models.DateTimeField(default=django.utils.timezone.now)), - ('deletor_admin', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), - ], - ), - ] diff --git a/administration/migrations/0005_verificationsetting_archived_by.py b/administration/migrations/0005_verificationsetting_archived_by.py deleted file mode 100644 index dadfd72ea51694f051a7730b60c37bf32210d354..0000000000000000000000000000000000000000 --- a/administration/migrations/0005_verificationsetting_archived_by.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 3.0.3 on 2020-06-04 00:09 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('administration', '0004_auto_20200517_1713'), - ] - - operations = [ - migrations.AddField( - model_name='verificationsetting', - name='archived_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/administration/migrations/0006_merge_20200604_0718.py b/administration/migrations/0006_merge_20200604_0718.py deleted file mode 100644 index 92c7b037550593a95bfd8bdd25312fc5568f1bd3..0000000000000000000000000000000000000000 --- a/administration/migrations/0006_merge_20200604_0718.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 3.0.3 on 2020-06-04 00:18 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('administration', '0005_deletionhistory'), - ('administration', '0005_verificationsetting_archived_by'), - ] - - operations = [ - ] diff --git a/administration/migrations/0007_auto_20200929_1218.py b/administration/migrations/0007_auto_20200929_1218.py deleted file mode 100644 index 735e71572df51f52e11ffe98a319fdceb3f46ba7..0000000000000000000000000000000000000000 --- a/administration/migrations/0007_auto_20200929_1218.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1 on 2020-09-29 05:18 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('administration', '0006_merge_20200604_0718'), - ] - - operations = [ - migrations.AlterField( - model_name='verificationreport', - name='report', - field=models.JSONField(), - ), - ] diff --git a/administration/migrations/0008_auto_20201009_1829.py b/administration/migrations/0008_auto_20201009_1829.py deleted file mode 100644 index 0302d90c1ccf01e6a7a1b313ed4ce7ee95c79573..0000000000000000000000000000000000000000 --- a/administration/migrations/0008_auto_20201009_1829.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1 on 2020-10-09 11:29 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('administration', '0007_auto_20200929_1218'), - ] - - operations = [ - migrations.AlterField( - model_name='verificationreport', - name='status', - field=models.CharField(choices=[('PENDING', 'Diproses'), ('APPROVE', 'Diterima'), ('DISAPPROVE', 'Ditolak'), ('REVISION', 'Perbaikan'), ('BLOCKED', 'Diblokir')], default='PENDING', max_length=30), - ), - ] diff --git a/app/migrations/0001_initial.py b/app/migrations/0001_initial.py index 51a62f576bc233af8f04a305c6be41ee5073e1fe..2e76e78466a5b35f01834a00102bee3798f0b177 100644 --- a/app/migrations/0001_initial.py +++ b/app/migrations/0001_initial.py @@ -1,6 +1,12 @@ -# Generated by Django 3.0.3 on 2020-05-08 14:42 +# Generated by Django 3.1 on 2020-10-30 15:28 +import app.models +from django.conf import settings +import django.contrib.postgres.search +import django.core.validators from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone class Migration(migrations.Migration): @@ -8,44 +14,215 @@ class Migration(migrations.Migration): initial = True dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name='Category', fields=[ - ('id', models.AutoField(auto_created=True, - primary_key=True, serialize=False, verbose_name='ID')), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=20)), - ('description', models.CharField(max_length=20)), + ('description', models.TextField(default='')), + ('archived', models.BooleanField(default=False)), + ('archived_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), ], ), migrations.CreateModel( name='Comment', fields=[ - ('id', models.AutoField(auto_created=True, - primary_key=True, serialize=False, verbose_name='ID')), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('username', models.CharField(max_length=100)), - ('profile', models.CharField(default='56acdf', max_length=100)), - ('comment', models.CharField(default='comments', max_length=150)), + ('profile', models.CharField(default=app.models.getRandomColor, max_length=100)), + ('comment', models.CharField(default='comments', max_length=240)), + ('timestamp', models.DateTimeField(default=django.utils.timezone.now)), + ], + ), + migrations.CreateModel( + name='DownloadStatistics', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('timestamp', models.DateTimeField(default=django.utils.timezone.now)), + ('downloader', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='riwayat_unduh', to=settings.AUTH_USER_MODEL)), ], ), migrations.CreateModel( name='Materi', fields=[ - ('id', models.AutoField(auto_created=True, - primary_key=True, serialize=False, verbose_name='ID')), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('deleted_at', models.DateTimeField(blank=True, null=True)), ('cover', models.ImageField(upload_to='')), ('content', models.FileField(upload_to='')), - ('title', models.CharField(default='title', max_length=50)), - ('author', models.CharField(default='author', max_length=30)), - ('publisher', models.CharField(default='publiser', max_length=30)), + ('title', models.CharField(default='Judul', max_length=50)), + ('author', models.CharField(default='Penyusun', max_length=30)), + ('publisher', models.CharField(default='Penerbit', max_length=30)), + ('release_year', models.IntegerField(default=app.models.current_year)), ('pages', models.IntegerField(default=0)), - ('descriptions', models.TextField(default='descriptions')), - ('status', models.CharField(choices=[('PENDING', 'Diproses'), ('APPROVE', 'Diterima'), ( - 'DISAPPROVE', 'Ditolak'), ('REVISION', 'Perbaikan')], default='PENDING', max_length=30)), - ('date_added', models.DateTimeField(auto_now_add=True)), + ('descriptions', models.TextField(default='Deskripsi')), + ('status', models.CharField(choices=[('PENDING', 'Diproses'), ('APPROVE', 'Diterima'), ('DISAPPROVE', 'Ditolak'), ('REVISION', 'Perbaikan'), ('BLOCKED', 'Diblokir')], default='PENDING', max_length=30)), + ('date_created', models.DateTimeField(default=django.utils.timezone.now)), + ('date_modified', models.DateTimeField(auto_now=True)), + ('yt_video_id', models.CharField(blank=True, max_length=100, null=True)), + ('_search_vector', django.contrib.postgres.search.SearchVectorField(editable=False, null=True)), ('categories', models.ManyToManyField(to='app.Category')), + ('uploader', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='ReqMaterial', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=100)), + ('timestamp', models.DateTimeField(default=django.utils.timezone.now)), + ], + ), + migrations.CreateModel( + name='SubmitVisitor', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('user_id', models.CharField(max_length=50)), + ('email', models.CharField(max_length=50)), + ('msg', models.CharField(max_length=100)), + ('timestamp', models.DateTimeField(default=django.utils.timezone.now)), + ], + ), + migrations.CreateModel( + name='SubscribeModel', + fields=[ + ('sys_id', models.AutoField(primary_key=True, serialize=False)), + ('email', models.EmailField(blank=True, max_length=200, unique=True)), + ('status', models.CharField(blank=True, max_length=64)), + ('created_date', models.DateTimeField(blank=True)), + ('updated_date', models.DateTimeField(blank=True)), + ], + options={ + 'db_table': 'app_subscribe', + }, + ), + migrations.CreateModel( + name='ViewStatistics', + 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(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='baca', to='app.materi')), + ], + ), + migrations.CreateModel( + name='Review', + 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)), + ('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')), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='RatingContributor', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('timestamp', models.DateTimeField(auto_now=True)), + ('score', models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(5)])), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='LikeComment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('timestamp', models.DateTimeField(default=django.utils.timezone.now)), + ('session_id', models.CharField(max_length=32)), + ('comment', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.comment')), + ], + ), + migrations.CreateModel( + name='Like', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('timestamp', models.DateTimeField(default=django.utils.timezone.now)), + ('session_id', models.CharField(max_length=32)), + ('materi', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.materi')), + ], + ), + migrations.CreateModel( + name='LaporanMateri', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('laporan', models.TextField(default='', validators=[django.core.validators.MinValueValidator(30), django.core.validators.MaxValueValidator(120)])), + ('timestamp', models.DateTimeField(default=django.utils.timezone.now)), + ('is_rejected', models.BooleanField(default=False)), + ('materi', models.ForeignKey(max_length=120, 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)), + ], + ), + migrations.CreateModel( + name='DummyViewStatistics', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.viewstatistics')), + ], + ), + migrations.CreateModel( + name='DummyLike', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.like')), + ], + ), + migrations.CreateModel( + name='DummyDownloadStatistics', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.downloadstatistics')), + ], + ), + migrations.CreateModel( + name='DummyComment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.comment')), + ], + ), + migrations.AddField( + model_name='downloadstatistics', + name='materi', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='unduh', to='app.materi'), + ), + migrations.CreateModel( + name='DislikeComment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('timestamp', models.DateTimeField(default=django.utils.timezone.now)), + ('session_id', models.CharField(max_length=32)), + ('comment', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.comment')), + ], + ), + migrations.AddField( + model_name='comment', + name='materi', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.materi'), + ), + migrations.AddField( + model_name='comment', + name='user', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + ), + migrations.CreateModel( + name='Rating', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('timestamp', models.DateTimeField(default=django.utils.timezone.now)), + ('score', models.IntegerField()), + ('materi', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.materi')), + ('user', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], + options={ + 'unique_together': {('materi', 'user')}, + }, ), ] diff --git a/app/migrations/0002_auto_20200508_2142.py b/app/migrations/0002_auto_20200508_2142.py deleted file mode 100644 index dfeeb8495f7ea4ab0e77001b5dec910341522e0e..0000000000000000000000000000000000000000 --- a/app/migrations/0002_auto_20200508_2142.py +++ /dev/null @@ -1,36 +0,0 @@ -# Generated by Django 3.0.3 on 2020-05-08 14:42 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('app', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='materi', - name='uploader', - field=models.ForeignKey( - null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='comment', - name='materi', - field=models.ForeignKey( - null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.Materi'), - ), - migrations.AddField( - model_name='comment', - name='user', - field=models.ForeignKey( - blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/app/migrations/0003_auto_20200509_2108.py b/app/migrations/0003_auto_20200509_2108.py deleted file mode 100644 index 844d9d85ad27f5f5b3102597c4e8e8ef6173f8fd..0000000000000000000000000000000000000000 --- a/app/migrations/0003_auto_20200509_2108.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.0.3 on 2020-05-09 14:08 - -import app.models -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0002_auto_20200508_2142'), - ] - - operations = [ - migrations.AlterField( - model_name='comment', - name='profile', - field=models.CharField(default=app.models.getRandomColor, max_length=100), - ), - ] diff --git a/app/migrations/0004_like.py b/app/migrations/0004_like.py deleted file mode 100644 index 7240ea7ac31c62b66b4ef2cdcd9790b2f6d2e143..0000000000000000000000000000000000000000 --- a/app/migrations/0004_like.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 3.0.3 on 2020-05-12 08:34 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0003_auto_20200509_2108'), - ] - - operations = [ - migrations.CreateModel( - name='Like', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('timestamp', models.DateTimeField(auto_now_add=True)), - ('materi', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.Materi')), - ], - ), - ] diff --git a/app/migrations/0005_like_session_id.py b/app/migrations/0005_like_session_id.py deleted file mode 100644 index 8520a4d3769e9bf68d9ed2fad52454391df55777..0000000000000000000000000000000000000000 --- a/app/migrations/0005_like_session_id.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.0.3 on 2020-05-12 09:24 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0004_like'), - ] - - operations = [ - migrations.AddField( - model_name='like', - name='session_id', - field=models.CharField(default='', max_length=32), - preserve_default=False, - ), - ] diff --git a/app/migrations/0006_downloadstatistics_viewstatistics.py b/app/migrations/0006_downloadstatistics_viewstatistics.py deleted file mode 100644 index 2b29fb125a53831e97d5888d6d878894dcd580d3..0000000000000000000000000000000000000000 --- a/app/migrations/0006_downloadstatistics_viewstatistics.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 3.0.3 on 2020-05-13 10:34 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0005_like_session_id'), - ] - - operations = [ - migrations.CreateModel( - name='ViewStatistics', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('timestamp', models.DateTimeField(auto_now_add=True)), - ('materi', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='baca', to='app.Materi')), - ], - ), - migrations.CreateModel( - name='DownloadStatistics', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('timestamp', models.DateTimeField(auto_now_add=True)), - ('materi', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='unduh', to='app.Materi')), - ], - ), - ] diff --git a/app/migrations/0007_auto_20200516_1743.py b/app/migrations/0007_auto_20200516_1743.py deleted file mode 100644 index 5a0973432393117a5aa0435217f0244c49a9cfe3..0000000000000000000000000000000000000000 --- a/app/migrations/0007_auto_20200516_1743.py +++ /dev/null @@ -1,43 +0,0 @@ -# Generated by Django 3.0.3 on 2020-05-16 10:43 - -from django.db import migrations, models -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0006_downloadstatistics_viewstatistics'), - ] - - operations = [ - migrations.RemoveField( - model_name='materi', - name='date_added', - ), - migrations.AddField( - model_name='materi', - name='date_created', - field=models.DateTimeField(default=django.utils.timezone.now), - ), - migrations.AddField( - model_name='materi', - name='date_modified', - field=models.DateTimeField(auto_now=True), - ), - migrations.AlterField( - model_name='downloadstatistics', - name='timestamp', - field=models.DateTimeField(default=django.utils.timezone.now), - ), - migrations.AlterField( - model_name='like', - name='timestamp', - field=models.DateTimeField(default=django.utils.timezone.now), - ), - migrations.AlterField( - model_name='viewstatistics', - name='timestamp', - field=models.DateTimeField(default=django.utils.timezone.now), - ), - ] diff --git a/app/migrations/0008_auto_20200518_1919.py b/app/migrations/0008_auto_20200518_1919.py deleted file mode 100644 index df20b16663a25a3d064ff3f30b01012f1ecf150c..0000000000000000000000000000000000000000 --- a/app/migrations/0008_auto_20200518_1919.py +++ /dev/null @@ -1,48 +0,0 @@ -# Generated by Django 3.0.3 on 2020-05-18 12:19 - -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0007_auto_20200516_1743'), - ] - - operations = [ - migrations.AddField( - model_name='comment', - name='timestamp', - field=models.DateTimeField(default=django.utils.timezone.now), - ), - migrations.CreateModel( - name='DummyViewStatistics', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.ViewStatistics')), - ], - ), - migrations.CreateModel( - name='DummyLike', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.Like')), - ], - ), - migrations.CreateModel( - name='DummyDownloadStatistics', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.DownloadStatistics')), - ], - ), - migrations.CreateModel( - name='DummyComment', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.Comment')), - ], - ), - ] diff --git a/app/migrations/0009_auto_20200518_2245.py b/app/migrations/0009_auto_20200518_2245.py deleted file mode 100644 index 68785da21a33971eced0e5cf01c37c9e350a56d9..0000000000000000000000000000000000000000 --- a/app/migrations/0009_auto_20200518_2245.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.0.3 on 2020-05-18 15:45 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0008_auto_20200518_1919'), - ] - - operations = [ - migrations.AlterField( - model_name='comment', - name='comment', - field=models.CharField(default='comments', max_length=240), - ), - ] diff --git a/app/migrations/0010_category_archived.py b/app/migrations/0010_category_archived.py deleted file mode 100644 index a864e59965a382bdc242de2970ab9cc7b76de822..0000000000000000000000000000000000000000 --- a/app/migrations/0010_category_archived.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.0.3 on 2020-06-03 06:48 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0009_auto_20200518_2245'), - ] - - operations = [ - migrations.AddField( - model_name='category', - name='archived', - field=models.BooleanField(default=False), - ), - ] diff --git a/app/migrations/0011_auto_20200603_1350.py b/app/migrations/0011_auto_20200603_1350.py deleted file mode 100644 index 14c08847378c0a4a310c77303a176bd5182ba90b..0000000000000000000000000000000000000000 --- a/app/migrations/0011_auto_20200603_1350.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.0.3 on 2020-06-03 06:50 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0010_category_archived'), - ] - - operations = [ - migrations.AlterField( - model_name='category', - name='description', - field=models.TextField(default=''), - ), - ] diff --git a/app/migrations/0012_category_archived_by.py b/app/migrations/0012_category_archived_by.py deleted file mode 100644 index 9919bf0538e111bef9794eeb7ab32517e3cb65e7..0000000000000000000000000000000000000000 --- a/app/migrations/0012_category_archived_by.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 3.0.3 on 2020-06-04 00:09 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('app', '0011_auto_20200603_1350'), - ] - - operations = [ - migrations.AddField( - model_name='category', - name='archived_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/app/migrations/0013_auto_20200919_2055.py b/app/migrations/0013_auto_20200919_2055.py deleted file mode 100644 index 7b7993c96f79f86abbf68a24f2c520e98ad3882a..0000000000000000000000000000000000000000 --- a/app/migrations/0013_auto_20200919_2055.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 3.0.3 on 2020-09-19 13:55 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0012_category_archived_by'), - ] - - operations = [ - migrations.AlterField( - model_name='materi', - name='author', - field=models.CharField(default='Penyusun', max_length=30), - ), - migrations.AlterField( - model_name='materi', - name='descriptions', - field=models.TextField(default='Deskripsi'), - ), - migrations.AlterField( - model_name='materi', - name='publisher', - field=models.CharField(default='Penerbit', max_length=30), - ), - migrations.AlterField( - model_name='materi', - name='title', - field=models.CharField(default='Judul', max_length=50), - ), - ] diff --git a/app/migrations/0014_rating.py b/app/migrations/0014_rating.py deleted file mode 100644 index 69ae6aa1e7dcd3345443a22672118f591ef8e680..0000000000000000000000000000000000000000 --- a/app/migrations/0014_rating.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 3.0.3 on 2020-09-29 11:30 - -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', '0013_auto_20200919_2055'), - ] - - operations = [ - migrations.CreateModel( - name='Rating', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('timestamp', models.DateTimeField(default=django.utils.timezone.now)), - ('score', models.IntegerField()), - ('materi', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.Materi')), - ('user', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - options={ - 'unique_together': {('materi', 'user')}, - }, - ), - ] diff --git a/app/migrations/0015_downloadstatistics_downloader.py b/app/migrations/0015_downloadstatistics_downloader.py deleted file mode 100644 index 16780d0c070064f852acf2e180dfcb1ba5179afc..0000000000000000000000000000000000000000 --- a/app/migrations/0015_downloadstatistics_downloader.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 3.0.4 on 2020-09-30 04:50 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('app', '0014_rating'), - ] - - operations = [ - migrations.AddField( - model_name='downloadstatistics', - name='downloader', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='riwayat_unduh', to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/app/migrations/0015_reqmaterial.py b/app/migrations/0015_reqmaterial.py deleted file mode 100644 index 197e5a18d1dbb8cc0daf3436660d9667ee421939..0000000000000000000000000000000000000000 --- a/app/migrations/0015_reqmaterial.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 3.0.3 on 2020-10-01 04:23 - -from django.db import migrations, models -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0014_rating'), - ] - - operations = [ - migrations.CreateModel( - name='ReqMaterial', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(max_length=100)), - ('timestamp', models.DateTimeField(default=django.utils.timezone.now)), - ], - ), - ] diff --git a/app/migrations/0016_ratingcontributor.py b/app/migrations/0016_ratingcontributor.py deleted file mode 100644 index 86d787f0b6a357456c259f9842dfd7e3b7e49415..0000000000000000000000000000000000000000 --- a/app/migrations/0016_ratingcontributor.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 3.1.1 on 2020-10-05 14:41 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('app', '0015_reqmaterial'), - ] - - operations = [ - migrations.CreateModel( - name='RatingContributor', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('timestamp', models.DateTimeField(auto_now=True)), - ('score', models.IntegerField()), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - ), - ] diff --git a/app/migrations/0017_auto_20201005_2145.py b/app/migrations/0017_auto_20201005_2145.py deleted file mode 100644 index 732caef72a1b44acef70e15c4d29b91a5df106b2..0000000000000000000000000000000000000000 --- a/app/migrations/0017_auto_20201005_2145.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.1.1 on 2020-10-05 14:45 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0016_ratingcontributor'), - ] - - operations = [ - migrations.AlterField( - model_name='ratingcontributor', - name='score', - field=models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(5)]), - ), - ] diff --git a/app/migrations/0018_merge_20201009_0700.py b/app/migrations/0018_merge_20201009_0700.py deleted file mode 100644 index 2e5d56480fa1f304956819f4012e5e52c2100d5e..0000000000000000000000000000000000000000 --- a/app/migrations/0018_merge_20201009_0700.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 3.1 on 2020-10-09 00:00 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0017_auto_20201005_2145'), - ('app', '0015_downloadstatistics_downloader'), - ] - - operations = [ - ] diff --git a/app/migrations/0019_auto_20201009_1829.py b/app/migrations/0019_auto_20201009_1829.py deleted file mode 100644 index 15bec11ed71a0b3062e68aeceda2e7434ec804e8..0000000000000000000000000000000000000000 --- a/app/migrations/0019_auto_20201009_1829.py +++ /dev/null @@ -1,34 +0,0 @@ -# Generated by Django 3.1 on 2020-10-09 11:29 - -from django.conf import settings -import django.core.validators -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', '0018_merge_20201009_0700'), - ] - - operations = [ - migrations.AlterField( - model_name='materi', - name='status', - field=models.CharField(choices=[('PENDING', 'Diproses'), ('APPROVE', 'Diterima'), ('DISAPPROVE', 'Ditolak'), ('REVISION', 'Perbaikan'), ('BLOCKED', 'Diblokir')], default='PENDING', max_length=30), - ), - migrations.CreateModel( - name='LaporanMateri', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('laporan', models.TextField(default='', validators=[django.core.validators.MinValueValidator(30), django.core.validators.MaxValueValidator(120)])), - ('timestamp', models.DateTimeField(default=django.utils.timezone.now)), - ('is_rejected', models.BooleanField(default=False)), - ('materi', models.ForeignKey(max_length=120, 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)), - ], - ), - ] diff --git a/app/migrations/0019_materi__search_vector.py b/app/migrations/0019_materi__search_vector.py deleted file mode 100644 index bd148040bd07b733963274efadcb1ad9995396c7..0000000000000000000000000000000000000000 --- a/app/migrations/0019_materi__search_vector.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.1 on 2020-10-09 11:19 - -import django.contrib.postgres.search -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0018_merge_20201009_0700'), - ] - - operations = [ - migrations.AddField( - model_name='materi', - name='_search_vector', - field=django.contrib.postgres.search.SearchVectorField(editable=False, null=True), - ), - ] diff --git a/app/migrations/0019_materi_yt_video_id.py b/app/migrations/0019_materi_yt_video_id.py deleted file mode 100644 index df8dc1afeba6a410f437c1ce8c29de0e0c947346..0000000000000000000000000000000000000000 --- a/app/migrations/0019_materi_yt_video_id.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1 on 2020-10-09 13:48 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0018_merge_20201009_0700'), - ] - - operations = [ - migrations.AddField( - model_name='materi', - name='yt_video_id', - field=models.CharField(blank=True, max_length=100, null=True), - ), - ] diff --git a/app/migrations/0020_merge_20201009_2039.py b/app/migrations/0020_merge_20201009_2039.py deleted file mode 100644 index 80867b028075f48fb2cd1b5838561fe9b20aa4cc..0000000000000000000000000000000000000000 --- a/app/migrations/0020_merge_20201009_2039.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 3.1 on 2020-10-09 13:39 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0019_materi__search_vector'), - ('app', '0019_auto_20201009_1829'), - ] - - operations = [ - ] diff --git a/app/migrations/0021_dislikecomment_likecomment.py b/app/migrations/0021_dislikecomment_likecomment.py deleted file mode 100644 index 1ce9111268da53f8e2a88fdf877c4250fb354695..0000000000000000000000000000000000000000 --- a/app/migrations/0021_dislikecomment_likecomment.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 3.1 on 2020-10-09 16:19 - -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0020_merge_20201009_2039'), - ] - - operations = [ - migrations.CreateModel( - name='LikeComment', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('timestamp', models.DateTimeField(default=django.utils.timezone.now)), - ('session_id', models.CharField(max_length=32)), - ('comment', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.comment')), - ], - ), - migrations.CreateModel( - name='DislikeComment', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('timestamp', models.DateTimeField(default=django.utils.timezone.now)), - ('session_id', models.CharField(max_length=32)), - ('comment', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.comment')), - ], - ), - ] diff --git a/app/migrations/0021_materi_release_year.py b/app/migrations/0021_materi_release_year.py deleted file mode 100644 index 8294e44629dc026ddff721b329e610946de3a7d5..0000000000000000000000000000000000000000 --- a/app/migrations/0021_materi_release_year.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.1 on 2020-10-09 16:13 - -import app.models -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0020_merge_20201009_2039'), - ] - - operations = [ - migrations.AddField( - model_name='materi', - name='release_year', - field=models.IntegerField(default=app.models.current_year), - ), - ] diff --git a/app/migrations/0022_merge_20201011_1122.py b/app/migrations/0022_merge_20201011_1122.py deleted file mode 100644 index 4b0d5ebf263822713c41cced80792494eb749a21..0000000000000000000000000000000000000000 --- a/app/migrations/0022_merge_20201011_1122.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 3.1 on 2020-10-11 04:22 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0021_dislikecomment_likecomment'), - ('app', '0021_materi_release_year'), - ] - - operations = [ - ] diff --git a/app/migrations/0023_materi_deleted_at.py b/app/migrations/0023_materi_deleted_at.py deleted file mode 100644 index 2a695fcfe8cfa105ee5b2b04ba7b163460267b26..0000000000000000000000000000000000000000 --- a/app/migrations/0023_materi_deleted_at.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1 on 2020-10-23 03:23 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0022_merge_20201011_1122'), - ] - - operations = [ - migrations.AddField( - model_name='materi', - name='deleted_at', - field=models.DateTimeField(blank=True, null=True), - ), - ] diff --git a/app/migrations/0023_merge_20201016_1713.py b/app/migrations/0023_merge_20201016_1713.py deleted file mode 100644 index ecb8c7929320e2599b38912d405bce12532e23a5..0000000000000000000000000000000000000000 --- a/app/migrations/0023_merge_20201016_1713.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 3.1 on 2020-10-16 10:13 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0022_merge_20201011_1122'), - ('app', '0019_materi_yt_video_id'), - ] - - operations = [ - ] diff --git a/app/migrations/0024_merge_20201026_0812.py b/app/migrations/0024_merge_20201026_0812.py deleted file mode 100644 index adc2f2f6bb36bb959b15608d753516fed53bf5df..0000000000000000000000000000000000000000 --- a/app/migrations/0024_merge_20201026_0812.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 3.1 on 2020-10-26 01:12 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0023_merge_20201016_1713'), - ('app', '0023_materi_deleted_at'), - ] - - operations = [ - ] diff --git a/app/migrations/0025_review.py b/app/migrations/0025_review.py deleted file mode 100644 index 79b7b89a4f655cf6851c7265539222047eef56e1..0000000000000000000000000000000000000000 --- a/app/migrations/0025_review.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 3.1 on 2020-10-29 11:22 - -import app.models -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', '0024_merge_20201026_0812'), - ] - - operations = [ - migrations.CreateModel( - name='Review', - 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)), - ('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')), - ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), - ], - ), - ] diff --git a/app/migrations/0026_submitvisitor.py b/app/migrations/0026_submitvisitor.py deleted file mode 100644 index b8497c75b257983dd84ea6b3cbc8b5228f6cd29d..0000000000000000000000000000000000000000 --- a/app/migrations/0026_submitvisitor.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 3.1 on 2020-10-29 13:45 - -from django.db import migrations, models -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0025_review'), - ] - - operations = [ - migrations.CreateModel( - name='SubmitVisitor', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('user_id', models.CharField(max_length=50)), - ('email', models.CharField(max_length=50)), - ('msg', models.CharField(max_length=100)), - ('timestamp', models.DateTimeField(default=django.utils.timezone.now)), - ], - ), - ] diff --git a/app/models.py b/app/models.py index e531ad121c188ab50d73c93dfbedd3eafb464389..661cde6be8d78d8b5d785ab31b34c8764c7ecd18 100644 --- a/app/models.py +++ b/app/models.py @@ -47,7 +47,7 @@ class MateriManager(models.Manager): if self.alive_only: return SoftDeletionQuerySet(self.model).filter(deleted_at=None) return SoftDeletionQuerySet(self.model) - + def search(self, search_text): search_vector = search.SearchVector("title", weight="A") search_query = search.SearchQuery(search_text) @@ -68,10 +68,10 @@ class SoftDeleteModel(models.Model): deleted_at = models.DateTimeField(blank=True, null=True) all_objects = MateriManager(alive_only=False) - + class Meta: abstract = True - + def soft_delete(self): self.deleted_at = timezone.now() self.save() @@ -85,7 +85,7 @@ class Materi(SoftDeleteModel): publisher = models.CharField(max_length=30, default="Penerbit") release_year = models.IntegerField(default=current_year) pages = models.IntegerField(default=0) - descriptions = models.TextField(default="Deskripsi") + descriptions = models.TextField(default="Deskripsi") status = models.CharField(max_length=30, choices=VERIFICATION_STATUS, default=VERIFICATION_STATUS[0][0]) categories = models.ManyToManyField(Category) date_created = models.DateTimeField(default=timezone.now) @@ -124,7 +124,7 @@ class Materi(SoftDeleteModel): def like_count(self): count = Like.objects.filter(materi=self).count() return count - + @property def comment_count(self): count = Comment.objects.filter(materi=self).count() @@ -157,7 +157,7 @@ class Comment(models.Model): def like_count(self): count = LikeComment.objects.filter(comment=self).count() return count - + @property def dislike_count(self): count = DislikeComment.objects.filter(comment=self).count() @@ -265,6 +265,19 @@ class RatingContributor(models.Model): class Meta: unique_together = ["contributor", "user"] +class SubscribeModel(models.Model): + sys_id = models.AutoField(primary_key=True, null=False, blank=True) + email = models.EmailField(null=False, blank=True, max_length=200, unique=True) + status = models.CharField(max_length=64, null=False, blank=True) + created_date = models.DateTimeField(null=False, blank=True) + updated_date = models.DateTimeField(null=False, blank=True) + + class Meta: + app_label = "app" + db_table = "app_subscribe" + + def __str__(self): + return self.email class LaporanMateri(models.Model): materi = models.ForeignKey(Materi, on_delete=models.CASCADE, max_length=120) diff --git a/app/static/images/sub-btn.png b/app/static/images/sub-btn.png new file mode 100644 index 0000000000000000000000000000000000000000..81b0489c7c988c9769980e0bca8698a24cc3851d Binary files /dev/null and b/app/static/images/sub-btn.png differ diff --git a/app/templates/app/emails/subscription.html b/app/templates/app/emails/subscription.html new file mode 100644 index 0000000000000000000000000000000000000000..6a83c2d0a33ee439882c3b3008ce091db597ee62 --- /dev/null +++ b/app/templates/app/emails/subscription.html @@ -0,0 +1,20 @@ +<div style="padding: 20px; background: #fafafa;font-size:15px;"> + Hi<br> + Thanks for subscribing to {{ project_name }} newsletter.<br> + We will be sending you latest published articles on <a href="{{ site_url }}">{{ site_url }}</a>. Mail frequency won't be more than twice a month.<br> + We hate spamming as much as you do.<br> + <br> + To confirm your subscription, please click on the link given below. If clicking doesn't work, copy paste the URL in browser.<br> + If you think this is a mistake, just ignore this email and we won't bother you again. + <br> + <br> + <a href="{{ confirmation_url }}">{{ confirmation_url }}</a> + + <br> + <br> + <p> + Note:<br> + This is notification only email. Please do not reply on this email.<br> + You can <a href="{{ contact_us_url }}">contact us here</a>. + </p> +</div> \ No newline at end of file diff --git a/app/templates/app/katalog_materi.html b/app/templates/app/katalog_materi.html index 224232c1a609e82a1004dd0183bd6ac83059cd48..93c719b560ad93d582913d20205b0d125f799565 100644 --- a/app/templates/app/katalog_materi.html +++ b/app/templates/app/katalog_materi.html @@ -48,7 +48,7 @@ </head> <body style="background-color: #f8f8f8;"> - + <!-- Page Content --> <div class="container"> @@ -76,14 +76,14 @@ </div> </div> </header> - + <a href="/download-history/" class="btn-history">Riwayat Unduh</a><br><br> <div id="container-materi-ordering"> <a href="?random=1" class="btn-materi-ordering">Acak Materi</a><br><br> <a href="?recommendation=1" class="btn-materi-ordering">Rekomendasi</a><br><br> </div> - + <div class="container"> <div class="row content"> <div class="col-3 sidebar"> @@ -209,4 +209,4 @@ </html> -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/app/templates/subscribe.html b/app/templates/subscribe.html new file mode 100644 index 0000000000000000000000000000000000000000..566776b36cbc0b63a6e9c57cd980f7a8220b6cec --- /dev/null +++ b/app/templates/subscribe.html @@ -0,0 +1,12 @@ +<div style="border: 1px solid darkgreen; border-radius: 2px; padding:10px; text-align: center;"> + <div><strong>SUBSCRIBE</strong></div> + <div style="margin-bottom: 10px;">Please subscribe to get the latest articles in your mailbox.</div> + <div> + <form action="{% url 'subscribe' %}" method="post" class="form-horizontal"> + {% csrf_token %} + <input type="email" name="email" class="form-control" placeholder="Your Email ID Please" required> + <br> + <input type="submit" value="Subscribe" class="btn btn-primary btn-sm"> + </form> + </div> +</div> \ No newline at end of file diff --git a/app/tests.py b/app/tests.py index 9f10c73f27f528bd814fb834b1084391f60514d9..2a0a6e069a9d9297915b0907ee1b1b932c5b6e6a 100644 --- a/app/tests.py +++ b/app/tests.py @@ -67,6 +67,9 @@ from .views import ( ) from app.forms import SuntingProfilForm, year_choices from app.utils.fileManagementUtil import get_random_filename, remove_image_exifdata +import pandas as pd +from django.core import mail +from app.utils.email_utility import send_gmail from app.utils.PasswordValidator import PasswordPolicyValidator ERROR_403_MESSAGE = "Kamu harus login untuk mengakses halaman ini" @@ -226,7 +229,7 @@ class DaftarKatalogSortingByJumlahTampilanTest(TestCase): self.client.get(self.url) response = self.client.get("/?sort=jumlah_tampilan") self.assertRegex(str(response.content), rf'.*Materi 2.*Materi 1.*') - + class DaftarKatalogPerKontributorTest(TestCase): def setUp(self): self.client = Client() @@ -322,13 +325,13 @@ class DetailMateriTest(TestCase): "ExampleCover921.jpg", b"Test file") self.content = SimpleUploadedFile("ExampleFile921.pdf", b"Test file") - self.materi1 = Materi.objects.create(title="Materi 1", author="Agas", - uploader=self.contributor, publisher="Kelas SC", - descriptions="Deskripsi Materi 1", status="APPROVE", + self.materi1 = Materi.objects.create(title="Materi 1", author="Agas", + uploader=self.contributor, publisher="Kelas SC", + descriptions="Deskripsi Materi 1", status="APPROVE", cover=self.cover, content=self.content) - self.materi2 = Materi.objects.create(title="Materi 2", author="Agad", - uploader=self.contributor, publisher="Kelas SM", - descriptions="Deskripsi Materi 2", status="APPROVE", + self.materi2 = Materi.objects.create(title="Materi 2", author="Agad", + uploader=self.contributor, publisher="Kelas SM", + descriptions="Deskripsi Materi 2", status="APPROVE", cover=self.cover, content=self.content) self.url = "/materi/" + str(self.materi1.id) + "/" self.download_url1 = self.url + "unduh" @@ -456,10 +459,10 @@ class DetailMateriTest(TestCase): comment = Comment.objects.get(comment="This is new comment by Anonymous") response = self.client.get(url_materi) session_id = response.context["session_id"] - + payload = {"comment": comment, "session_id": session_id} self.client.post("/comment/dislike/", payload) - + self.client.post("/comment/dislike/", payload) num_of_comment_dislikes = DislikeComment.objects.filter(comment=comment, session_id=session_id).count() self.assertEqual(num_of_comment_dislikes, 0) @@ -473,10 +476,10 @@ class DetailMateriTest(TestCase): comment = Comment.objects.get(comment="This is new comment by Anonymous") response = self.client.get(url_materi) session_id = response.context["session_id"] - + payload = {"comment": comment, "session_id": session_id} self.client.post("/comment/like/", payload) - + self.client.post("/comment/like/", payload) num_of_comment_likes = LikeComment.objects.filter(comment=comment, session_id=session_id).count() self.assertEqual(num_of_comment_likes, 0) @@ -516,9 +519,9 @@ class DetailMateriTest(TestCase): is_subscribing_to_material_comments=True ) - material = Materi.objects.create(title="Materi-subscribed", author="Tester", - uploader=contributor_subscribed, publisher="Kelas PMPL", - descriptions="Deskripsi Materi subscribed", status="APPROVE", + material = Materi.objects.create(title="Materi-subscribed", author="Tester", + uploader=contributor_subscribed, publisher="Kelas PMPL", + descriptions="Deskripsi Materi subscribed", status="APPROVE", cover=self.cover, content=self.content) url = "/materi/" + str(material.id) + "/" self.client.login(**self.contributor_credential) # comment with other user @@ -543,9 +546,9 @@ class DetailMateriTest(TestCase): is_subscribing_to_material_comments=False ) - material = Materi.objects.create(title="Materi-not-subscribed", author="Tester", - uploader=contributor_not_subscribed, publisher="Kelas PMPL", - descriptions="Deskripsi Materi non-subscribed", status="APPROVE", + material = Materi.objects.create(title="Materi-not-subscribed", author="Tester", + uploader=contributor_not_subscribed, publisher="Kelas PMPL", + descriptions="Deskripsi Materi non-subscribed", status="APPROVE", cover=self.cover, content=self.content) url = "/materi/" + str(material.id) + "/" self.client.login(**self.contributor_credential) # comment with other user @@ -573,9 +576,9 @@ class DetailMateriTest(TestCase): is_subscribing_to_material_comments=True ) - material = Materi.objects.create(title="Materi-subscribed", author="Tester", - uploader=contributor_subscribed, publisher="Kelas PMPL", - descriptions="Deskripsi Materi subscribed", status="APPROVE", + material = Materi.objects.create(title="Materi-subscribed", author="Tester", + uploader=contributor_subscribed, publisher="Kelas PMPL", + descriptions="Deskripsi Materi subscribed", status="APPROVE", cover=self.cover, content=self.content) url = "/materi/" + str(material.id) + "/" self.client.login(**contributor_subscribed_credentials) # comment with the same user @@ -653,7 +656,7 @@ class DetailMateriTest(TestCase): count = Comment.objects.all().filter(comment="This is new comment by Anonymous").count() self.assertEqual(count, 1) - + def test_review_models_can_create_new_object(self): test = Review.objects.create( username="saul", profile="121212", review="232323") @@ -700,7 +703,7 @@ class DetailMateriTest(TestCase): self.client.post(url, {"review": "This is new review by Anonymous"}) response = self.client.get(url) self.assertContains(response, "Anonymous") - + def test_detail_materi_contains_review_count(self): url = self.url self.client.login(**self.contributor_credential) @@ -731,25 +734,25 @@ class DetailMateriTest(TestCase): def test_hasil_citasi_APA_materi_has_no_published_date(self): response = self.client.get(self.url) - expected = self.materi1.author + " . (n.d) . " + self.materi1.title + " . " + self.materi1.publisher + expected = self.materi1.author + " . (n.d) . " + self.materi1.title + " . " + self.materi1.publisher self.assertIn(expected, response.context["citationAPA"]) self.assertIn(expected, response.context["citationAPA"]) def test_hasil_citasi_APA_materi_has_published_date(self): response = self.client.get(self.materi_with_published_date_url) - published_date = self.materi_with_published_date.published_date.strftime("%Y-%m-%d %H:%M") - expected = ( - self.materi_with_published_date.author - + " . (" - + published_date - + ") . " - + self.materi_with_published_date.title - + " . " - + self.materi_with_published_date.publisher - ) + published_date = self.materi_with_published_date.published_date.strftime("%Y-%m-%d %H:%M") + expected = ( + self.materi_with_published_date.author + + " . (" + + published_date + + ") . " + + self.materi_with_published_date.title + + " . " + + self.materi_with_published_date.publisher + ) self.assertIn(expected, response.context["citationAPA"]) - + def test_citation_IEEE_button(self): response = self.client.get(self.url) self.assertContains(response, "Citate IEEE") @@ -761,19 +764,19 @@ class DetailMateriTest(TestCase): current_month = current_date.strftime("%b") current_year = str(current_date.year) - expected = ( - "Agas, " - + "Materi 1. " - + "Kelas SC, n.d. " - + "Accessed on: " - + current_month - + ". " - + current_day - + ", " - + current_year - + ". [Online]. " - + "Available: http://testserver" - + self.url + expected = ( + "Agas, " + + "Materi 1. " + + "Kelas SC, n.d. " + + "Accessed on: " + + current_month + + ". " + + current_day + + ", " + + current_year + + ". [Online]. " + + "Available: http://testserver" + + self.url ) self.assertIn(expected, response.context["citationIEEE"]) @@ -856,7 +859,7 @@ class DetailMateriTest(TestCase): self.assertNotEqual(context1['materi_download_count'], context2['materi_download_count']) self.assertEqual(context1['materi_download_count'], 3) self.assertEqual(context2['materi_download_count'], 2) - + def test_download_count_displayed_on_template_when_no_download(self): response = self.client.get(self.url) html = response.content.decode("utf-8") @@ -901,12 +904,12 @@ class DetailMateriTest(TestCase): self.check_materi_info_not_in_html(self.dcount_info_name, dcount_materi2, html) self.check_materi_info_in_html(self.dcount_info_name, dcount_materi2, html2) self.check_materi_info_not_in_html(self.dcount_info_name, dcount_materi1, html2) - + def test_like_count_displayed_on_template_when_no_like(self): response = self.client.get(self.url) html = response.content.decode("utf-8") self.check_materi_info_in_html(self.lcount_info_name, 0, html) - + def test_like_count_displayed_on_template_when_single_like(self): payload = { 'materi_id': self.materi1.id, @@ -916,8 +919,8 @@ class DetailMateriTest(TestCase): response = self.client.get(self.url) html = response.content.decode("utf-8") - self.check_materi_info_in_html(self.lcount_info_name, 1, html) - + self.check_materi_info_in_html(self.lcount_info_name, 1, html) + def test_like_count_displayed_on_template_when_multiple_like(self): payload1 = { 'materi_id': self.materi1.id, @@ -929,17 +932,17 @@ class DetailMateriTest(TestCase): } self.client.post(self.like_url, payload1) self.client.post(self.like_url, payload2) - + response = self.client.get(self.url) html = response.content.decode("utf-8") self.check_materi_info_in_html(self.lcount_info_name, 2, html) - + def test_like_count_displayed_on_template_when_unlike(self): payload = { 'materi_id': self.materi1.id, 'session_id': "dummysession" } - + # Like materi once self.client.post(self.like_url, payload) response = self.client.get(self.url) @@ -981,11 +984,11 @@ class PostsViewTest(TestCase): for j in range (LIKES_COUNT_PER_POST[i]): Like.objects.create( - timestamp=datetime.now(), - materi=post, + timestamp=datetime.now(), + materi=post, session_id=("dummysession-" + str(i) + '-' + str(j)) ) - + for i, post_id in enumerate(post_comment_group_dict): post = post_comment_group_dict[post_id]["data"] @@ -1127,13 +1130,13 @@ class UploadPageTest(TestCase): response = Client().get("/fake/") self.assertEqual(response.status_code, 404) - def test_upload_page_url_admin_doesnt_exist(self): + def test_upload_page_url_admin_doesnt_exist(self): self.client.login(email="admin@gov.id", password="admin") response = self.client.get("/unggah/") self.assertEqual(response.status_code, 403) - def test_upload_page_url_admin_cant_upload(self): + def test_upload_page_url_admin_cant_upload(self): self.client.login(email="admin@gov.id", password="admin") response = self.client.post("/unggah/") @@ -1271,7 +1274,7 @@ class UploadExcelPageTest(TestCase): with open(file_name, 'rb') as fp: response = self.client.post("/unggah_excel/", {'excel': fp}) - + messages = list(dj_messages.get_messages(response.wsgi_request)) msg_text = messages[0].message @@ -1289,7 +1292,7 @@ class UploadExcelPageTest(TestCase): with open(file_name, 'rb') as fp: response = self.client.post("/unggah_excel/", {'excel': fp}) - + messages = list(dj_messages.get_messages(response.wsgi_request)) msg_text = messages[0].message @@ -1308,7 +1311,7 @@ class UploadExcelPageTest(TestCase): with open(file_name, 'rb') as fp: response = self.client.post("/unggah_excel/", {'excel': fp}) - + messages = list(dj_messages.get_messages(response.wsgi_request)) msg_text = messages[0].message @@ -1327,7 +1330,7 @@ class UploadExcelPageTest(TestCase): with open(file_name, 'rb') as fp: response = self.client.post("/unggah_excel/", {'excel': fp}) - + messages = list(dj_messages.get_messages(response.wsgi_request)) msg_text = messages[0].message @@ -1348,12 +1351,12 @@ class UploadExcelPageTest(TestCase): } categories = ['Computer Science','Machine Learning','Deep Learning'] - + file_name, data_frame = self.create_dummy_excel(field_lengths=field_lengths, categories=categories) with open(file_name, 'rb') as fp: self.client.post("/unggah_excel/", {'excel': fp}) - + title = data_frame['Title'][0] materi = Materi.objects.get(title=title) default_path = 'book-cover-placeholder.png' @@ -1367,11 +1370,11 @@ class UploadExcelPageTest(TestCase): password="kontributor") response = self.client.get("/unggah_excel/?template=1") - + self.assertEquals(response['Content-Type'],'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') self.assertEquals(response['Content-Disposition'],'attachment; filename=template.xlsx') - + @@ -1478,14 +1481,14 @@ class DeleteMateriTest(TestCase): self.client.login(**self.contributor_credential) response = self.client.get(self.url) self.assertEqual(response.status_code, 302) - + def test_url_soft_delete_materi_is_success_as_admin(self): self.client.login(**self.admin_credential) response = self.client.get(self.url) - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 302) self.materi1.refresh_from_db() self.assertNotEqual(self.materi1.deleted_at, None) - + def test_url_soft_delete_materi_is_success_as_superuser(self): self.client.login(**self.superuser_credential) response = self.client.get(self.url) @@ -1512,7 +1515,7 @@ class ProfilViewTest(TestCase): self.assertEqual(found.func.__name__, self.view.as_view().__name__) def _request_as_user(self, credentials): - self.client = Client() + self.client = Client() self.client.login(**credentials) return self.client.get(self.url) @@ -1554,7 +1557,7 @@ class SuntingProfilViewTest(TestCase): self.assertEqual(found.func.__name__, self.view.as_view().__name__) def _request_as_user(self, credentials): - self.client = Client() + self.client = Client() self.client.login(**credentials) return self.client.get(self.url) @@ -1935,7 +1938,7 @@ class RevisiMateriTest(TestCase): # Logout self.client.logout() - + class GenerateDummyCommandTest(TestCase): def setUp(self): @@ -2419,7 +2422,7 @@ class RatingContributorTest(TransactionTestCase): response = self.client.get(self.url) self.assertContains(response=response, text=f"Rating: {mean(avg)}", count=1) self.assertContains(response=response, text=f"oleh: {len(avg)} orang", count=1) - + def test_not_authenticated_user_should_not_able_to_add_rating(self): response_post = self.client.post(self.url, data={ "contributor":self.contributor.id, @@ -2429,11 +2432,11 @@ class RatingContributorTest(TransactionTestCase): response_get = self.client.get(self.url) self.assertEqual(response_post.status_code, 403) self.assertContains(response_get, "Kamu harus login untuk memberi rating") - + def test_not_authenticated_user_should_not_able_to_delete_rating(self): response_post = self.client.post(self.url, data={"delete":True}) self.assertEqual(response_post.status_code, 403) - + def test_authenticated_user_should_not_delete_rating_if_has_not_been_rated(self): self.client.login(**self.admin_credential) self.client.post(self.url, data={ @@ -2441,15 +2444,15 @@ class RatingContributorTest(TransactionTestCase): "user":self.admin.id, "score":3 }) - + self.assertEqual(1, RatingContributor.objects.filter(contributor=self.contributor).count()) self.client.logout() self.client.login(**self.anonymous_credential) self.client.post(self.url, data={"delete":True}) - + self.assertEqual(1, RatingContributor.objects.filter(contributor=self.contributor).count()) - + def test_authenticated_user_should_delete_rating_if_has_been_rated(self): self.client.login(**self.admin_credential) self.client.post(self.url, data={ @@ -2467,7 +2470,7 @@ class RatingContributorTest(TransactionTestCase): self.assertContains(response, 'Rating: 0') self.assertContains(response, 'oleh: 0 orang') self.assertEqual(0, RatingContributor.objects.filter(contributor=self.contributor).count()) - + def test_average_still_be_correct_when_rating_was_deleted(self): scores = [2,4] self.client.login(**self.admin_credential) @@ -2492,7 +2495,7 @@ class RatingContributorTest(TransactionTestCase): response = self.client.get(self.url) self.assertContains(response, f'Rating: {scores[0]}') self.assertContains(response, 'oleh: 1 orang') - + def test_authenticated_user_should_update_rating_if_has_been_rated(self): self.client.login(**self.anonymous_credential) self.client.post(self.url, data={ @@ -2508,7 +2511,7 @@ class RatingContributorTest(TransactionTestCase): response = self.client.get(self.url) self.assertContains(response, 'Rating: 3') self.assertContains(response, 'oleh: 1 orang') - + def test_authenticated_user_should_not_update_rating_if_has_not_been_rated(self): self.client.login(**self.anonymous_credential) response = self.client.get(self.url) @@ -2519,7 +2522,7 @@ class RatingContributorTest(TransactionTestCase): response = self.client.get(self.url) self.assertContains(response, 'Rating: 0') self.assertContains(response, 'oleh: 0 orang') - + class UserDownloadHistoryTest(TestCase): def setUp(self): @@ -2558,25 +2561,25 @@ class UserDownloadHistoryTest(TestCase): self.materi1 = Materi.objects.first() self.download_url = f"/materi/{self.materi1.id}/unduh" self.history_url = "/download-history/" - + def test_multiple_insert_download_statistic_with_user(self): DownloadStatistics(materi=self.materi1, downloader=self.user1_anonim).save() num_of_downloads = self.user1_anonim.riwayat_unduh.all().count() self.assertEqual(num_of_downloads, 1) - + DownloadStatistics(materi=self.materi1, downloader=self.user1_anonim).save() num_of_downloads = self.user1_anonim.riwayat_unduh.all().count() self.assertEqual(num_of_downloads, 2) - + def test_download_statistics_bound_to_specific_user(self): DownloadStatistics(materi=self.materi1, downloader=self.user1_anonim).save() num_of_downloads = self.user1_anonim.riwayat_unduh.all().count() self.assertEqual(num_of_downloads, 1) - + DownloadStatistics(materi=self.materi1).save() num_of_downloads = self.user1_anonim.riwayat_unduh.all().count() self.assertEqual(num_of_downloads, 1) - + DownloadStatistics(materi=self.materi1, downloader=self.user2_anonim).save() user1_num_of_downloads = self.user1_anonim.riwayat_unduh.all().count() user2_num_of_downloads = self.user2_anonim.riwayat_unduh.all().count() @@ -2586,14 +2589,14 @@ class UserDownloadHistoryTest(TestCase): def test_registered_user_download(self): # Login self.client.login(**self.user1_credential) - + self.client.get(self.download_url) num_of_downloads = self.user1_anonim.riwayat_unduh.all().count() self.assertEqual(num_of_downloads, 1) - + # Logout self.client.logout() - + def test_unregistered_user_download(self): self.client.get(self.download_url) downloaded_materi = self.client.session['downloaded_materi'] @@ -2611,10 +2614,10 @@ class UserDownloadHistoryTest(TestCase): self.client.get(self.download_url) num_of_downloads = self.user1_anonim.riwayat_unduh.all().count() self.assertEqual(num_of_downloads, 2) - + # Logout self.client.logout() - + def test_unregistered_user_multiple_download(self): self.client.get(self.download_url) downloaded_materi = self.client.session['downloaded_materi'] @@ -2627,27 +2630,27 @@ class UserDownloadHistoryTest(TestCase): num_of_downloads = DownloadStatistics.objects.filter( pk__in=downloaded_materi).count() self.assertEqual(num_of_downloads, 2) - + def test_registered_user_doesnt_use_session_when_download(self): # Login self.client.login(**self.user1_credential) - + self.client.get(self.download_url) self.assertFalse('downloaded_materi' in self.client.session) - + # Logout self.client.logout() - + def test_download_history_bound_to_specific_user(self): # Login Anonym 1 self.client.login(**self.user1_credential) self.client.get(self.download_url) num_of_downloads = self.user1_anonim.riwayat_unduh.all().count() self.assertEqual(num_of_downloads, 1) - + # Logout Anonym 1 self.client.logout() - + # Unregistered User download self.client.get(self.download_url) user1_num_of_downloads = self.user1_anonim.riwayat_unduh.all().count() @@ -2656,7 +2659,7 @@ class UserDownloadHistoryTest(TestCase): pk__in=downloaded_materi).count() self.assertEqual(user1_num_of_downloads, 1) self.assertEqual(guest_num_of_downloads, 1) - + # Login Anonym 2 self.client.login(**self.user2_credential) self.client.get(self.download_url) @@ -2665,7 +2668,7 @@ class UserDownloadHistoryTest(TestCase): self.assertEqual(user1_num_of_downloads, 1) self.assertEqual(guest_num_of_downloads, 1) self.assertEqual(user2_num_of_downloads, 1) - + # Logout Anonym 2 self.client.logout() @@ -2685,42 +2688,42 @@ class DownloadHistoryViewTest(TestCase): **self.contributor_credential, name="Kontributor", is_contributor=True ) self.client = Client() - + content1 = b"Test file" content2 = b"File Test" - + self.cover1 = SimpleUploadedFile("cover1.jpg",content1) self.content1 = SimpleUploadedFile("content1.txt",content1) - + self.cover2 = SimpleUploadedFile("cover2.jpg",content2) self.content2 = SimpleUploadedFile("content2.txt",content2) - + self.materi1 = Materi.objects.create(title="Materi 1", author="Agas", uploader=self.contributor, publisher="Kelas SC", descriptions="Deskripsi Materi 1", status="PENDING", cover=self.cover1, content=self.content1) self.materi2 = Materi.objects.create(title="Materi 2", author="Danin", uploader=self.contributor, publisher="Kelas DDP", descriptions="Deskripsi Materi 2", status="PENDING", cover=self.cover2, content=self.content2) - + self.download_url1 = f"/materi/{self.materi1.id}/unduh" self.download_url2 = f"/materi/{self.materi2.id}/unduh" self.history_url = "/download-history/" - + # Login self.client.login(**self.user_credential) def tearDown(self): # Logout self.client.logout() - + def test_allow_registered_user(self): response = self.client.get(self.history_url) self.assertEqual(response.status_code, 200) - + def test_allow_unregistered_user(self): # Forced Logout self.client.logout() - + response = self.client.get(self.history_url) self.assertEqual(response.status_code, 200) @@ -2732,14 +2735,14 @@ class DownloadHistoryViewTest(TestCase): response = self.client.get(self.history_url) resp_html = response.content.decode('utf8') self.assertIn(self.user_anonim.name, resp_html) - + def test_registered_user_download_history_correctly_displayed(self): self.client.get(self.download_url1) self.client.get(self.download_url2) self.client.get(self.download_url1) - + jkt_timezone = timezone(TIME_ZONE) - + download_history = self.user_anonim.riwayat_unduh.all() response = self.client.get(self.history_url) resp_html = response.content.decode('utf8') @@ -2747,19 +2750,19 @@ class DownloadHistoryViewTest(TestCase): downloaded_materi = riwayat.materi self.assertIn(downloaded_materi.title, resp_html) self.assertIn(downloaded_materi.author, resp_html) - + jkt_timestamp = riwayat.timestamp.astimezone(jkt_timezone) self.assertIn(jkt_timestamp.strftime("%d %B %Y %H:%M:%S"), resp_html) - + def test_unregistered_user_download_history_correctly_displayed(self): self.client.logout() self.client.get(self.download_url1) self.client.get(self.download_url2) self.client.get(self.download_url1) - + jkt_timezone = timezone(TIME_ZONE) - + response = self.client.get(self.history_url) resp_html = response.content.decode('utf8') for riwayat_id in self.client.session['downloaded_materi']: @@ -2767,19 +2770,19 @@ class DownloadHistoryViewTest(TestCase): downloaded_materi = riwayat.materi self.assertIn(downloaded_materi.title, resp_html) self.assertIn(downloaded_materi.author, resp_html) - + jkt_timestamp = riwayat.timestamp.astimezone(jkt_timezone) self.assertIn(jkt_timestamp.strftime("%d %B %Y %H:%M:%S"), resp_html) - + def test_download_history_not_display_if_user_changed(self): self.client.get(self.download_url1) self.client.get(self.download_url2) self.client.get(self.download_url1) self.client.logout() - + jkt_timezone = timezone(TIME_ZONE) - + download_history = self.user_anonim.riwayat_unduh.all() response = self.client.get(self.history_url) resp_html = response.content.decode('utf8') @@ -2787,23 +2790,23 @@ class DownloadHistoryViewTest(TestCase): downloaded_materi = riwayat.materi self.assertNotIn(downloaded_materi.title, resp_html) self.assertNotIn(downloaded_materi.author, resp_html) - + jkt_timestamp = riwayat.timestamp.astimezone(jkt_timezone) self.assertNotIn(jkt_timestamp.strftime("%d %B %Y %H:%M:%S"), resp_html) - + def test_unregistered_user_download_history_wont_be_saved_if_user_changes(self): self.client.logout() self.client.get(self.download_url1) self.client.get(self.download_url2) self.client.get(self.download_url1) - + self.client.get(self.history_url) self.client.login(**self.user_credential) self.client.logout() self.assertFalse('downloaded_materi' in self.client.session) - + def test_download_history_sorted_by_download_time(self): # download with 1 second interval to differ download time self.client.get(self.download_url1) @@ -2813,15 +2816,15 @@ class DownloadHistoryViewTest(TestCase): self.client.get(self.download_url1) sleep(1) self.client.get(self.download_url2) - + response = self.client.get(self.history_url) resp_html = response.content.decode('utf8') - + table_html = ("<table" + resp_html.split("<table")[1]).split("</table>")[0] + "</table>" soup = BeautifulSoup(table_html, 'html.parser') histories_html = soup.find('tbody').find_all('tr') prev_timestamp = None - + for riwayat_html in histories_html: materi_data = riwayat_html.find_all("td") date_format = "%d %B %Y %H:%M:%S" @@ -2829,7 +2832,7 @@ class DownloadHistoryViewTest(TestCase): if prev_timestamp: self.assertTrue(prev_timestamp > materi_timestamp) prev_timestamp = materi_timestamp - + def test_no_history_display_message(self): no_history_msg = "Anda belum mengunduh materi. Silahkan unduh materi yang anda butuhkan" response = self.client.get(self.history_url) @@ -2838,9 +2841,9 @@ class DownloadHistoryViewTest(TestCase): class MateriModelTest(TestCase): - + def setUp(self): - self.contributor = User.objects.create( + self.contributor = User.objects.create( email="kontributor@gov.id", password="passwordtest", name="kontributor", @@ -2927,7 +2930,7 @@ class RandomizedMateriTest(TestCase): response = Client().get("/?random=1") self.assertIn("Materi 1", response.content.decode()) self.assertIn("Materi 2", response.content.decode()) - + class YearChoicesTest(TestCase): def test_release_year_contains_the_right_current_year(self): now = dt.date.today().year @@ -2993,7 +2996,7 @@ class YTUrlVideoTest(TestCase): self.setUpImage() self.content = SimpleUploadedFile("ExampleFile221.pdf", b"Test file") self.category = Category.objects.create(id="1", name="medis", description="kategori medis") - + @override_settings(MEDIA_ROOT=tempfile.gettempdir()) def setUpImage(self): from django.core.files.uploadedfile import InMemoryUploadedFile @@ -3026,7 +3029,7 @@ class YTUrlVideoTest(TestCase): "yt_video_id":"jNwz4L9MGVY"} ) self.assertTrue(Materi.objects.get(title="Materi 1")) - + def test_upload_materi_with_invalid_yt_video_id(self): self.client.login(**self.contributor_credential) self.client.post( @@ -3036,7 +3039,7 @@ class YTUrlVideoTest(TestCase): "yt_video_id":"randomId"} ) self.assertEqual(Materi.objects.filter(title="Materi 2").count(), 0) - + def test_detail_materi_has_video_if_yt_video_id_not_empty(self): self.test_upload_materi_with_valid_yt_video_id() pk = Materi.objects.get(title="Materi 1").pk @@ -3087,7 +3090,7 @@ class ChangePasswordTest(TestCase): # Logout self.client.logout() - + class SeeRatedMateriByUser(TestCase): def setUp(self): self.client = Client() @@ -3262,28 +3265,28 @@ class PasswordValidatorPolicyTest(TestCase): self.password_length_lower_than_8 = "P4ss!" self.password_enforcing_policy = "Passw0rd!" self.validator = PasswordPolicyValidator() - + def test_using_password_no_lowercase(self): self.assertRaises(ValidationError, self.validator.validate, self.password_no_lowercase) def test_using_password_no_upprcase(self): self.assertRaises(ValidationError, self.validator.validate, self.password_no_uppercase) - + def test_using_password_no_digit(self): self.assertRaises(ValidationError, self.validator.validate, self.password_no_digit) def test_using_password_no_special_char(self): self.assertRaises(ValidationError, self.validator.validate, self.password_no_special_char) - + def test_using_password_with_length_less_than_8(self): self.assertRaises(ValidationError, self.validator.validate, self.password_length_lower_than_8) - + def test_using_password_using_correct_policy(self): self.assertEquals(self.validator.validate(self.password_enforcing_policy), None) - + class LandingPageNavbarTest(TestCase): - + def setUp(self): self.client = Client() self.contributor_credential = { @@ -3442,4 +3445,3 @@ class MateriRecommendationTest(TestCase): response = Client().get("/?recommendation=1") list = [int(id) for id in re.findall(r"Materi\s(\d+)[^\d]", response.content.decode())] self.assertEqual(list, [1, 2]) - diff --git a/app/urls.py b/app/urls.py index a8c7281cbdd6821bf7eace5f939520e099f53ad5..06e7d42410e9289b3f1342eb6cf58d6daa10e202 100644 --- a/app/urls.py +++ b/app/urls.py @@ -41,4 +41,7 @@ urlpatterns = [ path("password_success/", views.password_success, name="password_success"), path("given-rating/", views.see_given_rating, name="see_given_rating"), path("submit-visitor/", SubmitVisitorView.as_view(), name="submit-visitor"), + path(r'subscribe/', views.subscribe, name='subscribe'), + path(r'subscribeform/', views.subscribeform, name='subscribeform'), + path(r'subscription_confirmation/', views.subscription_confirmation, name='subscription_confirmation'), ] diff --git a/app/utils/email_utility.py b/app/utils/email_utility.py new file mode 100644 index 0000000000000000000000000000000000000000..bc720758597e59a51e26a060f58838a2a17ef60d --- /dev/null +++ b/app/utils/email_utility.py @@ -0,0 +1,90 @@ +import logging, traceback +from django.urls import reverse +import requests +from django.template.loader import get_template +from django.utils.html import strip_tags +from django.conf import settings +import smtplib +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +import email +import email.mime.application +from django.utils.html import strip_tags + + +def send_email(data): + try: + url = "https://api.mailgun.net/v3/<domain-name>/messages" + status = requests.post( + url, + auth=("api", settings.MAILGUN_API_KEY), + data={"from": "OKI <https://app.mailgun.com/app/sending/domains/sandbox34675edbad2a4ab08e896c3bd80524ec.mailgun.org>", + "to": [data["email"]], + "subject": data["subject"], + "text": data["plain_text"], + "html": data["html_text"]} + ) + logging.getLogger("info").info("Mail sent to " + data["email"] + ". status: " + str(status)) + return status + except Exception as e: + logging.getLogger("error").error(traceback.format_exc()) + return False + + +def send_subscription_email(email, subscription_confirmation_url): + data = dict() + data["confirmation_url"] = subscription_confirmation_url + data["subject"] = "Please Confirm The Subscription" + data["email"] = email + template = get_template("app/emails/subscription.html") + data["html_text"] = template.render(data) + data["plain_text"] = strip_tags(data["html_text"]) + return send_email(data) + + +def send_gmail(email, subscription_confirmation_url): + # create message + msg = MIMEMultipart('alternative') + msg['Subject'] = "Please verify your mail" + msg['From'] = "pmplmailer@gmail.com" + msg['To'] = email + + # create body + html_text = '<div style="border:1px solid black">Thanks for subscribing to Us. Please click this link to activate your subscription : ' + subscription_confirmation_url + '</div>' + plain_text = strip_tags(html_text) + + # Create the body of the message (a plain-text and an HTML version). + # Record the MIME types of both parts - text/plain and text/html. + part1 = MIMEText(plain_text, 'plain') + part2 = MIMEText(html_text, 'html') + + # Attach image if any + + # Attach parts into message container. + # According to RFC 2046, the last part of a multipart message, in this case + # the HTML message, is best and preferred. + msg.attach(part1) + msg.attach(part2) + + # Send the message via local SMTP server. + host = "smtp.gmail.com" + port = 587 + mail = smtplib.SMTP(host, port, timeout=60) + mail.ehlo() + mail.starttls() + + # Add recepiens, cc or bcc if any + recepient = [msg["To"]] + + # username and password of gmail id which will be used to send email + username = "pmplmailer@gmail.com" + password = "asdf1234$" + + # login using credentials + mail.login(username, password) + + # send email + mail.sendmail(msg["From"], recepient, msg.as_string()) + mail.quit() + + return "\nSent\n" \ No newline at end of file diff --git a/app/utils/encryption_util.py b/app/utils/encryption_util.py new file mode 100644 index 0000000000000000000000000000000000000000..a448767e066acceda561a4a48cc602628db41148 --- /dev/null +++ b/app/utils/encryption_util.py @@ -0,0 +1,37 @@ +from cryptography.fernet import Fernet +import base64 +import logging +import traceback +from django.conf import settings + +#this is your "password/ENCRYPT_KEY". keep it in settings.py file +#key = Fernet.generate_key() + +def encrypt(txt): + try: + # convert integer etc to string first + txt = str(txt) + # get the key from settings + cipher_suite = Fernet(settings.ENCRYPT_KEY) # key should be byte + # #input should be byte, so convert the text to byte + encrypted_text = cipher_suite.encrypt(txt.encode('ascii')) + # encode to urlsafe base64 format + encrypted_text = base64.urlsafe_b64encode(encrypted_text).decode("ascii") + return encrypted_text + except Exception as e: + # log the error if any + logging.getLogger("error_logger").error(traceback.format_exc()) + return None + + +def decrypt(txt): + try: + # base64 decode + txt = base64.urlsafe_b64decode(txt) + cipher_suite = Fernet(settings.ENCRYPT_KEY) + decoded_text = cipher_suite.decrypt(txt).decode("ascii") + return decoded_text + except Exception as e: + # log the error + logging.getLogger("error_logger").error(traceback.format_exc()) + return None \ No newline at end of file diff --git a/app/views.py b/app/views.py index a0062ed070c2065cc77c3e37757c749b70f470d1..306e84073b13dea436e90faa2952bafcb618f81c 100644 --- a/app/views.py +++ b/app/views.py @@ -1,5 +1,9 @@ import mimetypes import os +import re +import time +import logging +import traceback from io import BytesIO import django @@ -15,8 +19,7 @@ from django.core.paginator import Paginator from django.db.models import Q, Avg from django.http import (Http404, HttpResponse, HttpResponseRedirect, JsonResponse) -from django.shortcuts import get_object_or_404, redirect -from django.shortcuts import render +from django.shortcuts import get_object_or_404, redirect, render from django.template import loader from django.urls import reverse from django.urls import reverse_lazy @@ -34,6 +37,10 @@ from app.models import ( SubmitVisitor ) from authentication.models import User +from app.utils.encryption_util import encrypt, decrypt +from app.utils.email_utility import send_subscription_email, send_gmail +from app.models import SubscribeModel +from datetime import datetime from .services import DafterKatalogService, DetailMateriService, LikeDislikeService, MateriFieldValidationHelperService, \ DownloadViewMateriHelperService, UploadMateriService, EditProfileService, RevisiMateriService, \ DownloadHistoryService, GoogleDriveUploadService @@ -205,7 +212,7 @@ class DetailMateri(TemplateView): from_email = getattr(settings, 'EMAIL_HOST_USER'), recipient_list = [materi_uploader.email,], fail_silently = False, - ) + ) elif (review_text != None): review = Review.objects.create( review=review_text, username=DetailMateriService.get_user_name(request), materi=materi, user=user_obj @@ -285,6 +292,98 @@ def add_rating_materi(request): ) return JsonResponse({"success": False, "msg": "Forbidden"}, status=403) +def subscribeform(request): + return render(request, 'subscribe.html', ) + +def subscribe(request): + post_data = request.POST.copy() + email = post_data.get("email", None) + + error_msg = validate_email(email) + if error_msg: + messages.error(request, error_msg) + return HttpResponseRedirect(reverse('subscribe')) + + token = encrypt(email + "_" + str(time.time())) + subscription_confirmation_url = request.build_absolute_uri( reverse('subscription_confirmation')) + "?token=" + token + status = send_gmail(email, subscription_confirmation_url) + save_status = save_email(email) + + if save_status: + token = encrypt(email + "_" + str(time.time())) + subscription_confirmation_url = request.build_absolute_uri(reverse('subscription_confirmation')) + "?token=" + token + status = send_gmail(email, subscription_confirmation_url) + if not status: + SubscribeModel.objects.get(email=email).delete() + logging.getLogger("info").info( + "Deleted the record from Subscribe table for " + email + " as email sending failed. status: " + str( + status)) + else: + msg = "Mail sent to email Id '" + email + "'. Please confirm your subscription by clicking on " \ + "confirmation link provided in email. " \ + "Please check your spam folder as well." + messages.success(request, msg) + return HttpResponse(msg) + else: + msg = "Some error occurred. Please try in some time. Meanwhile we are looking into it." + messages.error(request, msg) + return HttpResponse(msg) + return HttpResponseRedirect(reverse('subscribeform')) + +def subscription_confirmation(request): + if "POST" == request.method: + raise Http404 + + token = request.GET.get("token", None) + + if not token: + logging.getLogger("warning").warning("Invalid Link ") + messages.error(request, "Invalid Link") + return HttpResponseRedirect(reverse('subscribeform')) + + token = decrypt(token) + if token: + token = token.split("_") + email = token[0] + print(email) + initiate_time = token[1] # time when email was sent , in epoch format. can be used for later calculations + try: + subscribe_model_instance = SubscribeModel.objects.get(email=email) + subscribe_model_instance.status = "CONFIRMED" + subscribe_model_instance.updated_date = datetime.today() + subscribe_model_instance.save() + messages.success(request, "Subscription Confirmed. Thank you.") + except Exception as e: + logging.getLogger("warning").warning(traceback.format_exc()) + messages.error(request, "Invalid Link") + else: + logging.getLogger("warning").warning("Invalid token ") + messages.error(request, "Invalid Link") + + return HttpResponse('<h1>Thank you for subscribing us</h1>') + +def validate_email(email): + if email is None: + return "Email is required." + elif not re.match(r"^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$", email): + return "Invalid Email Address." + else: + return None + +def save_email(email): + try: + subscribe_model_instance = SubscribeModel.objects.get(email=email) + except Exception as e: + subscribe_model_instance = SubscribeModel() + + + # does not matter if already subscribed or not...resend the email + subscribe_model_instance.email = email + subscribe_model_instance.status = "SUBSCRIBED" + subscribe_model_instance.created_date = datetime.today() + subscribe_model_instance.updated_date = datetime.today() + subscribe_model_instance.save() + return True def download_materi(request, pk): @@ -652,6 +751,7 @@ class RevisiMateriView(TemplateView): return self.render_to_response(context) + def pages(request): context = {} # All resource paths end in .html. diff --git a/authentication/migrations/0001_initial.py b/authentication/migrations/0001_initial.py index b28fe9c3d35bef40e5c1a2e04b5c1acbc47333fd..8dd6568cd24f5cf72e9aba257659da57907aa573 100644 --- a/authentication/migrations/0001_initial.py +++ b/authentication/migrations/0001_initial.py @@ -1,9 +1,8 @@ -# Generated by Django 3.0.3 on 2020-05-08 14:42 - -import django.utils.timezone -from django.db import migrations, models +# Generated by Django 3.1 on 2020-10-30 15:29 import authentication.models +from django.db import migrations, models +import django.utils.timezone class Migration(migrations.Migration): @@ -11,53 +10,41 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('auth', '0011_update_proxy_permissions'), + ('auth', '0012_alter_user_first_name_max_length'), ] operations = [ migrations.CreateModel( name='User', fields=[ - ('id', models.AutoField(auto_created=True, - primary_key=True, serialize=False, verbose_name='ID')), - ('password', models.CharField( - max_length=128, verbose_name='password')), - ('last_login', models.DateTimeField( - blank=True, null=True, verbose_name='last login')), - ('is_superuser', models.BooleanField(default=False, - help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), - ('first_name', models.CharField(blank=True, - max_length=30, verbose_name='first name')), - ('last_name', models.CharField(blank=True, - max_length=150, verbose_name='last name')), - ('is_staff', models.BooleanField(default=False, - help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), - ('is_active', models.BooleanField( - default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), - ('date_joined', models.DateTimeField( - default=django.utils.timezone.now, verbose_name='date joined')), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), + ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), ('email', models.EmailField(max_length=254, unique=True)), - ('username', models.CharField( - blank=True, default='', max_length=150)), + ('username', models.CharField(blank=True, default='', max_length=150)), ('name', models.CharField(max_length=150)), ('is_admin', models.BooleanField(default=False)), ('is_contributor', models.BooleanField(default=False)), ('instansi', models.CharField(max_length=240)), - ('nik', models.CharField(max_length=240)), + ('nik', models.CharField(max_length=16)), ('alamat', models.CharField(max_length=240)), ('nomor_telpon', models.CharField(max_length=240)), ('linkedin', models.URLField(blank=True, default='')), ('facebook', models.URLField(blank=True, default='')), ('twitter', models.URLField(blank=True, default='')), ('instagram', models.URLField(blank=True, default='')), - ('biography', models.TextField( - blank=True, default='', max_length=200)), - ('profile_picture', models.ImageField( - default='https://ibb.co/gv9YHt4', upload_to='profile_picture/')), - ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', - related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), - ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', - related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), + ('biography', models.TextField(blank=True, default='', max_length=200)), + ('default_profile_picture', models.BooleanField(blank=True, default=False)), + ('profile_picture', models.ImageField(default='default-image.jpg', upload_to='')), + ('is_subscribing_to_material_comments', models.BooleanField(default=True)), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), ], options={ 'verbose_name': 'user', diff --git a/authentication/migrations/0002_auto_20200518_1752.py b/authentication/migrations/0002_auto_20200518_1752.py deleted file mode 100644 index 8e88a1b0ce600471b466fc503131e56fd6defd54..0000000000000000000000000000000000000000 --- a/authentication/migrations/0002_auto_20200518_1752.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.0.3 on 2020-05-18 10:52 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('authentication', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='user', - name='profile_picture', - field=models.ImageField(default='https://i.ibb.co/9wgPzyZ/default-image.png', upload_to='profile_picture/'), - ), - ] diff --git a/authentication/migrations/0003_auto_20200519_1012.py b/authentication/migrations/0003_auto_20200519_1012.py deleted file mode 100644 index b705ff097ac330024e85c1b8c97daf8890e7a54e..0000000000000000000000000000000000000000 --- a/authentication/migrations/0003_auto_20200519_1012.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.0.3 on 2020-05-19 03:12 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('authentication', '0002_auto_20200518_1752'), - ] - - operations = [ - migrations.AddField( - model_name='user', - name='default_profile_picture', - field=models.URLField(blank=True, default="https://i.ibb.co/9wgPzyZ/default-image.png'"), - ), - migrations.AlterField( - model_name='user', - name='profile_picture', - field=models.ImageField(upload_to='', verbose_name=models.URLField(blank=True, default="https://i.ibb.co/9wgPzyZ/default-image.png'")), - ), - ] diff --git a/authentication/migrations/0004_auto_20200519_1020.py b/authentication/migrations/0004_auto_20200519_1020.py deleted file mode 100644 index 255dcf75f6a117a94a61e45177b7e4f257a1afc3..0000000000000000000000000000000000000000 --- a/authentication/migrations/0004_auto_20200519_1020.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.0.3 on 2020-05-19 03:20 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('authentication', '0003_auto_20200519_1012'), - ] - - operations = [ - migrations.AlterField( - model_name='user', - name='default_profile_picture', - field=models.BooleanField(blank=True, default=False), - ), - migrations.AlterField( - model_name='user', - name='profile_picture', - field=models.ImageField(upload_to='', verbose_name=models.BooleanField(blank=True, default=False)), - ), - ] diff --git a/authentication/migrations/0005_auto_20200519_1021.py b/authentication/migrations/0005_auto_20200519_1021.py deleted file mode 100644 index 05bf864c6833afd6496877d76da9a976f7add55f..0000000000000000000000000000000000000000 --- a/authentication/migrations/0005_auto_20200519_1021.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.0.3 on 2020-05-19 03:21 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('authentication', '0004_auto_20200519_1020'), - ] - - operations = [ - migrations.AlterField( - model_name='user', - name='profile_picture', - field=models.ImageField(default='default-image.jpg', upload_to=''), - ), - ] diff --git a/authentication/migrations/0006_auto_20200929_2125.py b/authentication/migrations/0006_auto_20200929_2125.py deleted file mode 100644 index a2c2da4f14cc46dadd6f038686ae09c7205e6c72..0000000000000000000000000000000000000000 --- a/authentication/migrations/0006_auto_20200929_2125.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1 on 2020-09-29 14:25 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('authentication', '0005_auto_20200519_1021'), - ] - - operations = [ - migrations.AlterField( - model_name='user', - name='first_name', - field=models.CharField(blank=True, max_length=150, verbose_name='first name'), - ), - ] diff --git a/authentication/migrations/0007_auto_20201009_1415.py b/authentication/migrations/0007_auto_20201009_1415.py deleted file mode 100644 index 012a5c89e717087a23903de27bbadbdf8798d63e..0000000000000000000000000000000000000000 --- a/authentication/migrations/0007_auto_20201009_1415.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1 on 2020-10-09 07:15 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('authentication', '0006_auto_20200929_2125'), - ] - - operations = [ - migrations.AlterField( - model_name='user', - name='nik', - field=models.CharField(max_length=16), - ), - ] diff --git a/authentication/migrations/0008_user_is_subscribing_to_material_comments.py b/authentication/migrations/0008_user_is_subscribing_to_material_comments.py deleted file mode 100644 index 2348d49d3ce1c3cbe3b11628b81dde0ad5be4478..0000000000000000000000000000000000000000 --- a/authentication/migrations/0008_user_is_subscribing_to_material_comments.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1 on 2020-10-29 12:52 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('authentication', '0007_auto_20201009_1415'), - ] - - operations = [ - migrations.AddField( - model_name='user', - name='is_subscribing_to_material_comments', - field=models.BooleanField(default=True), - ), - ] diff --git a/digipus/settings.py b/digipus/settings.py index 884c3e67cf7924e4165161d8d50a3ce04361a306..3fc4cad5a64dc14ba4badf26c09479b5dc7be212 100644 --- a/digipus/settings.py +++ b/digipus/settings.py @@ -189,4 +189,5 @@ EMAIL_PORT = config('EMAIL_PORT', default=587) # use Google Mail SMTP as default EMAIL_HOST_USER = config('EMAIL_HOST_USER', default="pmplclass2020@gmail.com") EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD', default="pmpldigipusemail") EMAIL_USE_TLS = True -EMAIL_USE_SSL = False \ No newline at end of file +EMAIL_USE_SSL = False +ENCRYPT_KEY = b'YVLSFH4WgxkLgCOmCBchVmahU_VIF2xPFdD7GUTsvgY=' diff --git a/forum/migrations/0001_initial.py b/forum/migrations/0001_initial.py index 09be1531345689e50d9940908018970274ada73f..652260331742b31ca6bacb8926451651385c17b3 100644 --- a/forum/migrations/0001_initial.py +++ b/forum/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1 on 2020-10-06 14:39 +# Generated by Django 3.1 on 2020-10-30 15:30 from django.conf import settings from django.db import migrations, models @@ -11,6 +11,7 @@ class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('app', '0001_initial'), ] operations = [ @@ -19,7 +20,10 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('title', models.TextField()), - ('description', models.TextField(default='')), + ('description', models.TextField(blank=True, default='')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('materi', models.ManyToManyField(blank=True, to='app.Materi')), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], ), @@ -28,7 +32,10 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('description', models.TextField(default='')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), ('discussion', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='forum.discussion')), + ('materi', models.ManyToManyField(blank=True, to='app.Materi')), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], ), diff --git a/forum/migrations/0002_auto_20201007_1903.py b/forum/migrations/0002_auto_20201007_1903.py deleted file mode 100644 index f26c6bac7c379bbb8dc5bb85ecbb59bb0e4b8e0c..0000000000000000000000000000000000000000 --- a/forum/migrations/0002_auto_20201007_1903.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 3.1 on 2020-10-07 12:03 - -from django.db import migrations, models -import django.utils.timezone - - -class Migration(migrations.Migration): - dependencies = [ - ('forum', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='discussion', - name='created_at', - field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), - preserve_default=False, - ), - migrations.AddField( - model_name='discussion', - name='updated_at', - field=models.DateTimeField(auto_now=True), - ), - ] diff --git a/forum/migrations/0003_auto_20201008_2056.py b/forum/migrations/0003_auto_20201008_2056.py deleted file mode 100644 index dab5b854233c101e4563576f554eacb33b0365e9..0000000000000000000000000000000000000000 --- a/forum/migrations/0003_auto_20201008_2056.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 3.1 on 2020-10-08 13:56 - -from django.db import migrations, models -import django.utils.timezone - - -class Migration(migrations.Migration): - dependencies = [ - ('forum', '0002_auto_20201007_1903'), - ] - - operations = [ - migrations.AddField( - model_name='discussioncomment', - name='created_at', - field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), - preserve_default=False, - ), - migrations.AddField( - model_name='discussioncomment', - name='updated_at', - field=models.DateTimeField(auto_now=True), - ), - ] diff --git a/forum/migrations/0004_discussion_materi.py b/forum/migrations/0004_discussion_materi.py deleted file mode 100644 index 8cbd9bd33e85ad3e50b4021d38ecc61d6afeebed..0000000000000000000000000000000000000000 --- a/forum/migrations/0004_discussion_materi.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1 on 2020-10-08 18:37 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ('app', '0015_reqmaterial'), - ('forum', '0003_auto_20201008_2056'), - ] - - operations = [ - migrations.AddField( - model_name='discussion', - name='materi', - field=models.ManyToManyField(blank=True, null=True, to='app.Materi'), - ), - ] diff --git a/forum/migrations/0005_auto_20201009_0138.py b/forum/migrations/0005_auto_20201009_0138.py deleted file mode 100644 index c4fc40216dcc5203cd5f094eac30a507e281ea11..0000000000000000000000000000000000000000 --- a/forum/migrations/0005_auto_20201009_0138.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1 on 2020-10-08 18:38 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ('app', '0015_reqmaterial'), - ('forum', '0004_discussion_materi'), - ] - - operations = [ - migrations.AlterField( - model_name='discussion', - name='materi', - field=models.ManyToManyField(blank=True, to='app.Materi'), - ), - ] diff --git a/forum/migrations/0006_auto_20201009_0625.py b/forum/migrations/0006_auto_20201009_0625.py deleted file mode 100644 index 3e59801201877f0ae3fa0d9ca0972d8aad393c17..0000000000000000000000000000000000000000 --- a/forum/migrations/0006_auto_20201009_0625.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.1 on 2020-10-08 23:25 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ('app', '0015_reqmaterial'), - ('forum', '0005_auto_20201009_0138'), - ] - - operations = [ - migrations.AddField( - model_name='discussioncomment', - name='materi', - field=models.ManyToManyField(blank=True, to='app.Materi'), - ), - migrations.AlterField( - model_name='discussion', - name='description', - field=models.TextField(blank=True, default=''), - ), - ] diff --git a/news/migrations/0001_initial.py b/news/migrations/0001_initial.py index 7c584641a29f5191a19848e13e52ed48722d8ff4..ed7921e164b76563aa96c5b5d43cf0260e5f7b1a 100644 --- a/news/migrations/0001_initial.py +++ b/news/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1 on 2020-10-03 07:47 +# Generated by Django 3.1 on 2020-10-30 15:30 from django.db import migrations, models