-
Notifications
You must be signed in to change notification settings - Fork 96
Feat/user profile enhancement #2718
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,7 +17,7 @@ | |
| UserLvlLink, | ||
| UserIgLvlLink, | ||
| ) | ||
| from db.user import User, UserSettings, Socials | ||
| from db.user import User, UserSettings, Socials, UserProfile | ||
| from utils.exception import CustomException | ||
| from utils.permission import JWTUtils | ||
| from utils.types import ( | ||
|
|
@@ -577,3 +577,80 @@ def get_college_name(self, obj): | |
| org_type = self._get_org_type(obj) | ||
| user_org_link = self._get_user_org_link(obj, org_type) | ||
| return user_org_link.org.title if user_org_link and user_org_link.org else None | ||
|
|
||
|
|
||
| class UserProfileSerializer(serializers.ModelSerializer): | ||
| user_id = serializers.CharField(source="user.id", read_only=True) | ||
| full_name = serializers.CharField(source="user.full_name", read_only=True) | ||
| email = serializers.CharField(source="user.email", read_only=True) | ||
| muid = serializers.CharField(source="user.muid", read_only=True) | ||
| profile_pic = serializers.SerializerMethodField() | ||
|
|
||
| class Meta: | ||
| model = UserProfile | ||
| fields = [ | ||
| "user_id", | ||
| "full_name", | ||
| "email", | ||
| "muid", | ||
| "profile_pic", | ||
| "bio", | ||
| "projects", | ||
| "experience", | ||
| "created_at", | ||
| "updated_at", | ||
| ] | ||
| read_only_fields = [ | ||
| "user_id", | ||
| "full_name", | ||
| "email", | ||
| "muid", | ||
| "profile_pic", | ||
| "created_at", | ||
| "updated_at", | ||
| ] | ||
|
|
||
| def get_profile_pic(self, obj): | ||
| return obj.user.profile_pic | ||
|
|
||
|
|
||
| class UserProfileUpdateSerializer(serializers.ModelSerializer): | ||
| class Meta: | ||
| model = UserProfile | ||
| fields = [ | ||
| "bio", | ||
| "projects", | ||
| "experience", | ||
| ] | ||
|
|
||
| def validate_projects(self, value): | ||
| if not isinstance(value, list): | ||
| raise serializers.ValidationError("Projects must be a list") | ||
|
|
||
| for project in value: | ||
| if not isinstance(project, dict): | ||
| raise serializers.ValidationError("Each project must be an object") | ||
|
|
||
| required_fields = ["title", "link", "description", "tags"] | ||
| for field in required_fields: | ||
| if field not in project: | ||
| raise serializers.ValidationError(f"Project must have '{field}' field") | ||
|
|
||
| return value | ||
|
|
||
| def validate_experience(self, value): | ||
| if not isinstance(value, list): | ||
| raise serializers.ValidationError("Experience must be a list") | ||
|
|
||
| for exp in value: | ||
| if not isinstance(exp, dict): | ||
| raise serializers.ValidationError("Each experience entry must be an object") | ||
|
|
||
| return value | ||
|
Comment on lines
+626
to
+649
|
||
|
|
||
| def update(self, instance, validated_data): | ||
| instance.bio = validated_data.get("bio", instance.bio) | ||
| instance.projects = validated_data.get("projects", instance.projects) | ||
| instance.experience = validated_data.get("experience", instance.experience) | ||
| instance.save() | ||
| return instance | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,6 +26,7 @@ | |
| UserEndgoals, | ||
| UserRoleLink, | ||
| UserSettings, | ||
| UserProfile, | ||
| ) | ||
| from utils.permission import CustomizePermission, JWTUtils | ||
| from utils.response import CustomResponse | ||
|
|
@@ -726,3 +727,108 @@ def get(self, request, muid): | |
|
|
||
| serializer = profile_serializer.UserPermuteSerializer(user, many=False) | ||
| return CustomResponse(response=serializer.data).get_success_response() | ||
|
|
||
|
|
||
| class UserProfileEnhancedAPI(APIView): | ||
| """ | ||
| User Profile Enhancement API | ||
|
|
||
| Handles extended user profile information including bio, projects, and experience. | ||
|
|
||
| Endpoints: | ||
| GET /api/v1/dashboard/profile/user-profile/ - Fetch user profile | ||
| PATCH /api/v1/dashboard/profile/user-profile/ - Update user profile | ||
| """ | ||
|
|
||
| authentication_classes = [CustomizePermission] | ||
|
|
||
| def get(self, request): | ||
| """ | ||
| Fetch full user profile with bio, projects, and experience | ||
|
|
||
| Returns: | ||
| User profile with all details | ||
| """ | ||
| user_id = JWTUtils.fetch_user_id(request) | ||
| user = User.objects.filter(id=user_id).first() | ||
|
|
||
| if not user: | ||
| return CustomResponse( | ||
| general_message="User not found" | ||
| ).get_failure_response() | ||
|
|
||
| # Get or create user profile | ||
| user_profile, created = UserProfile.objects.get_or_create( | ||
| user=user, | ||
| defaults={"created_by_id": user_id, "updated_by_id": user_id} | ||
| ) | ||
|
|
||
| serializer = profile_serializer.UserProfileSerializer(user_profile, many=False) | ||
|
|
||
| return CustomResponse(response=serializer.data).get_success_response() | ||
|
|
||
| def patch(self, request): | ||
| """ | ||
| Update user profile fields (bio, projects, experience) | ||
|
|
||
| Request body: | ||
| { | ||
| "bio": "User biography", | ||
| "projects": [ | ||
| { | ||
| "title": "Project Title", | ||
| "link": "https://github.com/...", | ||
| "description": "Project description", | ||
| "tags": ["React", "Firebase"] | ||
| } | ||
| ], | ||
| "experience": [ | ||
| { | ||
| "company": "Company Name", | ||
| "position": "Position", | ||
| "duration": "2020-2023" | ||
| } | ||
| ] | ||
| } | ||
|
|
||
| Returns: | ||
| Updated user profile | ||
| """ | ||
| user_id = JWTUtils.fetch_user_id(request) | ||
| user = User.objects.filter(id=user_id).first() | ||
|
|
||
| if not user: | ||
| return CustomResponse( | ||
| general_message="User not found" | ||
| ).get_failure_response() | ||
|
|
||
| # Get or create user profile | ||
| user_profile, created = UserProfile.objects.get_or_create( | ||
| user=user, | ||
| defaults={"created_by_id": user_id, "updated_by_id": user_id} | ||
| ) | ||
|
|
||
| serializer = profile_serializer.UserProfileUpdateSerializer( | ||
| user_profile, data=request.data, partial=True | ||
| ) | ||
|
|
||
| if serializer.is_valid(): | ||
| serializer.save() | ||
|
|
||
|
Comment on lines
+805
to
+817
|
||
| DiscordWebhooks.general_updates( | ||
| WebHookCategory.USER_NAME.value, | ||
| WebHookActions.UPDATE.value, | ||
| user_id, | ||
| ) | ||
|
|
||
| response_serializer = profile_serializer.UserProfileSerializer( | ||
| user_profile, many=False | ||
| ) | ||
| return CustomResponse( | ||
| response=response_serializer.data | ||
| ).get_success_response() | ||
|
|
||
| return CustomResponse( | ||
| general_message="Invalid data", | ||
| error=serializer.errors | ||
| ).get_failure_response() | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file defines
UserProfileSerializertwice. The second definition overwrites the existingUserProfileSerializer(used for the mainUserprofile response), which will break endpoints that still expect the original serializer. Rename the new serializer (e.g.,UserProfileEnhancedSerializer) and update usages accordingly.