Skip to content

Commit

Permalink
Merge pull request #2265 from gtech-mulearn/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
nashnsulthan authored Nov 5, 2024
2 parents c722c8e + 9b8bf4e commit 4e4d34a
Show file tree
Hide file tree
Showing 9 changed files with 1,129 additions and 107 deletions.
96 changes: 96 additions & 0 deletions alter-scripts/alter-1.59.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import os
import sys
import uuid
from decouple import config
import django

from connection import execute

os.chdir("..")
sys.path.append(os.getcwd())
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mulearnbackend.settings")
django.setup()


def get_recurring_lc_ids():
query = "SELECT id, day from learning_circle where day is not null"
return execute(query)


def migrate_to_new_lc():
lcs = get_recurring_lc_ids()
execute("DROP TABLE IF EXISTS circle_meet_attendees;")
execute("DROP TABLE IF EXISTS circle_meet_tasks;")
execute("DROP TABLE IF EXISTS circle_meet_attendee_report;")
execute("DROP TABLE IF EXISTS circle_meeting_log;")
execute(
"""
ALTER TABLE learning_circle
MODIFY COLUMN name VARCHAR(255),
MODIFY COLUMN circle_code VARCHAR(15),
DROP COLUMN meet_time,
DROP COLUMN day,
DROP COLUMN meet_place,
DROP FOREIGN KEY fk_learning_circle_ref_updated_by,
DROP COLUMN updated_by,
ADD COLUMN is_recurring BOOLEAN DEFAULT FALSE NOT NULL,
ADD COLUMN recurrence_type VARCHAR(10),
ADD COLUMN recurrence INT;
"""
)
execute(
"""
CREATE TABLE circle_meeting_log
(
id VARCHAR(36) PRIMARY KEY NOT NULL,
circle_id VARCHAR(36) NOT NULL,
meet_code VARCHAR(6) NOT NULL,
title VARCHAR(100) NOT NULL,
is_report_needed BOOLEAN DEFAULT TRUE NOT NULL,
report_description VARCHAR(1000),
coord_x FLOAT NOT NULL NOT NULL,
coord_y FLOAT NOT NULL NOT NULL,
meet_place VARCHAR(255) NOT NULL,
meet_time DATETIME NOT NULL,
duration INT NOT NULL,
is_report_submitted BOOLEAN DEFAULT FALSE NOT NULL,
is_approved BOOLEAN DEFAULT FALSE NOT NULL,
report_text VARCHAR(1000),
created_by VARCHAR(36) NOT NULL,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
CONSTRAINT fk_circle_meeting_log_ref_circle_id FOREIGN KEY (circle_id) REFERENCES learning_circle (id) ON DELETE CASCADE,
CONSTRAINT fk_circle_meeting_log_ref_created_by FOREIGN KEY (created_by) REFERENCES user (id) ON DELETE CASCADE
);
"""
)
execute(
"""
CREATE TABLE circle_meet_attendees (
id VARCHAR(36) PRIMARY KEY NOT NULL,
user_id VARCHAR(36) NOT NULL,
meet_id VARCHAR(36) NOT NULL,
is_joined BOOLEAN DEFAULT FALSE NOT NULL,
joined_at DATETIME,
is_report_submitted BOOLEAN DEFAULT FALSE NOT NULL,
is_lc_approved BOOLEAN DEFAULT FALSE NOT NULL,
report_text VARCHAR(1000),
report_link VARCHAR(200),
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
CONSTRAINT fk_circle_meet_attendees_ref_meet_id FOREIGN KEY (meet_id) REFERENCES circle_meeting_log (id) ON DELETE CASCADE,
CONSTRAINT fk_circle_meet_attendees_ref_user_id FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE
);
"""
)
for id, day in lcs:
day = str(day).split(",")[0]
query = f"""UPDATE learning_circle SET is_recurring = TRUE, recurrence_type = 'weekly', recurrence = {day} WHERE id = '{id}'"""
execute(query)


if __name__ == "__main__":
migrate_to_new_lc()
execute(
"UPDATE system_setting SET value = '1.59', updated_at = now() WHERE `key` = 'db.version';"
)
301 changes: 301 additions & 0 deletions api/dashboard/learningcircle/learningcircle_serializer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
from datetime import datetime, timedelta, timezone

import pytz
from db.learning_circle import LearningCircle, CircleMeetingLog, CircleMeetingAttendees
from rest_framework import serializers

from db.organization import Organization
from db.task import InterestGroup
from utils.types import LearningCircleRecurrenceType
from utils.utils import DateTimeUtils


class LearningCircleCreateEditSerialzier(serializers.ModelSerializer):
ig = serializers.PrimaryKeyRelatedField(
queryset=InterestGroup.objects.all(), required=True
)
org = serializers.PrimaryKeyRelatedField(
queryset=Organization.objects.all(), required=False, allow_null=True
)

def update(self, instance, validated_data):
instance.ig_id = validated_data.get("ig_id", instance.ig_id)
instance.org_id = validated_data.get("org_id", instance.org_id)
instance.is_recurring = validated_data.get(
"is_recurring", instance.is_recurring
)
instance.recurrence_type = validated_data.get(
"recurrence_type", instance.recurrence_type
)
instance.recurrence = validated_data.get("recurrence", instance.recurrence)
instance.updated_at = DateTimeUtils.get_current_utc_time()
instance.save()
return instance

def create(self, validated_data):
user_id = self.context.get("user_id")
validated_data["created_by_id"] = user_id
return LearningCircle.objects.create(**validated_data)

def validate(self, attrs):
is_recurring = attrs.get("is_recurring")
recurrence_type = attrs.get("recurrence_type")
recurrence = attrs.get("recurrence")
if not is_recurring:
attrs["recurrence_type"] = None
attrs["recurrence"] = None
else:
if not recurrence_type or not recurrence:
raise serializers.ValidationError(
"Recurrence type and recurrence are required for recurring learning circles"
)
if recurrence_type not in LearningCircleRecurrenceType.get_all_values():
raise serializers.ValidationError("Invalid recurrence type.")
if recurrence_type == LearningCircleRecurrenceType.WEEKLY.value:
if recurrence < 1 or recurrence > 7:
raise serializers.ValidationError(
"Recurrence should be between 1 and 7 for weekly learning circles"
)
elif recurrence_type == LearningCircleRecurrenceType.MONTHLY.value:
if recurrence < 1 or recurrence > 28:
raise serializers.ValidationError(
"Recurrence should be between 1 and 28 for monthly learning circles"
)
return super().validate(attrs)

class Meta:
model = LearningCircle
fields = ["ig", "org", "is_recurring", "recurrence_type", "recurrence"]


class LearningCircleListSerializer(serializers.ModelSerializer):
ig = serializers.CharField(source="ig.name", read_only=True)
org = serializers.CharField(source="org.name", read_only=True, allow_null=True)
created_by = serializers.CharField(source="created_by_id.full_name", read_only=True)
next_meetup = serializers.SerializerMethodField()

def _get_next_weekday(self, target_day: int):
today = datetime.now()
current_day = today.isoweekday() + 2
current_day = current_day if current_day <= 7 else 1
days_until_next = ((target_day - current_day + 7) % 7) + 1
days_until_next = days_until_next or 7
next_day_date = today + timedelta(days=days_until_next)
return next_day_date.date()

def _get_month_day(self, target_day: int):
today = datetime.now()
current_day = today.day
current_month = today.month
current_year = today.year
if current_day >= target_day:
current_month += 1
if current_month > 12:
current_month = 1
current_year += 1
return datetime(current_year, current_month, target_day).date()

def get_next_meetup(self, obj):
next_meetup = (
CircleMeetingLog.objects.filter(circle_id=obj.id)
.filter(
# meet_time__gte=DateTimeUtils.get_current_utc_time(),
is_report_submitted=False,
)
.order_by("-meet_time")
.first()
)
if next_meetup:
return {
**CircleMeetingLogListSerializer(next_meetup).data,
"is_scheduled": True,
}
if not obj.is_recurring:
return None
if obj.recurrence_type == LearningCircleRecurrenceType.WEEKLY.value:
return {
"is_scheduled": False,
"meet_time": self._get_next_weekday(obj.recurrence),
}
if obj.recurrence_type == LearningCircleRecurrenceType.MONTHLY.value:
return {
"is_scheduled": False,
"meet_time": self._get_month_day(obj.recurrence),
}
return {"is_scheduled": False, "meet_time": None}

class Meta:
model = LearningCircle
fields = [
"id",
"ig",
"org",
"is_recurring",
"recurrence_type",
"recurrence",
"created_by",
"next_meetup",
]


class CircleMeetingLogCreateEditSerializer(serializers.ModelSerializer):
circle_id = serializers.PrimaryKeyRelatedField(
queryset=LearningCircle.objects.all(), required=True
)

def update(self, instance, validated_data):
instance.title = validated_data.get("title", instance.title)
instance.is_report_needed = validated_data.get(
"is_report_needed", instance.is_report_needed
)
instance.report_description = validated_data.get(
"report_description", instance.report_description
)
instance.coord_x = validated_data.get("coord_x", instance.coord_x)
instance.coord_y = validated_data.get("coord_y", instance.coord_y)
instance.meet_place = validated_data.get("meet_place", instance.meet_place)
instance.meet_time = validated_data.get("meet_time", instance.meet_time)
instance.duration = validated_data.get("duration", instance.duration)
instance.updated_at = DateTimeUtils.get_current_utc_time()
instance.save()
return instance

def create(self, validated_data):
user_id = self.context.get("user_id")
meet_code = self.context.get("meet_code")
validated_data["created_by_id"] = user_id
validated_data["meet_code"] = meet_code
meet = CircleMeetingLog.objects.create(**validated_data)
CircleMeetingAttendees.objects.create(
meet_id=meet, user_id_id=user_id, is_joined=True, joined_at=datetime.now()
)
return meet

def validate(self, attrs):
is_report_needed = attrs.get("is_report_needed")
report_description = attrs.get("report_description")
if not is_report_needed:
attrs["report_description"] = None
else:
if not report_description:
raise serializers.ValidationError("Report description is required")
return super().validate(attrs)

def validate_circle_id(self, value):
if CircleMeetingLog.objects.filter(
circle_id=value, is_report_submitted=False
).exists():
raise serializers.ValidationError(
"There is already an ongoing meeting for this learning circle"
)
return value

class Meta:
model = CircleMeetingLog
fields = [
"circle_id",
"title",
"is_report_needed",
"report_description",
"coord_x",
"coord_y",
"meet_place",
"meet_time",
"duration",
]


class CircleMeetingLogListSerializer(serializers.ModelSerializer):
circle = serializers.CharField(source="circle_id.id", read_only=True)
created_by = serializers.CharField(source="created_by_id.full_name", read_only=True)
is_started = serializers.SerializerMethodField()
is_ended = serializers.SerializerMethodField()

def get_is_started(self, obj):
return obj.meet_time <= DateTimeUtils.get_current_utc_time()

def get_is_ended(self, obj):
return (obj.meet_time + timedelta(hours=obj.duration + 1)) <= datetime.now(
timezone.utc
)

class Meta:
model = CircleMeetingLog
fields = [
"id",
"circle",
"meet_code",
"title",
"is_report_needed",
"report_description",
"coord_x",
"coord_y",
"meet_place",
"meet_time",
"duration",
"created_by",
"is_started",
"is_ended",
"is_report_submitted",
]


class CircleMeetingAttendeeSerializer(serializers.ModelSerializer):

class Meta:
model = CircleMeetingAttendees
fields = ["is_joined", "is_report_submitted", "is_lc_approved"]


class CircleMeetupInfoSerializer(serializers.ModelSerializer):
title = serializers.CharField(read_only=True)
is_report_needed = serializers.BooleanField(read_only=True)
report_description = serializers.CharField(read_only=True)
coord_x = serializers.FloatField(read_only=True)
coord_y = serializers.FloatField(read_only=True)
meet_place = serializers.CharField(read_only=True)
meet_time = serializers.DateTimeField(read_only=True)
duration = serializers.IntegerField(read_only=True)
is_approved = serializers.BooleanField(read_only=True)
is_started = serializers.SerializerMethodField()
is_ended = serializers.SerializerMethodField()
attendee = serializers.SerializerMethodField()

def get_is_started(self, obj):
return obj.meet_time <= DateTimeUtils.get_current_utc_time()

def get_is_ended(self, obj):
return (obj.meet_time + timedelta(hours=obj.duration + 1)) <= datetime.now(
timezone.utc
)

def get_attendee(self, obj):
if user_id := self.context.get("user_id"):
user_obj = obj.circle_meeting_attendance_meet_id.filter(
user_id=user_id
).first()
if not user_obj:
return None
return CircleMeetingAttendeeSerializer(
user_obj,
many=False,
).data
return None

class Meta:
model = CircleMeetingLog
fields = [
"id",
"title",
"is_report_needed",
"report_description",
"coord_x",
"coord_y",
"meet_place",
"meet_time",
"duration",
"is_approved",
"is_started",
"is_ended",
"attendee",
]
Loading

0 comments on commit 4e4d34a

Please sign in to comment.