diff --git a/api/dashboard/lc/dash_lc_serializer.py b/api/dashboard/lc/dash_lc_serializer.py index 9eb17801..32b6471e 100644 --- a/api/dashboard/lc/dash_lc_serializer.py +++ b/api/dashboard/lc/dash_lc_serializer.py @@ -678,13 +678,9 @@ class CircleMeetDetailSerializer(serializers.ModelSerializer): def get_is_attendee_report_submitted(self, obj): user_id = self.context.get("user_id") - return ( - CircleMeetAttendeeReport.objects.select_related( - "meet_task__meet", "attendee__user" - ) - .filter(meet_task__meet=obj, attendee__user_id=user_id) - .exists() - ) + return CircleMeetAttendees.objects.filter( + meet=obj, user_id=user_id, is_report_submitted=True + ).exists() def get_is_report_submitted(self, obj): return obj.is_report_submitted @@ -941,9 +937,11 @@ def get_held_on(self, obj): .values_list("joined_at", flat=True) .first() ) - + def get_report_submitted_attendees(self, obj): - return CircleMeetAttendees.objects.filter(meet=obj,is_report_submitted=True).count() + return CircleMeetAttendees.objects.filter( + meet=obj, is_report_submitted=True + ).count() def get_join_count(self, obj): return CircleMeetAttendees.objects.filter( diff --git a/api/dashboard/organisation/organisation_views.py b/api/dashboard/organisation/organisation_views.py index 1fdc33d0..c1ecb6b1 100644 --- a/api/dashboard/organisation/organisation_views.py +++ b/api/dashboard/organisation/organisation_views.py @@ -10,6 +10,7 @@ Department, OrgAffiliation, Organization, + UnverifiedOrganization, UserOrganizationLink, District, ) @@ -30,6 +31,8 @@ OrganizationKarmaTypeGetPostPatchDeleteSerializer, OrganizationKarmaLogGetPostPatchDeleteSerializer, OrganizationImportSerializer, + OrganizationVerifySerializer, + UnverifiedOrganizationsSerializer, ) @@ -713,9 +716,39 @@ def post(self, request): return CustomResponse( response={"To Organisations not present"} ).get_failure_response() - + UserOrganizationLink.objects.filter(org=from_org).update(org=to_org) from_org.delete() return CustomResponse( - response={"Organisations transferred successfully"} + response={"Organisations transferred successfully"} + ).get_success_response() + + +class UnverifiedOrganizationsListAPI(APIView): + permission_classes = [CustomizePermission] + + def get(self, request): + unverified_orgs = UnverifiedOrganization.objects.filter(verified__isnull=True) + seializer = UnverifiedOrganizationsSerializer(unverified_orgs, many=True) + return CustomResponse(response=seializer.data).get_success_response() + + +class VerifyOrganizationAPI(APIView): + permission_classes = [CustomizePermission] + + def post(self, request, uorg_id): + user_id = JWTUtils.fetch_user_id(request) + unverifed_org = UnverifiedOrganization.objects.filter(id=uorg_id).first() + if not unverifed_org: + return CustomResponse( + general_message="Organization does not exist" + ).get_failure_response() + serialzier = OrganizationVerifySerializer( + data=request.data, context={"user_id": user_id} + ) + if serialzier.is_valid(): + serialzier.update(unverifed_org, serialzier.validated_data) + return CustomResponse( + general_message="Organization verified successfully" ).get_success_response() + return CustomResponse(general_message=serialzier.errors).get_failure_response() diff --git a/api/dashboard/organisation/serializers.py b/api/dashboard/organisation/serializers.py index bba1224b..b4429f01 100644 --- a/api/dashboard/organisation/serializers.py +++ b/api/dashboard/organisation/serializers.py @@ -1,3 +1,4 @@ +from datetime import datetime, timezone import uuid from django.db import models @@ -8,13 +9,15 @@ from db.organization import ( Organization, District, + UnverifiedOrganization, + UserOrganizationLink, Zone, State, OrgAffiliation, Department, OrgKarmaType, OrgKarmaLog, - College + College, ) from utils.permission import JWTUtils from utils.types import OrganizationType @@ -116,7 +119,9 @@ def create(self, validated_data): validated_data["updated_by_id"] = user_id orgobj = Organization.objects.create(**validated_data) if validated_data.get("org_type") == OrganizationType.COLLEGE.value: - College.objects.create(org=orgobj, created_by_id=user_id, updated_by_id=user_id) + College.objects.create( + org=orgobj, created_by_id=user_id, updated_by_id=user_id + ) return orgobj def update(self, instance, validated_data): @@ -274,8 +279,8 @@ def get_update_summary(self, instance): # Simulate the transaction for relation in Organization._meta.related_objects: if isinstance( - relation, - (models.ForeignKey, models.OneToOneField, models.ManyToManyField), + relation, + (models.ForeignKey, models.OneToOneField, models.ManyToManyField), ): related_model = relation.related_model related_field_name = relation.field.name @@ -286,7 +291,7 @@ def get_update_summary(self, instance): field.name for field in related_model._meta.fields if isinstance(field, models.ForeignKey) - and field.related_model == Organization + and field.related_model == Organization ), None, ) @@ -297,7 +302,7 @@ def get_update_summary(self, instance): continue if existing_relations := related_model.objects.filter( - **{related_field_name: instance} + **{related_field_name: instance} ): update_summary.append( { @@ -326,8 +331,8 @@ def update(self, instance, validated_data): for relation in Organization._meta.related_objects: # Determine related model and related field name if isinstance( - relation, - (models.ForeignKey, models.OneToOneField, models.ManyToManyField), + relation, + (models.ForeignKey, models.OneToOneField, models.ManyToManyField), ): related_model = relation.related_model related_field_name = relation.field.name @@ -339,7 +344,7 @@ def update(self, instance, validated_data): field.name for field in related_model._meta.fields if isinstance(field, models.ForeignKey) - and field.related_model == Organization + and field.related_model == Organization ), None, ) @@ -355,7 +360,7 @@ def update(self, instance, validated_data): # Handle OneToOne relationships: delete existing relation if it points to the instance if isinstance(relation, (models.OneToOneField, models.OneToOneRel)): if existing_relation := related_model.objects.filter( - **{related_field_name: instance} + **{related_field_name: instance} ).first(): existing_relation.delete() @@ -424,7 +429,55 @@ class Meta: def to_representation(self, instance): representation = super().to_representation(instance) - representation['affiliation_id'] = instance.affiliation.title if instance.affiliation else None - representation['district_id'] = instance.district.name if instance.district else None + representation["affiliation_id"] = ( + instance.affiliation.title if instance.affiliation else None + ) + representation["district_id"] = ( + instance.district.name if instance.district else None + ) return representation + + +class OrganizationVerifySerializer(serializers.ModelSerializer): + verified = serializers.BooleanField(required=True) + org_id = serializers.PrimaryKeyRelatedField( + queryset=Organization.objects.all(), required=True + ) + + def update(self, instance, validated_data): + if instance.verified: + raise serializers.ValidationError("Organization already verified") + instance.verified = validated_data.get("verified") + instance.org = validated_data.get("org_id") + instance.verified_by_id = self.context.get("user_id") + instance.verified_at = datetime.now(timezone.utc) + instance.save() + if instance.verified: + if UserOrganizationLink.objects.filter( + user_id=instance.created_by_id, org_id=instance.org_id + ).exists(): + raise serializers.ValidationError( + "Unable to assign organization to user" + ) + print(validated_data.get("org_id")) + UserOrganizationLink.objects.create( + user_id=instance.created_by_id, + org=validated_data.get("org_id"), + verified=True, + created_by_id=instance.verified_by_id, + ) + return instance + + class Meta: + model = UnverifiedOrganization + fields = ["verified", "org_id"] + + +class UnverifiedOrganizationsSerializer(serializers.ModelSerializer): + id = serializers.CharField(read_only=True) + created_by = serializers.CharField(source="created_by.full_name", read_only=True) + + class Meta: + model = UnverifiedOrganization + fields = ["id", "title", "org_type", "created_by", "created_at"] diff --git a/api/dashboard/organisation/urls.py b/api/dashboard/organisation/urls.py index 9006b6b6..68fa037f 100644 --- a/api/dashboard/organisation/urls.py +++ b/api/dashboard/organisation/urls.py @@ -4,27 +4,76 @@ urlpatterns = [ # path('add', portal_views.AddPortal.as_view()), - path('institutes/create/', organisation_views.InstitutionPostUpdateDeleteAPI.as_view()), - path('institutes/edit//', organisation_views.InstitutionPostUpdateDeleteAPI.as_view()), - path('institutes/delete//', organisation_views.InstitutionPostUpdateDeleteAPI.as_view()), - path('institutes//csv/', organisation_views.InstitutionCSVAPI.as_view()), - path('institutes/info//', organisation_views.InstitutionDetailsAPI.as_view()), - path('institutes/prefill//', organisation_views.InstitutionPrefillAPI.as_view()), - path('institutes//', organisation_views.InstitutionAPI.as_view()), - path('institutes///', organisation_views.InstitutionAPI.as_view()), - path('institutes/org/affiliation/show/', organisation_views.AffiliationGetPostUpdateDeleteAPI.as_view()), - path('institutes/org/affiliation/create/', organisation_views.AffiliationGetPostUpdateDeleteAPI.as_view()), - path('institutes/org/affiliation/edit//', organisation_views.AffiliationGetPostUpdateDeleteAPI.as_view()), - path('institutes/org/affiliation/delete//', organisation_views.AffiliationGetPostUpdateDeleteAPI.as_view()), - path('departments/', organisation_views.DepartmentAPI.as_view()), - path('departments/create/', organisation_views.DepartmentAPI.as_view()), - path('departments/edit//', organisation_views.DepartmentAPI.as_view()), - path('departments/delete//', organisation_views.DepartmentAPI.as_view()), - path('affiliation/list/', organisation_views.AffiliationListAPI.as_view()), - path('merge_organizations//', organisation_views.OrganizationMergerView.as_view()), - path('karma-type/create/', organisation_views.OrganizationKarmaTypeGetPostPatchDeleteAPI.as_view()), - path('karma-log/create/', organisation_views.OrganizationKarmaLogGetPostPatchDeleteAPI.as_view()), - path('base-template/', organisation_views.OrganisationBaseTemplateAPI.as_view()), - path('import/', organisation_views.OrganisationImportAPI.as_view()), - path('transfer/', organisation_views.TransferAPI.as_view()), + path( + "institutes/create/", + organisation_views.InstitutionPostUpdateDeleteAPI.as_view(), + ), + path( + "institutes/edit//", + organisation_views.InstitutionPostUpdateDeleteAPI.as_view(), + ), + path( + "institutes/delete//", + organisation_views.InstitutionPostUpdateDeleteAPI.as_view(), + ), + path( + "institutes//csv/", organisation_views.InstitutionCSVAPI.as_view() + ), + path( + "institutes/info//", + organisation_views.InstitutionDetailsAPI.as_view(), + ), + path( + "institutes/prefill//", + organisation_views.InstitutionPrefillAPI.as_view(), + ), + path("institutes//", organisation_views.InstitutionAPI.as_view()), + path( + "institutes///", + organisation_views.InstitutionAPI.as_view(), + ), + path( + "institutes/org/affiliation/show/", + organisation_views.AffiliationGetPostUpdateDeleteAPI.as_view(), + ), + path( + "institutes/org/affiliation/create/", + organisation_views.AffiliationGetPostUpdateDeleteAPI.as_view(), + ), + path( + "institutes/org/affiliation/edit//", + organisation_views.AffiliationGetPostUpdateDeleteAPI.as_view(), + ), + path( + "institutes/org/affiliation/delete//", + organisation_views.AffiliationGetPostUpdateDeleteAPI.as_view(), + ), + path("departments/", organisation_views.DepartmentAPI.as_view()), + path("departments/create/", organisation_views.DepartmentAPI.as_view()), + path( + "departments/edit//", + organisation_views.DepartmentAPI.as_view(), + ), + path( + "departments/delete//", + organisation_views.DepartmentAPI.as_view(), + ), + path("affiliation/list/", organisation_views.AffiliationListAPI.as_view()), + path( + "merge_organizations//", + organisation_views.OrganizationMergerView.as_view(), + ), + path( + "karma-type/create/", + organisation_views.OrganizationKarmaTypeGetPostPatchDeleteAPI.as_view(), + ), + path( + "karma-log/create/", + organisation_views.OrganizationKarmaLogGetPostPatchDeleteAPI.as_view(), + ), + path("base-template/", organisation_views.OrganisationBaseTemplateAPI.as_view()), + path("import/", organisation_views.OrganisationImportAPI.as_view()), + path("transfer/", organisation_views.TransferAPI.as_view()), + path("verify/list/", organisation_views.UnverifiedOrganizationsListAPI.as_view()), + path("verify//", organisation_views.VerifyOrganizationAPI.as_view()), ] diff --git a/api/dashboard/user/dash_user_views.py b/api/dashboard/user/dash_user_views.py index bd20b284..c7d8c875 100644 --- a/api/dashboard/user/dash_user_views.py +++ b/api/dashboard/user/dash_user_views.py @@ -373,28 +373,38 @@ def post(self, request): return CustomResponse( response={"user_id": user.id, "profile_pic": uploaded_file_url} ).get_success_response() - - + + class UserAddOrgAPI(APIView): - def post(self,request): - user = User.objects.filter(id=JWTUtils.fetch_user_id(request)).first() + def post(self, request): + user_id = JWTUtils.fetch_user_id(request) + if not (user := cache.get(f"db_user_{user_id}")): + user = User.objects.filter(id=user_id).first() if user is None: return CustomResponse( general_message="No user data available" ).get_failure_response() - serializer=dash_user_serializer.UserOrgLinkSerializer(data=request.data,context={'user':user}) - if(serializer.is_valid()): + serializer = dash_user_serializer.UserOrgLinkSerializer( + data=request.data, context={"user": user} + ) + if serializer.is_valid(): serializer.save() - return CustomResponse(general_message="organisation linked successfully").get_success_response() + return CustomResponse( + general_message="organisation linked successfully" + ).get_success_response() return CustomResponse(response=serializer.errors).get_failure_response() - def get(self,request): + + def get(self, request): user = User.objects.filter(id=JWTUtils.fetch_user_id(request)).first() if user is None: return CustomResponse( general_message="No user data available" ).get_failure_response() - - links = UserOrganizationLink.objects.filter(user=user,org__org_type=OrganizationType.COLLEGE.value).select_related('org','department') - serializer=dash_user_serializer.GetUserLinkSerializer(instance=links,many=True) + + links = UserOrganizationLink.objects.filter( + user=user, org__org_type=OrganizationType.COLLEGE.value + ).select_related("org", "department") + serializer = dash_user_serializer.GetUserLinkSerializer( + instance=links, many=True + ) return CustomResponse(response=serializer.data).get_success_response() - \ No newline at end of file diff --git a/api/register/register_views.py b/api/register/register_views.py index e86ffd5f..ed7d5345 100644 --- a/api/register/register_views.py +++ b/api/register/register_views.py @@ -110,6 +110,26 @@ def post(self, request): return CustomResponse(general_message=serializer.errors).get_failure_response() +class UnverifiedOrganizationCreateView(APIView): + permission_classes = [CustomizePermission] + + def post(self, request): + user_id = JWTUtils.fetch_user_id(request) + serialized_org = serializers.UnverifiedOrganizationCreateSerializer( + data=request.data, context={"user_id": user_id} + ) + + if not serialized_org.is_valid(): + return CustomResponse( + general_message=serialized_org.errors + ).get_failure_response() + + serialized_org.save() + return CustomResponse( + general_message="Organization Request Submitted." + ).get_success_response() + + class UserRegisterValidateAPI(APIView): def put(self, request): serialized_user = serializers.RegisterSerializer(data=request.data) diff --git a/api/register/serializers.py b/api/register/serializers.py index 59aca448..8af636f3 100644 --- a/api/register/serializers.py +++ b/api/register/serializers.py @@ -11,6 +11,7 @@ District, Organization, State, + UnverifiedOrganization, UserOrganizationLink, Zone, ) @@ -272,6 +273,21 @@ def create(self, validated_data): return kkem_link +class UnverifiedOrganizationCreateSerializer(serializers.ModelSerializer): + def create(self, validated_data): + validated_data["created_by_id"] = self.context.get("user_id") + return UnverifiedOrganization.objects.create(**validated_data) + + def validate_org_type(self, org_type): + if org_type not in OrganizationType.get_all_values(): + raise serializers.ValidationError("Invalid organization type") + return org_type + + class Meta: + model = UnverifiedOrganization + fields = ["title", "org_type"] + + class UserSerializer(serializers.ModelSerializer): role = serializers.PrimaryKeyRelatedField( queryset=Role.objects.all(), required=False, write_only=True diff --git a/api/register/urls.py b/api/register/urls.py index 0537f4a0..ef1b1c4f 100644 --- a/api/register/urls.py +++ b/api/register/urls.py @@ -24,4 +24,8 @@ path("user-zone/", register_views.UserZoneAPI.as_view()), path("interests/", register_views.UserInterestAPI.as_view()), path("connect-discord/", register_views.ConnectDiscordAPI.as_view()), + path( + "organization/create/", + register_views.UnverifiedOrganizationCreateView.as_view(), + ), ] diff --git a/db/organization.py b/db/organization.py index 12984318..29066d55 100644 --- a/db/organization.py +++ b/db/organization.py @@ -218,3 +218,19 @@ def state(self): @property def district(self): return self.org.district + + +class UnverifiedOrganization(models.Model): + id = models.CharField(primary_key=True, max_length=36, default=lambda:str(uuid.uuid4())) + title = models.CharField(max_length=100) + org_type = models.CharField(max_length=25) + verified = models.BooleanField(null=True) + verified_by = models.ForeignKey(User, models.DO_NOTHING, db_column='verified_by', related_name='unverified_organizations_verified_by', null=True) + verified_at = models.DateTimeField(null=True) + org = models.ForeignKey(Organization, models.DO_NOTHING, related_name='unverified_organizations_org') + created_by = models.ForeignKey(User, models.DO_NOTHING, db_column='created_by', related_name='unverified_organizations_created_by') + created_at = models.DateTimeField(auto_now=True) + + class Meta: + managed = False + db_table = 'unverified_organization'