diff --git a/api/dashboard/campus/campus_views.py b/api/dashboard/campus/campus_views.py index 5909e8e1..f64102aa 100644 --- a/api/dashboard/campus/campus_views.py +++ b/api/dashboard/campus/campus_views.py @@ -1,16 +1,16 @@ from django.db.models import Count, F -from rest_framework.views import APIView from django.db.models import Q, Case, When, Value +from rest_framework.views import APIView -from db.user import User -from . import serializers +from db.organization import UserOrganizationLink from db.task import Level, Wallet +from db.user import User +from utils.permission import CustomizePermission, JWTUtils, role_required from utils.response import CustomResponse from utils.types import OrganizationType, RoleType from utils.utils import CommonUtils, DateTimeUtils +from . import serializers from .dash_campus_helper import get_user_college_link -from utils.permission import CustomizePermission, JWTUtils, role_required -from utils.exception import CustomException class CampusDetailsAPI(APIView): @@ -29,7 +29,7 @@ class CampusDetailsAPI(APIView): authentication_classes = [CustomizePermission] # Use the role_required decorator to specify the allowed roles for this view - @role_required([RoleType.CAMPUS_LEAD.value, RoleType.ENABLER.value]) + @role_required([RoleType.CAMPUS_LEAD.value, RoleType.ENABLER.value, RoleType.LEAD_ENABLER.value]) def get(self, request): # Fetch the user's ID from the request using JWTUtils user_id = JWTUtils.fetch_user_id(request) @@ -44,7 +44,7 @@ def get(self, request): general_message="Campus lead has no college" ).get_failure_response() - # Serialize the user's organization link using the CampusDetailsSerializer + # # Serialize the user's organization link using the CampusDetailsSerializer serializer = serializers.CampusDetailsSerializer(user_org_link, many=False) # Return a success response with the serialized data @@ -54,7 +54,7 @@ def get(self, request): class CampusStudentInEachLevelAPI(APIView): authentication_classes = [CustomizePermission] - @role_required([RoleType.CAMPUS_LEAD.value, RoleType.ENABLER.value]) + @role_required([RoleType.CAMPUS_LEAD.value, RoleType.ENABLER.value, RoleType.LEAD_ENABLER.value]) def get(self, request): user_id = JWTUtils.fetch_user_id(request) @@ -80,12 +80,11 @@ def get(self, request): class CampusStudentDetailsAPI(APIView): authentication_classes = [CustomizePermission] - @role_required([RoleType.CAMPUS_LEAD.value, RoleType.ENABLER.value]) + @role_required([RoleType.CAMPUS_LEAD.value, RoleType.ENABLER.value, RoleType.LEAD_ENABLER.value]) def get(self, request): user_id = JWTUtils.fetch_user_id(request) user_org_link = get_user_college_link(user_id) - - start_date, end_date = DateTimeUtils.get_start_and_end_of_previous_month() + is_alumni = request.query_params.get("is_alumni", "").lower() in ["1", "true"] if user_org_link.org is None: return CustomResponse( @@ -96,6 +95,7 @@ def get(self, request): Wallet.objects.filter( user__user_organization_link_user__org=user_org_link.org, user__user_organization_link_user__org__org_type=OrganizationType.COLLEGE.value, + user__user_organization_link_user__is_alumni=is_alumni, ) .distinct() .order_by("-karma", "-created_at") @@ -111,13 +111,18 @@ def get(self, request): User.objects.filter( user_organization_link_user__org=user_org_link.org, user_organization_link_user__org__org_type=OrganizationType.COLLEGE.value, + user_organization_link_user__is_alumni=is_alumni, ) .distinct() .annotate( user_id=F("id"), + email_=F("email"), + mobile_=F("mobile"), karma=F("wallet_user__karma"), level=F("user_lvl_link_user__level__name"), join_date=F("created_at"), + department=F('user_organization_link_user__department__title'), + graduation_year=F("user_organization_link_user__graduation_year") )) paginated_queryset = CommonUtils.get_paginated_queryset( @@ -150,7 +155,7 @@ def get(self, request): class CampusStudentDetailsCSVAPI(APIView): authentication_classes = [CustomizePermission] - @role_required([RoleType.CAMPUS_LEAD.value, RoleType.ENABLER.value]) + @role_required([RoleType.CAMPUS_LEAD.value, RoleType.ENABLER.value, RoleType.LEAD_ENABLER.value]) def get(self, request): user_id = JWTUtils.fetch_user_id(request) user_org_link = get_user_college_link(user_id) @@ -210,7 +215,7 @@ def get(self, request): class WeeklyKarmaAPI(APIView): authentication_classes = [CustomizePermission] - @role_required([RoleType.CAMPUS_LEAD.value, RoleType.ENABLER.value]) + @role_required([RoleType.CAMPUS_LEAD.value, RoleType.ENABLER.value, RoleType.LEAD_ENABLER.value]) def get(self, request): user_id = JWTUtils.fetch_user_id(request) @@ -222,4 +227,23 @@ def get(self, request): ).get_failure_response() serializer = serializers.WeeklyKarmaSerializer(user_org_link) - return CustomResponse(response=serializer.data).get_success_response() \ No newline at end of file + return CustomResponse(response=serializer.data).get_success_response() + + +class ChangeStudentTypeAPI(APIView): + authentication_classes = [CustomizePermission] + + @role_required([RoleType.CAMPUS_LEAD.value, RoleType.ENABLER.value, RoleType.LEAD_ENABLER.value]) + def patch(self, request, member_id): + user_id = JWTUtils.fetch_user_id(request) + + user_org_link = get_user_college_link(user_id) + user_org_link_obj = UserOrganizationLink.objects.filter(user__id=member_id, + org=user_org_link.org, + org__org_type=OrganizationType.COLLEGE.value).first() + + serializer = serializers.ChangeStudentTypeSerializer(user_org_link_obj, data=request.data) + if serializer.is_valid(): + serializer.save() + return CustomResponse(general_message='Student Type updated successfully').get_success_response() + return CustomResponse(message=serializer.errors).get_failure_response() diff --git a/api/dashboard/campus/serializers.py b/api/dashboard/campus/serializers.py index b3e62450..b35db335 100644 --- a/api/dashboard/campus/serializers.py +++ b/api/dashboard/campus/serializers.py @@ -3,9 +3,11 @@ from django.db.models import Sum from rest_framework import serializers -from db.organization import UserOrganizationLink +from db.organization import UserOrganizationLink, College from db.task import KarmaActivityLog +from db.user import User from utils.types import OrganizationType +from utils.types import RoleType from utils.utils import DateTimeUtils @@ -14,11 +16,14 @@ class CampusDetailsSerializer(serializers.ModelSerializer): campus_code = serializers.ReadOnlyField(source="org.code") campus_zone = serializers.ReadOnlyField(source="org.district.zone.name") campus_lead = serializers.ReadOnlyField(source="user.fullname") + campus_level = serializers.SerializerMethodField() total_karma = serializers.SerializerMethodField() total_members = serializers.SerializerMethodField() active_members = serializers.SerializerMethodField() rank = serializers.SerializerMethodField() + lead = serializers.SerializerMethodField() + class Meta: model = UserOrganizationLink fields = [ @@ -26,21 +31,51 @@ class Meta: "campus_lead", "campus_code", "campus_zone", + "campus_level", "total_karma", "total_members", "active_members", "rank", + "lead", ] + def get_lead(self, obj): + + campus_lead = User.objects.filter( + user_organization_link_user__org=obj.org, + user_organization_link_user__org__org_type=OrganizationType.COLLEGE.value, + user_role_link_user__role__title=RoleType.CAMPUS_LEAD.value, + ).first() + if campus_lead: + campus_lead = campus_lead.fullname + + enabler = User.objects.filter( + user_organization_link_user__org=obj.org, + user_organization_link_user__org__org_type=OrganizationType.COLLEGE.value, + user_role_link_user__role__title=RoleType.ENABLER.value, + ).first() + if enabler: + enabler = enabler.fullname + + return {'campus_lead': campus_lead, 'enabler': enabler} + + def get_campus_level(self, obj): + campus = College.objects.filter(org=obj.org).first() + if campus: + return campus.level + + return None + def get_total_members(self, obj): return obj.org.user_organization_link_org.count() def get_active_members(self, obj): - last_month = DateTimeUtils.get_current_utc_time() - timedelta(days=30) + + last_month = DateTimeUtils.get_current_utc_time() - timedelta(weeks=26) # 6months return obj.org.user_organization_link_org.filter( verified=True, user__wallet_user__isnull=False, - user__wallet_user__created_at__gte=last_month, + user__wallet_user__karma_last_update_at__gte=last_month, ).count() def get_total_karma(self, obj): @@ -84,9 +119,13 @@ class CampusStudentDetailsSerializer(serializers.Serializer): level = serializers.CharField() # is_active = serializers.CharField() join_date = serializers.CharField() + email = serializers.CharField() + mobile = serializers.CharField() + graduation_year = serializers.CharField() + department = serializers.CharField() class Meta: - fields = ("user_id", "fullname", "karma", "muid", "rank", "level", "join_date") + fields = ("user_id", "email", "mobile", "fullname", "karma", "muid", "rank", "level", "join_date") def get_rank(self, obj): ranks = self.context.get("ranks") @@ -121,3 +160,30 @@ def to_representation(self, instance): response[str(date)] = karma_logs.get("karma", 0) return response + + +class ChangeStudentTypeSerializer(serializers.Serializer): + is_alumni = serializers.BooleanField(default=False) + + class Meta: + model = UserOrganizationLink + fields = ("is_alumni",) + + def update(self, instance, validated_data): + instance.is_alumni = validated_data.get('is_alumni') + instance.save() + + return instance + + +class ListAluminiSerializer(serializers.Serializer): + user_id = serializers.CharField() + fullname = serializers.SerializerMethodField() + muid = serializers.CharField() + karma = serializers.IntegerField() + rank = serializers.SerializerMethodField() + level = serializers.CharField() + join_date = serializers.CharField() + + class Meta: + fields = ("user_id", "fullname", "karma", "muid", "rank", "level", "join_date") diff --git a/api/dashboard/campus/urls.py b/api/dashboard/campus/urls.py index 012b7a66..7ac88f98 100644 --- a/api/dashboard/campus/urls.py +++ b/api/dashboard/campus/urls.py @@ -8,4 +8,6 @@ path("student-details/", campus_views.CampusStudentDetailsAPI.as_view(), name='student-details'), path("student-details/csv/", campus_views.CampusStudentDetailsCSVAPI.as_view(), name='student-details-csv'), path("weekly-karma/", campus_views.WeeklyKarmaAPI.as_view(), name='weekly-karma-insights'), + + path('change-student-type//', campus_views.ChangeStudentTypeAPI.as_view(), name='change-student-type') ] diff --git a/api/dashboard/college/college_view.py b/api/dashboard/college/college_view.py index 45f973db..09f0f01d 100644 --- a/api/dashboard/college/college_view.py +++ b/api/dashboard/college/college_view.py @@ -1,89 +1,30 @@ from rest_framework.views import APIView -from utils.types import OrganizationType, RoleType + from db.organization import College -from db.user import User -from utils.permission import JWTUtils from utils.response import CustomResponse +from utils.utils import CommonUtils from .serializer import ( - CollegeCreateDeleteSerializer, CollegeListSerializer, - CollegeEditSerializer, ) -from utils.utils import CommonUtils -from django.db.models import Case,When,CharField,F + class CollegeApi(APIView): def get(self, request, college_code=None): if college_code: colleges = College.objects.filter(id=college_code) else: - colleges = College.objects.all().select_related( - "created_by", "updated_by", "org" - ) + colleges = College.objects.all().select_related("org") - leads = ( - User.objects.filter( - user_organization_link_user__org__org_type=OrganizationType.COLLEGE.value, - user_role_link_user__role__title=RoleType.CAMPUS_LEAD.value, - ) - .distinct() - .annotate( - college=Case( - When( - user_organization_link_user__org__org_type=OrganizationType.COLLEGE.value, - then=F("user_organization_link_user__org__id"), - ), - default=None, - output_field=CharField(), - ) - ) - ) paginated_queryset = CommonUtils.get_paginated_queryset( colleges, request, - search_fields=["created_by__firstname"], - sort_fields={"created_by": "created_by__firstname"}, + search_fields=[""], + sort_fields=None, ) serializer = CollegeListSerializer( - paginated_queryset.get("queryset"), many=True,context={"leads":leads} + paginated_queryset.get("queryset"), many=True ) return CustomResponse().paginated_response( data=serializer.data, pagination=paginated_queryset.get("pagination") ) - - def post(self, request): - request_data = request.data - request_data["created_by"] = request_data[ - "updated_by" - ] = JWTUtils.fetch_user_id(request) - - serializer = CollegeCreateDeleteSerializer(data=request_data) - if serializer.is_valid(): - serializer.save() - return CustomResponse(serializer.data).get_success_response() - return CustomResponse(message=serializer.errors).get_failure_response() - - -class CollegeUpdateDeleteApi(APIView): - def delete(self, request, college_id): - if college := College.objects.filter(id=college_id).first(): - college.delete() - return CustomResponse( - general_message="College succesfully deleted" - ).get_success_response() - return CustomResponse(general_message="Invalid college").get_failure_response() - - def patch(self, request, college_id): - college = College.objects.filter(id=college_id).first() - request_data = request.data - request_data["updated_by"] = JWTUtils.fetch_user_id(request) - if college is None: - return CustomResponse( - general_message="Invalid college" - ).get_failure_response() - serializer = CollegeEditSerializer(college, data=request_data, partial=True) - if serializer.is_valid(): - serializer.save() - return CustomResponse(serializer.data).get_success_response() - return CustomResponse(message=serializer.errors).get_failure_response() diff --git a/api/dashboard/college/serializer.py b/api/dashboard/college/serializer.py index 653c7a06..4a71a473 100644 --- a/api/dashboard/college/serializer.py +++ b/api/dashboard/college/serializer.py @@ -1,77 +1,75 @@ -import uuid -from utils.utils import DateTimeUtils -from rest_framework import serializers -from db.organization import College, Organization -from utils.types import RoleType, OrganizationType +from datetime import timedelta + from django.db.models import Sum +from rest_framework import serializers + from db.learning_circle import LearningCircle +from db.organization import College +from utils.types import RoleType, OrganizationType +from utils.utils import DateTimeUtils class CollegeListSerializer(serializers.ModelSerializer): - created_by = serializers.CharField(source="created_by.fullname") - updated_by = serializers.CharField(source="updated_by.fullname") org = serializers.CharField(source="org.title") - number_of_students = serializers.SerializerMethodField() + number_of_members = serializers.SerializerMethodField() total_karma = serializers.SerializerMethodField() no_of_lc = serializers.SerializerMethodField() + no_of_alumni = serializers.SerializerMethodField() class Meta: model = College fields = [ "id", - "level", + # "level", "org", - "updated_by", - "created_by", - "updated_at", - "created_at", - "number_of_students", + "number_of_members", "total_karma", "no_of_lc", + "no_of_alumni", ] + def get_no_of_alumni(self, obj): + return obj.org.user_organization_link_org.filter( + org__org_type=OrganizationType.COLLEGE.value, + user__user_role_link_user__role__title=RoleType.STUDENT.value, + is_alumni=True, + verified=True, + ).count() + def get_no_of_lc(self, obj): learning_circle_count = LearningCircle.objects.filter(org=obj.org).count() - return learning_circle_count + LearningCircle.objects.filter(org=obj.org, + created_at__lte=DateTimeUtils.get_current_utc_time() - timedelta(days=30)).count() + return {'lc_count': learning_circle_count, 'no_of_lc_increased': 1} - def get_number_of_students(self, obj): - return obj.org.user_organization_link_org.filter( - user__user_role_link_user__role__title=RoleType.STUDENT.value + def get_number_of_members(self, obj): + member_count = obj.org.user_organization_link_org.all().count() + + no_of_members_increased = obj.org.user_organization_link_org.filter( + created_at__lte=DateTimeUtils.get_current_utc_time() - timedelta(days=30) ).count() + return {'member_count': member_count, 'no_of_members_increased': no_of_members_increased} def get_total_karma(self, obj): - return ( + total_karma_gained = ( obj.org.user_organization_link_org.filter( org__org_type=OrganizationType.COLLEGE.value, + is_alumni=False, verified=True, user__wallet_user__isnull=False, ).aggregate(total_karma=Sum("user__wallet_user__karma"))["total_karma"] or 0 ) - - # def get_lead_name(self, obj): - # leads = self.context.get("leads") - # college_lead = [lead for lead in leads if lead.college == obj.id] - # return college_lead.fullname if college_lead else None - - # def get_lead_contact(self, obj): - # leads = self.context.get("leads") - # college_lead = [lead for lead in leads if lead.college == obj.id] - # return college_lead.mobile if college_lead else None - - -class CollegeCreateDeleteSerializer(serializers.ModelSerializer): - class Meta: - model = College - fields = [ - "level", - "org", - "updated_by", - "created_by", - ] - - -class CollegeEditSerializer(serializers.ModelSerializer): - class Meta: - model = College - fields = ["level", "updated_by"] + total_karma_increased = ( + obj.org.user_organization_link_org.filter( + org__org_type=OrganizationType.COLLEGE.value, + is_alumni=False, + verified=True, + user__wallet_user__isnull=False, + user__wallet_user__created_at__gt=DateTimeUtils.get_current_utc_time() - timedelta(days=30), + ).aggregate(total_karma=Sum("user__wallet_user__karma"))["total_karma"] + or 0 + ) + increased_percentage = total_karma_increased * 100 / 100 + return {'total_karma_gained': total_karma_gained, 'total_karma_increased': total_karma_increased, + 'increased_percentage': increased_percentage} diff --git a/api/dashboard/college/urls.py b/api/dashboard/college/urls.py index e85f9b7c..c4bb5d45 100644 --- a/api/dashboard/college/urls.py +++ b/api/dashboard/college/urls.py @@ -4,6 +4,5 @@ urlpatterns = [ path('', college_view.CollegeApi.as_view()), - path('delete//', college_view.CollegeUpdateDeleteApi.as_view()), path('/', college_view.CollegeApi.as_view()) ] diff --git a/api/dashboard/lc/dash_lc_serializer.py b/api/dashboard/lc/dash_lc_serializer.py index e756a0dc..3d3f47b4 100644 --- a/api/dashboard/lc/dash_lc_serializer.py +++ b/api/dashboard/lc/dash_lc_serializer.py @@ -1,12 +1,14 @@ import uuid -from django.db.models import Sum, Count +from django.db.models import Sum, Count, Value, CharField +from django.db.models.functions import Concat from rest_framework import serializers from db.learning_circle import LearningCircle, UserCircleLink, InterestGroup, CircleMeetingLog from db.task import TaskList, UserIgLink from db.organization import UserOrganizationLink from db.task import KarmaActivityLog +from db.user import User from utils.types import OrganizationType from utils.utils import DateTimeUtils from utils.types import Lc @@ -505,17 +507,42 @@ def update(self, instance, validated_data): class MeetRecordsCreateEditDeleteSerializer(serializers.ModelSerializer): + attendees_details = serializers.SerializerMethodField() + meet_created_by = serializers.CharField(source='created_by.fullname', required=False) + meet_created_at = serializers.CharField(source='created_at', required=False) + meet_id = serializers.CharField(source='id', required=False) class Meta: model = CircleMeetingLog fields = [ + "meet_id", "meet_time", "meet_place", "day", "attendees", "agenda", + "attendees_details", + "meet_created_by", + "meet_created_at" ] + def get_attendees_details(self, obj): + attendees_list = obj.attendees.split(',') + + attendees_details_list = User.objects.filter( + id__in=attendees_list + ).values( + 'profile_pic', + fullname=Concat( + 'first_name', + Value(' '), + 'last_name', + output_field=CharField() + ), + ) + + return attendees_details_list + def create(self, validated_data): validated_data['id'] = uuid.uuid4() validated_data['circle_id'] = self.context.get('circle_id') @@ -531,7 +558,7 @@ def update(self, instance, validated_data): def validate_attendees(self, attendees): task = TaskList.objects.filter(hashtag=Lc.TASK_HASHTAG.value).first() - attendees = attendees.split(',') + attendees_list = attendees.split(',') user_id = self.context.get('user_id') @@ -544,24 +571,46 @@ def validate_attendees(self, attendees): updated_by_id=user_id, created_by_id=user_id, ) - for user in attendees + for user in attendees_list ]) return attendees +class ListAllMeetRecordsSerializer(serializers.ModelSerializer): + class Meta: + model = CircleMeetingLog + fields = [ + "id", + "meet_time", + "day", + ] + + class IgTaskDetailsSerializer(serializers.ModelSerializer): task = serializers.CharField(source='title') - is_completed = serializers.SerializerMethodField() + task_status = serializers.SerializerMethodField() + task_id = serializers.CharField(source='id') + task_level = serializers.CharField(source='level.level_order') + task_level_karma = serializers.CharField(source='level.karma') + task_karma = serializers.CharField(source='karma') + task_description = serializers.CharField(source='description') + interest_group = serializers.CharField(source='ig.name') class Meta: model = TaskList fields = [ + "task_id", "task", - "is_completed" + "task_karma", + "task_description", + "interest_group", + "task_status", + "task_level", + "task_level_karma", ] - def get_is_completed(self, obj): + def get_task_status(self, obj): ig_id = self.context.get('ig_id') user_ig_links = UserIgLink.objects.filter(ig=ig_id).select_related('user') diff --git a/api/dashboard/lc/dash_lc_view.py b/api/dashboard/lc/dash_lc_view.py index 34e10a48..402a8f3f 100644 --- a/api/dashboard/lc/dash_lc_view.py +++ b/api/dashboard/lc/dash_lc_view.py @@ -2,7 +2,8 @@ from decouple import config from django.core.mail import send_mail -from django.db.models import Q, F +from django.db.models import Q, F, Value, CharField +from django.db.models.functions import Concat from django.shortcuts import redirect from rest_framework.views import APIView @@ -17,7 +18,8 @@ from .dash_lc_serializer import LearningCircleSerializer, LearningCircleCreateSerializer, LearningCircleHomeSerializer, \ LearningCircleUpdateSerializer, LearningCircleJoinSerializer, \ LearningCircleMainSerializer, LearningCircleNoteSerializer, LearningCircleDataSerializer, \ - LearningCircleMemberListSerializer, MeetRecordsCreateEditDeleteSerializer, IgTaskDetailsSerializer, ScheduleMeetingSerializer + LearningCircleMemberListSerializer, MeetRecordsCreateEditDeleteSerializer, IgTaskDetailsSerializer, \ + ScheduleMeetingSerializer, ListAllMeetRecordsSerializer domain = config("FR_DOMAIN_NAME") from_mail = config("FROM_MAIL") @@ -395,35 +397,24 @@ def delete(self, request, circle_id): class MeetRecordsGetPostPatchDeleteAPI(APIView): - def get(self, request, circle_id=None, meet_id=None): - + def get(self, request, meet_id=None, circle_id=None): if meet_id: - circle_meeting_log = CircleMeetingLog.objects.filter( - id=meet_id - ).values( - "id", - "meet_time", - "meet_place", - "day", - "attendees", - "agenda", - meet_created_by=F("created_by__first_name"), - meet_created_at=F("created_at"), - meet_updated_by=F("updated_by__first_name"), - meet_updated_at=F("updated_at"), - ) + circle_meeting_log = CircleMeetingLog.objects.get(id=meet_id) + + serializer = MeetRecordsCreateEditDeleteSerializer( + circle_meeting_log, + many=False + ).data if circle_id: - circle_meeting_log = CircleMeetingLog.objects.filter( - circle_id=circle_id - ).values( - "id", - "meet_time", - "day" - ) + circle_meeting_log = CircleMeetingLog.objects.filter(circle_id=circle_id) + serializer = ListAllMeetRecordsSerializer( + circle_meeting_log, + many=True + ).data return CustomResponse( - response=circle_meeting_log + response=serializer ).get_success_response() def post(self, request, circle_id): @@ -683,17 +674,15 @@ def put(self, request, circle_id): class IgTaskDetailsAPI(APIView): def get(self, request, ig_id): - task_list = TaskList.objects.filter( - ig=ig_id - ).first() + task_list = TaskList.objects.filter(ig=ig_id) serializer = IgTaskDetailsSerializer( task_list, - many=False, + many=True, context={ 'ig': ig_id } - ).data + ) return CustomResponse( response=serializer.data diff --git a/api/dashboard/lc/urls.py b/api/dashboard/lc/urls.py index 556b4864..44cbfde0 100644 --- a/api/dashboard/lc/urls.py +++ b/api/dashboard/lc/urls.py @@ -8,6 +8,7 @@ path('data/', dash_lc_view.LearningCircleDataAPI.as_view()), path('list-all/', dash_lc_view.TotalLearningCircleListApi.as_view()), path('create/', dash_lc_view.LearningCircleCreateApi.as_view()), + path('ig-task//', dash_lc_view.IgTaskDetailsAPI.as_view()), path('list-members//', dash_lc_view.LearningCircleListMembersApi.as_view()), # path('invite/', dash_lc_view.LearningCircleInviteLeadAPI.as_view()), path('list-all//', dash_lc_view.TotalLearningCircleListApi.as_view()), @@ -25,5 +26,4 @@ path('member/invite///', dash_lc_view.LearningCircleInviteMemberAPI.as_view()), path('member/invite/status////', dash_lc_view.LearningCircleInvitationStatus.as_view()), path('lead///', dash_lc_view.LearningCircleLeadTransfer.as_view()), - path('ig-task//', dash_lc_view.IgTaskDetailsAPI.as_view()), ] diff --git a/api/dashboard/profile/profile_view.py b/api/dashboard/profile/profile_view.py index 32527568..7aff3224 100644 --- a/api/dashboard/profile/profile_view.py +++ b/api/dashboard/profile/profile_view.py @@ -461,7 +461,7 @@ def post(self, request): general_message="No user data available" ).get_failure_response() - self.save_password(request,user) + return self.save_password(request,user) def save_password(self, request, user_obj): new_password = request.data.get("password") diff --git a/api/dashboard/roles/assets/role_base_template.xlsx b/api/dashboard/roles/assets/role_base_template.xlsx new file mode 100644 index 00000000..ef71582d Binary files /dev/null and b/api/dashboard/roles/assets/role_base_template.xlsx differ diff --git a/api/dashboard/roles/dash_roles_serializer.py b/api/dashboard/roles/dash_roles_serializer.py index 15588959..580ab0b4 100644 --- a/api/dashboard/roles/dash_roles_serializer.py +++ b/api/dashboard/roles/dash_roles_serializer.py @@ -80,3 +80,26 @@ def create(self, validated_data): validated_data["created_at"] = DateTimeUtils.get_current_utc_time() return super().create(validated_data) + +class UserRoleBulkAssignSerializer(serializers.ModelSerializer): + user_id = serializers.CharField(required=True) + role_id = serializers.CharField(required=True) + created_by_id = serializers.CharField(required=True, allow_null=False) + class Meta: + model = UserRoleLink + fields = [ + "id", + "user_id", + "role_id", + "verified", + "created_by_id", + "created_at", + ] + + def to_representation(self, instance): + representation = super().to_representation(instance) + + representation['user_id'] = instance.user.fullname if instance.user else None + representation['role_id'] = instance.role.title if instance.role else None + return representation + \ No newline at end of file diff --git a/api/dashboard/roles/dash_roles_views.py b/api/dashboard/roles/dash_roles_views.py index 75c6fb10..f8519ecd 100644 --- a/api/dashboard/roles/dash_roles_views.py +++ b/api/dashboard/roles/dash_roles_views.py @@ -1,13 +1,18 @@ +import uuid from django.db import IntegrityError from rest_framework.views import APIView from db.user import Role, User, UserRoleLink -from utils.permission import CustomizePermission, role_required +from utils.permission import CustomizePermission, role_required, JWTUtils from utils.response import CustomResponse from utils.types import RoleType, WebHookActions, WebHookCategory -from utils.utils import CommonUtils, DiscordWebhooks +from utils.utils import CommonUtils, DiscordWebhooks, ImportCSV from . import dash_roles_serializer +from openpyxl import load_workbook +from tempfile import NamedTemporaryFile +from io import BytesIO +from django.http import FileResponse class RoleAPI(APIView): authentication_classes = [CustomizePermission] @@ -202,3 +207,156 @@ def delete(self, request): return CustomResponse( general_message="User Role deleted successfully" ).get_success_response() + +class RoleBaseTemplateAPI(APIView): + authentication_classes = [CustomizePermission] + + def get(self, request): + wb = load_workbook('./api/dashboard/roles/assets/role_base_template.xlsx') + ws = wb['Data Definitions'] + + roles = Role.objects.all().values_list('title', flat=True) + data = { + 'role': roles + } + # Write data column-wise + for col_num, (col_name, col_values) in enumerate(data.items(), start=1): + for row, value in enumerate(col_values, start=2): + ws.cell(row=row, column=col_num, value=value) + + # Save the file + with NamedTemporaryFile() as tmp: + tmp.close() # with statement opened tmp, close it so wb.save can open it + wb.save(tmp.name) + with open(tmp.name, 'rb') as f: + f.seek(0) + new_file_object = f.read() + return FileResponse(BytesIO(new_file_object), as_attachment=True, filename='role_base_template.xlsx') + +class UserRoleBulkAssignAPI(APIView): + authentication_classes = [CustomizePermission] + + @role_required([RoleType.ADMIN.value]) + + def post(self, request): + try: + file_obj = request.FILES["user_roles_list"] + except KeyError: + return CustomResponse( + general_message="File not found." + ).get_failure_response() + + excel_data = ImportCSV() + excel_data = excel_data.read_excel_file(file_obj) + + if not excel_data: + return CustomResponse( + general_message="Empty csv file." + ).get_failure_response() + + temp_headers = [ + "muid", + "role" + ] + first_entry = excel_data[0] + for key in temp_headers: + if key not in first_entry: + return CustomResponse( + general_message=f"{key} does not exist in the file." + ).get_failure_response() + + excel_data = [row for row in excel_data if any(row.values())] + valid_rows = [] + error_rows = [] + + users_to_fetch = set() + roles_to_fetch = set() + user_role_link_to_check = set() + + for row in excel_data[1:]: + keys_to_keep = ["muid", "role"] + row_keys = list(row.keys()) + + # Remove columns other than "muid" and "role" + for key in row_keys: + if key not in keys_to_keep: + del row[key] + + for row in excel_data[1:]: + user = row.get("muid") + role = row.get("role") + users_to_fetch.add(user) + roles_to_fetch.add(role) + if (user, role) in user_role_link_to_check: + row["error"] = "Duplicate entry" + error_rows.append(row) + excel_data.remove(row) + else: + user_role_link_to_check.add((user, role)) + + users = User.objects.filter( + muid__in=users_to_fetch + ).values( + "id", + "muid", + ) + roles = Role.objects.filter( + title__in=roles_to_fetch + ).values( + "id", + "title", + ) + existing_user_role_links = list(UserRoleLink.objects.filter( + user__muid__in=users_to_fetch, + role__title__in=roles_to_fetch + ).values_list('user__muid', 'role__title')) + users_dict = {user["muid"]: user["id"] for user in users} + roles_dict = {role["title"]: role["id"] for role in roles} + + for row in excel_data[1:]: + user = row.pop("muid") + role = row.pop("role") + + user_id = users_dict.get(user) + role_id = roles_dict.get(role) + + if not user_id: + row["muid"] = user + row["role"] = role + row["error"] = f"Invalid user muid: {user}" + error_rows.append(row) + elif not role_id: + row["muid"] = user + row["role"] = role + row["error"] = f"Invalid role: {role}" + error_rows.append(row) + elif (user, role) in existing_user_role_links: + row["muid"] = user + row["role"] = role + row["error"] = f"User {user} already has role {role}" + error_rows.append(row) + else: + request_user_id = JWTUtils.fetch_user_id(request) + row["id"] = str(uuid.uuid4()) + row["user_id"] = user_id + row["role_id"] = role_id + row["verified"] = True + row["created_by_id"] = request_user_id + valid_rows.append(row) + + user_roles_serializer = dash_roles_serializer.UserRoleBulkAssignSerializer(data=valid_rows, many=True) + success_data = [] + if user_roles_serializer.is_valid(): + user_roles_serializer.save() + for user_role_data in user_roles_serializer.data: + success_data.append({ + 'user': user_role_data.get('user_id', ''), + 'role': user_role_data.get('role_id', ''), + }) + else: + error_rows.append(user_roles_serializer.errors) + + return CustomResponse( + response={"Success": success_data, "Failed": error_rows} + ).get_success_response() + diff --git a/api/dashboard/roles/urls.py b/api/dashboard/roles/urls.py index c186b585..dc0790a9 100644 --- a/api/dashboard/roles/urls.py +++ b/api/dashboard/roles/urls.py @@ -5,6 +5,8 @@ # app_name will help us do a reverse look-up latter. urlpatterns = [ path('user-role//', dash_roles_views.UserRoleSearchAPI.as_view(), name='search-user-role'), + path('base-template/', dash_roles_views.RoleBaseTemplateAPI.as_view(), name="roles-base-template"), + path('bulk-assign/', dash_roles_views.UserRoleBulkAssignAPI.as_view(), name="user-roles-import"), path('user-role/', dash_roles_views.UserRole.as_view(), name='create-delete-user-role'), path('', dash_roles_views.RoleAPI.as_view(), name="roles-list"), path('', dash_roles_views.RoleAPI.as_view(), name="roles-create"), diff --git a/api/dashboard/user/dash_user_views.py b/api/dashboard/user/dash_user_views.py index 28968620..4508cc65 100644 --- a/api/dashboard/user/dash_user_views.py +++ b/api/dashboard/user/dash_user_views.py @@ -1,18 +1,18 @@ import uuid from datetime import timedelta +from decouple import config as decouple_config from django.contrib.auth.hashers import make_password +from django.core.files.storage import FileSystemStorage from django.db.models import Q from rest_framework.views import APIView from db.user import ForgotPassword, User, UserRoleLink from utils.permission import CustomizePermission, JWTUtils, role_required -from utils.response import CustomResponse, ImageResponse +from utils.response import CustomResponse from utils.types import RoleType, WebHookActions, WebHookCategory from utils.utils import CommonUtils, DateTimeUtils, DiscordWebhooks, send_template_mail from . import dash_user_serializer -from django.core.files.storage import FileSystemStorage -from decouple import config as decouple_config BE_DOMAIN_NAME = decouple_config('BE_DOMAIN_NAME') @@ -50,7 +50,6 @@ def get(self, request, user_id): return CustomResponse(response=serializer.data).get_success_response() - @role_required([RoleType.ADMIN.value]) def delete(self, request, user_id): user = User.objects.filter(id=user_id).first() diff --git a/db-scripts b/db-scripts deleted file mode 160000 index b0cd9ea3..00000000 --- a/db-scripts +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b0cd9ea3db832f5417e11f86ca6a989b4c3f830f diff --git a/db/organization.py b/db/organization.py index 856e431f..b8bb19d7 100644 --- a/db/organization.py +++ b/db/organization.py @@ -4,139 +4,148 @@ from .user import User + # fmt: off # noinspection PyPep8 class Country(models.Model): - id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4) - name = models.CharField(max_length=75, null=False, unique=True) - updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='updated_by', related_name='country_updated_by') - updated_at = models.DateTimeField(auto_now=True) - created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='created_by', related_name='country_created_by') - created_at = models.DateTimeField(auto_now_add=True) + id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4) + name = models.CharField(max_length=75, null=False, unique=True) + updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='updated_by', + related_name='country_updated_by') + updated_at = models.DateTimeField(auto_now=True) + created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='created_by', + related_name='country_created_by') + created_at = models.DateTimeField(auto_now_add=True) class Meta: managed = False db_table = 'country' - class State(models.Model): - id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4) - name = models.CharField(max_length=75) - country = models.ForeignKey(Country, on_delete=models.CASCADE, related_name='state_country') - updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='updated_by', related_name='state_updated_by') - updated_at = models.DateTimeField(auto_now=True) - created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='created_by', related_name='state_created_by') - created_at = models.DateTimeField(auto_now_add=True) + id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4) + name = models.CharField(max_length=75) + country = models.ForeignKey(Country, on_delete=models.CASCADE, related_name='state_country') + updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='updated_by', + related_name='state_updated_by') + updated_at = models.DateTimeField(auto_now=True) + created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='created_by', + related_name='state_created_by') + created_at = models.DateTimeField(auto_now_add=True) class Meta: managed = False db_table = 'state' - class Zone(models.Model): - id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4) - name = models.CharField(max_length=75) - state = models.ForeignKey(State, on_delete=models.CASCADE, related_name='zone_state') - updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='updated_by', related_name='zone_updated_by') - updated_at = models.DateTimeField(auto_now=True) - created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='created_by', related_name='zone_created_by') - created_at = models.DateTimeField(auto_now_add=True) + id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4) + name = models.CharField(max_length=75) + state = models.ForeignKey(State, on_delete=models.CASCADE, related_name='zone_state') + updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='updated_by', + related_name='zone_updated_by') + updated_at = models.DateTimeField(auto_now=True) + created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='created_by', + related_name='zone_created_by') + created_at = models.DateTimeField(auto_now_add=True) class Meta: managed = False db_table = 'zone' - class District(models.Model): - id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4) - name = models.CharField(max_length=75) - zone = models.ForeignKey(Zone, on_delete=models.CASCADE, related_name="district_zone") - updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='updated_by', related_name='district_updated_by') - updated_at = models.DateTimeField(auto_now=True) - created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='created_by', related_name='district_created_by') - created_at = models.DateTimeField(auto_now_add=True) + id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4) + name = models.CharField(max_length=75) + zone = models.ForeignKey(Zone, on_delete=models.CASCADE, related_name="district_zone") + updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='updated_by', + related_name='district_updated_by') + updated_at = models.DateTimeField(auto_now=True) + created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='created_by', + related_name='district_created_by') + created_at = models.DateTimeField(auto_now_add=True) class Meta: managed = False db_table = 'district' - class OrgAffiliation(models.Model): - id = models.CharField(primary_key=True, max_length=36,default=uuid.uuid4()) + id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4()) title = models.CharField(max_length=75) updated_by = models.ForeignKey( User, on_delete=models.CASCADE, db_column='updated_by', related_name='org_affiliation_updated_by') updated_at = models.DateTimeField(auto_now=True) created_by = models.ForeignKey( User, on_delete=models.CASCADE, db_column='created_by', related_name='org_affiliation_created_by') - created_at = models.DateTimeField(auto_now_add=True) + created_at = models.DateTimeField(auto_now_add=True) class Meta: managed = False db_table = 'org_affiliation' - class Organization(models.Model): - id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4()) - title = models.CharField(max_length=100) - code = models.CharField(unique=True, max_length=12) - org_type = models.CharField(max_length=25) - affiliation = models.ForeignKey(OrgAffiliation, on_delete=models.CASCADE, blank=True, null=True) - district = models.ForeignKey(District, on_delete=models.CASCADE, related_name='organization_district') - updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='updated_by', related_name='organization_updated_by') - updated_at = models.DateTimeField(auto_now=True) - created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='created_by', related_name='organization_created_by') - created_at = models.DateTimeField(auto_now_add=True) + id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4()) + title = models.CharField(max_length=100) + code = models.CharField(unique=True, max_length=12) + org_type = models.CharField(max_length=25) + affiliation = models.ForeignKey(OrgAffiliation, on_delete=models.CASCADE, blank=True, null=True) + district = models.ForeignKey(District, on_delete=models.CASCADE, related_name='organization_district') + updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='updated_by', + related_name='organization_updated_by') + updated_at = models.DateTimeField(auto_now=True) + created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='created_by', + related_name='organization_created_by') + created_at = models.DateTimeField(auto_now_add=True) class Meta: managed = False db_table = 'organization' - class Department(models.Model): - id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4()) - title = models.CharField(max_length=100) - updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='updated_by', related_name='department_updated_by') - updated_at = models.DateTimeField(auto_now=True) - created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='created_by', related_name='department_created_by') - created_at = models.DateTimeField(auto_now_add=True) + id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4()) + title = models.CharField(max_length=100) + updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='updated_by', + related_name='department_updated_by') + updated_at = models.DateTimeField(auto_now=True) + created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='created_by', + related_name='department_created_by') + created_at = models.DateTimeField(auto_now_add=True) class Meta: managed = False db_table = 'department' - class College(models.Model): - id = models.CharField(primary_key=True, max_length=36,default=uuid.uuid4()) - level = models.IntegerField() - org = models.OneToOneField(Organization, on_delete=models.CASCADE, related_name='college_org', unique=True) - updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='updated_by', related_name='college_updated_by') - updated_at = models.DateTimeField(auto_now=True) - created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='created_by', related_name='college_created_by') - created_at = models.DateTimeField(auto_now_add=True) - + id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4()) + level = models.IntegerField() + org = models.OneToOneField(Organization, on_delete=models.CASCADE, related_name='college_org', unique=True) + updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='updated_by', + related_name='college_updated_by') + updated_at = models.DateTimeField(auto_now=True) + created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='created_by', + related_name='college_created_by') + created_at = models.DateTimeField(auto_now_add=True) + class Meta: managed = False db_table = 'college' - class OrgDiscordLink(models.Model): - id = models.CharField(primary_key=True, max_length=36) - discord_id = models.CharField(unique=True, max_length=36) - org = models.OneToOneField(Organization, on_delete=models.CASCADE, related_name='org_discord_link_org_id') - updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='updated_by', related_name='org_discord_link_updated_by') - updated_at = models.DateTimeField(auto_now=True) - created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='created_by', related_name='org_discord_link_created_by') - created_at = models.DateTimeField(auto_now_add=True) + id = models.CharField(primary_key=True, max_length=36) + discord_id = models.CharField(unique=True, max_length=36) + org = models.OneToOneField(Organization, on_delete=models.CASCADE, related_name='org_discord_link_org_id') + updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='updated_by', + related_name='org_discord_link_updated_by') + updated_at = models.DateTimeField(auto_now=True) + created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='created_by', + related_name='org_discord_link_created_by') + created_at = models.DateTimeField(auto_now_add=True) class Meta: managed = False @@ -144,14 +153,17 @@ class Meta: class UserOrganizationLink(models.Model): - id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4) - user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_organization_link_user') - org = models.ForeignKey(Organization, on_delete=models.CASCADE, related_name='user_organization_link_org') - department = models.ForeignKey(Department, on_delete=models.CASCADE, blank=True, null=True, related_name='user_organization_link_department') - graduation_year= models.CharField(max_length=10, blank=True, null=True) - verified = models.BooleanField() - created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='created_by', related_name='user_organization_link_created_by') - created_at = models.DateTimeField(auto_now_add=True) + id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4) + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_organization_link_user') + org = models.ForeignKey(Organization, on_delete=models.CASCADE, related_name='user_organization_link_org') + department = models.ForeignKey(Department, on_delete=models.CASCADE, blank=True, null=True, + related_name='user_organization_link_department') + graduation_year = models.CharField(max_length=10, blank=True, null=True) + verified = models.BooleanField() + is_alumni = models.IntegerField(blank=True, null=True, default=False) + created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column='created_by', + related_name='user_organization_link_created_by') + created_at = models.DateTimeField(auto_now_add=True) class Meta: managed = False @@ -159,15 +171,16 @@ class Meta: class OrgKarmaType(models.Model): - id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4()) - title = models.CharField(max_length=75) - karma = models.IntegerField() + id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4()) + title = models.CharField(max_length=75) + karma = models.IntegerField() description = models.CharField(max_length=200, blank=True, null=True) - updated_by = models.ForeignKey(User, models.DO_NOTHING, db_column='updated_by', related_name='org_karma_type_updated_by') - updated_at = models.DateTimeField(auto_now=True) - created_by = models.ForeignKey(User, models.DO_NOTHING, db_column='created_by', + updated_by = models.ForeignKey(User, models.DO_NOTHING, db_column='updated_by', + related_name='org_karma_type_updated_by') + updated_at = models.DateTimeField(auto_now=True) + created_by = models.ForeignKey(User, models.DO_NOTHING, db_column='created_by', related_name='org_karma_type_created_by') - created_at = models.DateTimeField(auto_now=True) + created_at = models.DateTimeField(auto_now=True) class Meta: managed = False @@ -175,11 +188,12 @@ class Meta: class OrgKarmaLog(models.Model): - id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4()) - org = models.ForeignKey(Organization, models.DO_NOTHING, related_name='org_karma_log_org') - karma = models.IntegerField() - type = models.ForeignKey(OrgKarmaType, models.DO_NOTHING, db_column='type', related_name='org_karma_log_type') - updated_by = models.ForeignKey(User, models.DO_NOTHING, db_column='updated_by', related_name='org_karma_log_updated_by') + id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4()) + org = models.ForeignKey(Organization, models.DO_NOTHING, related_name='org_karma_log_org') + karma = models.IntegerField() + type = models.ForeignKey(OrgKarmaType, models.DO_NOTHING, db_column='type', related_name='org_karma_log_type') + updated_by = models.ForeignKey(User, models.DO_NOTHING, db_column='updated_by', + related_name='org_karma_log_updated_by') updated_at = models.DateTimeField(auto_now=True) created_by = models.ForeignKey(User, models.DO_NOTHING, db_column='created_by', related_name='org_karma_log_created_by') @@ -189,7 +203,6 @@ class Meta: managed = False db_table = 'org_karma_log' - @property def total_karma(self): try: diff --git a/db/task.py b/db/task.py index db94bead..a18b470e 100644 --- a/db/task.py +++ b/db/task.py @@ -5,6 +5,7 @@ from db.organization import Organization from .user import User + # fmt: off # noinspection PyPep8 @@ -32,193 +33,205 @@ class Meta: db_table = "channel" - class InterestGroup(models.Model): - id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4) - name = models.CharField(max_length=75, unique=True) - code = models.CharField(max_length=10, unique=True) - icon = models.CharField(max_length=10) - updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="updated_by", related_name="interest_group_updated_by") - updated_at = models.DateTimeField(auto_now=True) - created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="created_by", related_name="interest_group_created_by") - created_at = models.DateTimeField(auto_now_add=True) + id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4) + name = models.CharField(max_length=75, unique=True) + code = models.CharField(max_length=10, unique=True) + icon = models.CharField(max_length=10) + updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="updated_by", + related_name="interest_group_updated_by") + updated_at = models.DateTimeField(auto_now=True) + created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="created_by", + related_name="interest_group_created_by") + created_at = models.DateTimeField(auto_now_add=True) class Meta: managed = False db_table = "interest_group" - class Level(models.Model): - id = models.CharField(primary_key=True, max_length=36) - level_order = models.IntegerField() - name = models.CharField(max_length=36) - karma = models.IntegerField() - created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="created_by", related_name="level_created_by") - created_at = models.DateTimeField(auto_now_add=True) - updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="updated_by", related_name="level_updated_by") - updated_at = models.DateTimeField(auto_now=True) + id = models.CharField(primary_key=True, max_length=36) + level_order = models.IntegerField() + name = models.CharField(max_length=36) + karma = models.IntegerField() + created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="created_by", + related_name="level_created_by") + created_at = models.DateTimeField(auto_now_add=True) + updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="updated_by", + related_name="level_updated_by") + updated_at = models.DateTimeField(auto_now=True) class Meta: managed = False db_table = "level" - class UserLvlLink(models.Model): - id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4) - user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="user_lvl_link_user") - level = models.ForeignKey(Level, on_delete=models.CASCADE, related_name="user_lvl_link_level") - updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="updated_by", related_name="user_lvl_link_updated_by") - updated_at = models.DateTimeField(auto_now=True) - created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="created_by", related_name="user_lvl_link_created_by") - created_at = models.DateTimeField(auto_now_add=True) + id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4) + user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="user_lvl_link_user") + level = models.ForeignKey(Level, on_delete=models.CASCADE, related_name="user_lvl_link_level") + updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="updated_by", + related_name="user_lvl_link_updated_by") + updated_at = models.DateTimeField(auto_now=True) + created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="created_by", + related_name="user_lvl_link_created_by") + created_at = models.DateTimeField(auto_now_add=True) class Meta: managed = False db_table = "user_lvl_link" - class TaskType(models.Model): - id = models.CharField(primary_key=True, max_length=36) - title = models.CharField(max_length=75) - updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="updated_by", related_name="task_type_updated_by") - updated_at = models.DateTimeField(auto_now=True) - created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="created_by", related_name="task_type_created_by") - created_at = models.DateTimeField(auto_now_add=True) + id = models.CharField(primary_key=True, max_length=36) + title = models.CharField(max_length=75) + updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="updated_by", + related_name="task_type_updated_by") + updated_at = models.DateTimeField(auto_now=True) + created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="created_by", + related_name="task_type_created_by") + created_at = models.DateTimeField(auto_now_add=True) class Meta: managed = False db_table = "task_type" - class TaskList(models.Model): - id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4) - hashtag = models.CharField(max_length=75) - discord_link = models.CharField(max_length=200, blank=True, null=True) - title = models.CharField(max_length=75) - description = models.CharField(max_length=200, null=True) - karma = models.IntegerField(null=True) - channel = models.ForeignKey(Channel, on_delete=models.CASCADE, null =True) - type = models.ForeignKey(TaskType, on_delete=models.CASCADE) - org = models.ForeignKey(Organization, on_delete=models.CASCADE, null=True) - level = models.ForeignKey(Level, on_delete=models.CASCADE, null=True) - ig = models.ForeignKey(InterestGroup, on_delete=models.CASCADE, null =True, related_name="task_list_ig") - event = models.CharField(max_length=50, null =True) - active = models.BooleanField(default=True) - variable_karma = models.BooleanField(default=False) - usage_count = models.IntegerField(default=1) - updated_by = models.ForeignKey(User, models.DO_NOTHING, db_column="updated_by", related_name="task_list_updated_by") - updated_at = models.DateTimeField(auto_now=True) - created_by = models.ForeignKey(User, models.DO_NOTHING, db_column="created_by", related_name="task_list_created_by") - created_at = models.DateTimeField(auto_now_add=True) - + id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4) + hashtag = models.CharField(max_length=75) + discord_link = models.CharField(max_length=200, blank=True, null=True) + title = models.CharField(max_length=75) + description = models.CharField(max_length=200, null=True) + karma = models.IntegerField(null=True) + channel = models.ForeignKey(Channel, on_delete=models.CASCADE, null=True) + type = models.ForeignKey(TaskType, on_delete=models.CASCADE) + org = models.ForeignKey(Organization, on_delete=models.CASCADE, null=True) + level = models.ForeignKey(Level, on_delete=models.CASCADE, null=True) + ig = models.ForeignKey(InterestGroup, on_delete=models.CASCADE, null=True, related_name="task_list_ig") + event = models.CharField(max_length=50, null=True) + active = models.BooleanField(default=True) + variable_karma = models.BooleanField(default=False) + usage_count = models.IntegerField(default=1) + updated_by = models.ForeignKey(User, models.DO_NOTHING, db_column="updated_by", related_name="task_list_updated_by") + updated_at = models.DateTimeField(auto_now=True) + created_by = models.ForeignKey(User, models.DO_NOTHING, db_column="created_by", related_name="task_list_created_by") + created_at = models.DateTimeField(auto_now_add=True) + class Meta: managed = False db_table = "task_list" - class Wallet(models.Model): - id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4) - user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="wallet_user") - karma = models.IntegerField(default=0) - coin = models.FloatField(default=0) - updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="updated_by", related_name="wallet_updated_by") - updated_at = models.DateTimeField(auto_now=True) - created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="created_by", related_name="wallet_created_by") - created_at = models.DateTimeField(auto_now_add=True) + id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4) + user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="wallet_user") + karma = models.IntegerField(default=0) + karma_last_update_at = models.DateTimeField(blank=True, null=True, auto_now_add=True) + coin = models.FloatField(default=0) + updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="updated_by", + related_name="wallet_updated_by") + updated_at = models.DateTimeField(auto_now=True) + created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="created_by", + related_name="wallet_created_by") + created_at = models.DateTimeField(auto_now_add=True) class Meta: managed = False db_table = "wallet" - class KarmaActivityLog(models.Model): - id = models.CharField(default=uuid.uuid4, primary_key=True, max_length=36) - karma = models.IntegerField() - task = models.ForeignKey(TaskList, on_delete=models.CASCADE, related_name="karma_activity_log_task") - user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True, related_name="karma_activity_log_user") - task_message_id = models.CharField(max_length=36) - lobby_message_id = models.CharField(max_length=36, blank=True, null=True) - dm_message_id = models.CharField(max_length=36, blank=True, null=True) - peer_approved = models.BooleanField(blank=True, null=True) - peer_approved_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="peer_approved_by", blank =True, null =True, related_name="karma_activity_log_peer_approved_by") - appraiser_approved = models.BooleanField(blank=True, null=True) - appraiser_approved_by= models.ForeignKey(User, on_delete=models.CASCADE, db_column="appraiser_approved_by", blank =True, null =True, related_name="karma_activity_log_appraiser_approved_by") - updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="updated_by", related_name="karma_activity_log_updated_by") - updated_at = models.DateTimeField(auto_now=True) - created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="created_by", related_name="karma_activity_log_created_by") - created_at = models.DateTimeField(auto_now_add=True) + id = models.CharField(default=uuid.uuid4, primary_key=True, max_length=36) + karma = models.IntegerField() + task = models.ForeignKey(TaskList, on_delete=models.CASCADE, related_name="karma_activity_log_task") + user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True, + related_name="karma_activity_log_user") + task_message_id = models.CharField(max_length=36) + lobby_message_id = models.CharField(max_length=36, blank=True, null=True) + dm_message_id = models.CharField(max_length=36, blank=True, null=True) + peer_approved = models.BooleanField(blank=True, null=True) + peer_approved_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="peer_approved_by", blank=True, + null=True, related_name="karma_activity_log_peer_approved_by") + appraiser_approved = models.BooleanField(blank=True, null=True) + appraiser_approved_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="appraiser_approved_by", + blank=True, null=True, + related_name="karma_activity_log_appraiser_approved_by") + updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="updated_by", + related_name="karma_activity_log_updated_by") + updated_at = models.DateTimeField(auto_now=True) + created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="created_by", + related_name="karma_activity_log_created_by") + created_at = models.DateTimeField(auto_now_add=True) class Meta: managed = False db_table = "karma_activity_log" - class MucoinActivityLog(models.Model): - id = models.CharField(primary_key=True, max_length=36) - user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="mucoin_activity_log_user") - coin = models.FloatField() - status = models.CharField(max_length=36) - task = models.ForeignKey(TaskList, on_delete=models.CASCADE, related_name="mucoin_activity_log_task_list") - updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="updated_by", related_name="mucoin_activity_log_updated_by") - updated_at = models.DateTimeField(auto_now=True) - created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="created_by", related_name="mucoin_activity_log_created_by") - created_at = models.DateTimeField(auto_now_add=True) + id = models.CharField(primary_key=True, max_length=36) + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="mucoin_activity_log_user") + coin = models.FloatField() + status = models.CharField(max_length=36) + task = models.ForeignKey(TaskList, on_delete=models.CASCADE, related_name="mucoin_activity_log_task_list") + updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="updated_by", + related_name="mucoin_activity_log_updated_by") + updated_at = models.DateTimeField(auto_now=True) + created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="created_by", + related_name="mucoin_activity_log_created_by") + created_at = models.DateTimeField(auto_now_add=True) class Meta: managed = False db_table = "mucoin_activity_log" - class MucoinInviteLog(models.Model): - id = models.CharField(primary_key=True, max_length=36) - user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="mucoin_invite_log_user") - email = models.CharField(max_length=200) - invite_code = models.CharField(max_length=36) - created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="created_by", related_name="mucoin_invite_log_created_by") - created_at = models.DateTimeField(auto_now_add=True) + id = models.CharField(primary_key=True, max_length=36) + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="mucoin_invite_log_user") + email = models.CharField(max_length=200) + invite_code = models.CharField(max_length=36) + created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="created_by", + related_name="mucoin_invite_log_created_by") + created_at = models.DateTimeField(auto_now_add=True) class Meta: managed = False db_table = "mucoin_invite_log" - class UserIgLink(models.Model): - id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4) - user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="user_ig_link_user") - ig = models.ForeignKey(InterestGroup, on_delete=models.CASCADE, related_name="user_ig_link_ig") - created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="created_by", related_name="user_ig_link_created_by") - created_at = models.DateTimeField(auto_now_add=True) + id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4) + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="user_ig_link_user") + ig = models.ForeignKey(InterestGroup, on_delete=models.CASCADE, related_name="user_ig_link_ig") + created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="created_by", + related_name="user_ig_link_created_by") + created_at = models.DateTimeField(auto_now_add=True) class Meta: managed = False db_table = "user_ig_link" - -class VoucherLog(models.Model): - id = models.CharField(primary_key=True, max_length=36) - code = models.CharField(unique=True, max_length=255) - user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="voucher_log_user") - task = models.ForeignKey(TaskList, on_delete=models.CASCADE, related_name="voucher_log_task") - karma = models.IntegerField() - week = models.CharField(max_length=2, null=True) - month = models.CharField(max_length=10) - claimed = models.BooleanField() - event = models.CharField(max_length=50, null=True) - description = models.CharField(max_length=2000, null=True) - updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="updated_by", related_name="voucher_log_updated_by") - updated_at = models.DateTimeField(auto_now=True) - created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="created_by", related_name="voucher_log_created_by") - created_at = models.DateTimeField(auto_now_add=True) +class VoucherLog(models.Model): + id = models.CharField(primary_key=True, max_length=36) + code = models.CharField(unique=True, max_length=255) + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="voucher_log_user") + task = models.ForeignKey(TaskList, on_delete=models.CASCADE, related_name="voucher_log_task") + karma = models.IntegerField() + week = models.CharField(max_length=2, null=True) + month = models.CharField(max_length=10) + claimed = models.BooleanField() + event = models.CharField(max_length=50, null=True) + description = models.CharField(max_length=2000, null=True) + updated_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="updated_by", + related_name="voucher_log_updated_by") + updated_at = models.DateTimeField(auto_now=True) + created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column="created_by", + related_name="voucher_log_created_by") + created_at = models.DateTimeField(auto_now_add=True) class Meta: managed = False diff --git a/mulearnbackend/settings.py b/mulearnbackend/settings.py index 774d6404..4d43d0e1 100644 --- a/mulearnbackend/settings.py +++ b/mulearnbackend/settings.py @@ -57,7 +57,7 @@ "debug_toolbar.middleware.DebugToolbarMiddleware", "django.middleware.common.CommonMiddleware", "corsheaders.middleware.CorsMiddleware", - "mulearnbackend.middlewares.UniversalErrorHandlerMiddleware", + # "mulearnbackend.middlewares.UniversalErrorHandlerMiddleware", ] ROOT_URLCONF = "mulearnbackend.urls" diff --git a/utils/types.py b/utils/types.py index 7ba7fd18..85d05422 100644 --- a/utils/types.py +++ b/utils/types.py @@ -44,6 +44,7 @@ class RoleType(Enum): IG_FACILITATOR = "IG Facilitator" TECH_TEAM = 'Tech Team' CAMPUS_ACTIVATION_TEAM = "Campus Activation Team" + LEAD_ENABLER = "Lead Enabler" class OrganizationType(Enum): diff --git a/utils/utils.py b/utils/utils.py index 7ce72127..3866f87d 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -90,6 +90,7 @@ def generate_csv(queryset: QuerySet, csv_name: str) -> HttpResponse: class DateTimeUtils: """ A utility class for handling date and time operations. + """ @staticmethod