Skip to content

Commit

Permalink
Merge pull request #81 from mnqrt/feat/adrian/tanyateman
Browse files Browse the repository at this point in the history
Feat/adrian/tanyateman
  • Loading branch information
mnqrt authored Aug 9, 2024
2 parents 629dcc7 + c638be4 commit 55513d3
Show file tree
Hide file tree
Showing 12 changed files with 305 additions and 16 deletions.
25 changes: 25 additions & 0 deletions main/migrations/0017_question.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 3.1.2 on 2024-08-02 03:23

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('main', '0016_auto_20240727_0233'),
]

operations = [
migrations.CreateModel(
name='Question',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('question_text', models.TextField()),
('is_anonym', models.IntegerField()),
('attachment', models.CharField(max_length=120)),
('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='main.course')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='main.profile')),
],
),
]
18 changes: 18 additions & 0 deletions main/migrations/0018_question_like_count.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.1.2 on 2024-08-02 03:49

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('main', '0017_question'),
]

operations = [
migrations.AddField(
model_name='question',
name='like_count',
field=models.IntegerField(default=0),
),
]
18 changes: 18 additions & 0 deletions main/migrations/0019_question_verification_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.1.2 on 2024-08-05 06:31

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('main', '0018_question_like_count'),
]

operations = [
migrations.AddField(
model_name='question',
name='verification_status',
field=models.TextField(choices=[('Menunggu Verifikasi', 'Waiting'), ('Terverifikasi', 'Approved')], default='Menunggu Verifikasi'),
),
]
25 changes: 25 additions & 0 deletions main/migrations/0020_auto_20240809_0024.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 3.1.2 on 2024-08-09 00:24

import datetime
from django.db import migrations, models
from django.utils.timezone import utc


class Migration(migrations.Migration):

dependencies = [
('main', '0019_question_verification_status'),
]

operations = [
migrations.AddField(
model_name='question',
name='created_at',
field=models.DateTimeField(default=datetime.datetime(2024, 8, 9, 0, 24, 51, 49643, tzinfo=utc)),
),
migrations.AddField(
model_name='question',
name='updated_at',
field=models.DateTimeField(default=datetime.datetime(2024, 8, 9, 0, 24, 51, 49654, tzinfo=utc)),
),
]
54 changes: 53 additions & 1 deletion main/models.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import mimetypes
from django.contrib.auth.models import User
from django.db import models
from django.db.models.deletion import CASCADE
from django.db.models import UniqueConstraint
import boto3
import environ

from django.utils import timezone

env = environ.Env()
expires_in = 60*60*7 # 7 Hours

class Tag(models.Model):
"""
Tag for a review
Expand Down Expand Up @@ -172,4 +178,50 @@ class ScoreSubcomponent(models.Model):
class Meta:
constraints = [
UniqueConstraint(fields=['score_component', 'subcomponent_number'], name='unique_number')
]
]

class Question(models.Model):
"""
This class represent the Question model for "TanyaTeman" Feature
is_anonym = 1 if the user wants to be anonym in the question, otherwise is_anonym = 0
"""

class VerificationStatus(models.TextChoices):
WAITING = "Menunggu Verifikasi"
APPROVED = "Terverifikasi"

user = models.ForeignKey(Profile, on_delete=CASCADE)
question_text = models.TextField()
course = models.ForeignKey(Course, on_delete=CASCADE)
is_anonym = models.IntegerField()
attachment = models.CharField(max_length=120)
like_count = models.IntegerField(default=0)
verification_status = models.TextField(choices=VerificationStatus.choices, default=VerificationStatus.WAITING)
created_at = models.DateTimeField(default=timezone.now())
updated_at = models.DateTimeField(default=timezone.now())

def save(self, *args, **kwargs):
self.updated_at = timezone.now()
return super(Question, self).save(*args, **kwargs)

def get_attachment_presigned_url(self, expires_in=expires_in):
s3 = boto3.client(
's3',
aws_access_key_id=env("ACCESS_KEY_ID"),
aws_secret_access_key=env("ACCESS_KEY_SECRET"),
region_name=env("AWS_REGION")
)

attachment_type = self.attachment.split('.')[-1]

url = s3.generate_presigned_url(
'get_object',
Params={
'Bucket': env("BUCKET_NAME"),
'Key': self.attachment,
'ResponseContentDisposition': 'inline',
'ResponseContentType': attachment_type
},
ExpiresIn=expires_in
)
return url
46 changes: 44 additions & 2 deletions main/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from rest_framework import serializers
from django.db.models import Avg

from .models import Calculator, Course, Profile, Review, ScoreComponent, Tag, Bookmark, UserCumulativeGPA, UserGPA, CourseSemester, ScoreSubcomponent
from .models import Calculator, Course, Profile, Question, Review, ScoreComponent, Tag, Bookmark, UserCumulativeGPA, UserGPA, CourseSemester, ScoreSubcomponent

# class CurriculumSerializer(serializers.ModelSerializer):
# class Meta:
Expand Down Expand Up @@ -335,4 +335,46 @@ def get_courses_calculator(self, obj):
return CalculatorSerializer(list_calculator, many=True).data

def get_semester(self, obj):
return UserGPASerializer(obj).data
return UserGPASerializer(obj).data

class AddQuestionSerializer(serializers.Serializer):
attachment_file = serializers.FileField()
course_id = serializers.IntegerField()
question_text = serializers.CharField()
is_anonym = serializers.IntegerField()

class TanyaTemanProfileSerializer(serializers.ModelSerializer):
program = serializers.SerializerMethodField('get_cleaned_program')
generation = serializers.SerializerMethodField('get_generation')

class Meta:
model = Profile
fields = ['name', 'program', 'generation']

def get_cleaned_program(self, obj):
# Previously, the format of study program is "Ilmu Komputer (Computer Science)"
# So we need to remove the excess translation
cleaned_study_program = str(obj.study_program).strip()
if "(" in cleaned_study_program:
cleaned_study_program = cleaned_study_program[:cleaned_study_program.find("(")]

return cleaned_study_program.strip()

def get_generation(self, obj):
generation = 2000 + int(obj.npm[:2])
return str(generation)

class QuestionSerializer(serializers.ModelSerializer):
user = TanyaTemanProfileSerializer()
course = CourseForSemesterSerializer()
attachment_url = serializers.SerializerMethodField('get_attachment_url')

class Meta:
model = Question
fields = ['id', 'user', 'question_text', 'course', 'is_anonym', 'attachment_url', 'like_count', 'verification_status', 'created_at', 'updated_at']

def get_user(self, obj):
return obj.user.username

def get_attachment_url(self, obj):
return obj.get_attachment_presigned_url()
5 changes: 4 additions & 1 deletion main/urls.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from django import conf
from rest_framework import routers
from django.urls import path, include

from main.views_tanyateman import tanya_teman
from .views_gpa_calculator import course_semester, gpa_calculator, gpa_calculator_with_semester, course_semester_with_course_id, course_component, course_subcomponent
from .views_calculator import calculator, score_component
from .views import like, tag, bookmark, account, leaderboard
Expand All @@ -26,6 +28,7 @@
path('course-semester', course_semester, name='course-semester'),
path('course-semester/<str:course_id>', course_semester_with_course_id, name="course-with-id"),
path('course-component', course_component, name="course-component"),
path('course-subcomponent', course_subcomponent, name="course-subcomponent")
path('course-subcomponent', course_subcomponent, name="course-subcomponent"),
path('tanya-teman', tanya_teman, name='tanya-teman')
] + router.urls

17 changes: 15 additions & 2 deletions main/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from rest_framework.response import Response
from rest_framework import status

from .models import Calculator, Course, Profile, ScoreComponent, UserCumulativeGPA, UserGPA, ScoreSubcomponent
from .models import Calculator, Course, CourseSemester, Profile, ScoreComponent, UserCumulativeGPA, UserGPA, ScoreSubcomponent
from .fasilkom_courses import IK_COURSES, SI_COURSES


Expand Down Expand Up @@ -208,4 +208,17 @@ def get_recommended_score(calculator: Calculator, target_score: int) -> float :
if percentage_left == 0:
return 0

return score_left / percentage_left * 100
return score_left / percentage_left * 100

def get_max_possible_score(calculator: Calculator) -> float :
current_score = calculator.total_score
percentage_left = get_null_sum_from_calculator(calculator)

return current_score + percentage_left

def delete_semester(semester: UserGPA):
course_semesters = CourseSemester.objects.filter(semester=semester)
for course_semester in course_semesters:
calculator = course_semester.calculator
calculator.delete()
semester.delete()
19 changes: 10 additions & 9 deletions main/views_gpa_calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from main.views_calculator import score_component
from .serializers import CalculatorSerializer, ScoreComponentSerializer, UserCumulativeGPASerializer, UserGPASerializer, CourseForSemesterSerializer, SemesterWithCourseSerializer

from .utils import get_recommended_score, get_score, response, update_course_score, validate_body, check_notexist_and_create_user_cumulative_gpa, validate_body_minimum, add_semester_gpa, delete_semester_gpa, update_semester_gpa, update_cumulative_gpa, get_fasilkom_courses, add_course_to_semester, validate_params, delete_course_to_semester
from .utils import get_recommended_score, get_score, response, update_course_score, validate_body, check_notexist_and_create_user_cumulative_gpa, validate_body_minimum, add_semester_gpa, delete_semester_gpa, update_semester_gpa, update_cumulative_gpa, get_fasilkom_courses, add_course_to_semester, validate_params, delete_course_to_semester, get_max_possible_score, delete_semester
from .models import Calculator, Profile, ScoreComponent, UserCumulativeGPA, UserGPA, Course, CourseSemester, ScoreSubcomponent
from django.db.models import F

Expand Down Expand Up @@ -37,7 +37,10 @@ def gpa_calculator(request):
})

if request.method == 'DELETE':
UserGPA.objects.filter(userCumulativeGPA__user=user).delete()
user_gpas = UserGPA.objects.filter(userCumulativeGPA__user=user)
for user_gpa in user_gpas:
delete_semester(user_gpa)

user_cumulative_gpa.total_gpa = 0
user_cumulative_gpa.total_sks = 0
update_cumulative_gpa(user_cumulative_gpa)
Expand Down Expand Up @@ -130,7 +133,7 @@ def gpa_calculator_with_semester(request, given_semester):
return response(error="There is no object with given_semester={}".format(given_semester), status=status.HTTP_404_NOT_FOUND)

delete_semester_gpa(user_cumulative_gpa, total_sks=user_gpa.total_sks,semester_gpa=user_gpa.semester_gpa)
user_gpa.delete()
delete_semester(user_gpa)

return response(status=status.HTTP_204_NO_CONTENT)

Expand Down Expand Up @@ -195,8 +198,6 @@ def course_semester(request):

valid_course_ids.append(course_id)

print(valid_course_ids)

for course_id in valid_course_ids:
course = Course.objects.filter(pk=course_id).first()

Expand Down Expand Up @@ -239,7 +240,7 @@ def course_semester_with_course_id(request, course_id):
delete_course_to_semester(semester=semester, sks=course_semester.course.sks, score=get_score(course_semester.calculator.total_score))
delete_semester_gpa(user_cumulative_gpa=user_cumulative_gpa,
total_sks=course_semester.course.sks,
semester_gpa=0)
semester_gpa=get_score(course_semester.calculator.total_score))

course_semester.delete()
return response(status=status.HTTP_204_NO_CONTENT)
Expand All @@ -266,7 +267,8 @@ def course_component(request):
return response(data={
'score_component': ScoreComponentSerializer(score_components, many=True).data,
'calculator': CalculatorSerializer(calculator).data,
'recommended_score': get_recommended_score(calculator, target_score)
'recommended_score': get_recommended_score(calculator, target_score),
'max_possible_score': get_max_possible_score(calculator)
})

if request.method == 'POST':
Expand Down Expand Up @@ -341,7 +343,7 @@ def course_component(request):
score_component.delete()
calculator.save()

return response(status=status.HTTP_200_OK)
return response(status=status.HTTP_204_NO_CONTENT)

@api_view(['GET', 'POST', 'PUT'])
def course_subcomponent(request):
Expand Down Expand Up @@ -500,5 +502,4 @@ def course_subcomponent(request):
cur_sks=course.sks, cur_score=get_score(calculator.total_score))

score_component_value = ScoreComponent.objects.filter(calculator=calculator, name=name, weight=weight, score=component_total_score).first()
print("::", score_component_value)
return response(data=ScoreComponentSerializer(score_component_value).data, status=status.HTTP_201_CREATED)
Loading

0 comments on commit 55513d3

Please sign in to comment.