diff --git a/lms/djangoapps/appsembler_api/apidocs.md b/lms/djangoapps/appsembler_api/apidocs.md index e926b33347ea..6f3912cc5cfa 100644 --- a/lms/djangoapps/appsembler_api/apidocs.md +++ b/lms/djangoapps/appsembler_api/apidocs.md @@ -175,6 +175,71 @@ Cache-Control: no-cache } ``` +### Update user account + +This endpoint allows to update a user account. Receives a lookup parameter and N optional parameters which are all the attributes that needs to be updated. +The endpoint can update email, all available profile fields and also has support for [registration extension form fields](https://github.com/open-craft/custom-form-app). + +* URL: `/appsembler_api/v0/accounts/update_user +* Method: `POST` +* Data Params + * Required: + * `user_lookup` # can be username or email + * Optional: + * `email` # user's email + * `name` # user full name + * `country` # country iso code, ex: `ES`, `UY`, `US` + * `gender` # user gender, accepted values `m`, `f` or `o` + * `level_of_education` # user education, accepted values `p`, `m`, `b`, `a`, `hs`, `jhs`, `el`, `none` or `other`, + * `year_of_birth` # four digit year as string + * `city` # text + * `mailing_address` # long text + * `language` # language iso code, ex 'ES', 'EN' + * `goals` # long text + * `bio` # text + +You also can send extended profile form fiels, but that depends on every installation, you'll need to find the [registration extension form app fork](https://github.com/open-craft/custom-form-app) that is installed on the instance, and check the field names, and accepted values. + +* Success Response + * Code: 200 + * Content: Success message and list of updated fields and values +``` +{ + "success": "The following fields has been updated: name=Doe, John, country=ES" +} +``` +* Error Responses: + * Code: 404 NOT FOUND + * Reason: User not exists + +* Error Responses: + * Code: 400 NOT FOUND + * Reason: No user lookup parameter sent + +* Example call: +``` +POST /appsembler_api/v0/accounts/update_user +Host: example.com +Content-Type: application/json +Authorization: Bearer cbf6a5de322cf6a4323c957a882xy1s321c954b86 +Cache-Control: no-cache +{ + "user_lookup": "staff@example.com", + "emai": "new_staff@example.com", + "name": "Staff New Name", + "country": "US", + "gender": "f", + "level_of_education": "m", + "year_of_birth": "2000", + "city": "Montevideo", + "mailing_address": "A streen and a number 2345 FL, USA", + "language": "es", + "goals": "To be famous", + "bio": "I'm not famous yet" + "district": "101815" # a custom form field +} +``` + ### Check Existing Username This endpoint is a tool to check if an user exists given the username. diff --git a/lms/djangoapps/appsembler_api/urls.py b/lms/djangoapps/appsembler_api/urls.py index 6f55ad8e99a6..b21604bd0b60 100644 --- a/lms/djangoapps/appsembler_api/urls.py +++ b/lms/djangoapps/appsembler_api/urls.py @@ -9,6 +9,7 @@ url(r'^accounts/user_without_password', views.CreateUserAccountWithoutPasswordView.as_view(), name="create_user_account_without_password_api"), url(r'^accounts/create', views.CreateUserAccountView.as_view(), name="create_user_account_api"), url(r'^accounts/connect', views.UserAccountConnect.as_view(), name="user_account_connect_api"), + url(r'^accounts/update_user', views.UpdateUserAccount.as_view(), name="user_account_update_user"), url(r'^accounts/get-user/(?P[\w.+-]+)', views.GetUserAccountView.as_view(), name="get_user_account_api"), # bulk enrollment API diff --git a/lms/djangoapps/appsembler_api/views.py b/lms/djangoapps/appsembler_api/views.py index b0ce661bfa3d..9d6d1969ed3d 100644 --- a/lms/djangoapps/appsembler_api/views.py +++ b/lms/djangoapps/appsembler_api/views.py @@ -9,6 +9,7 @@ from django.core.urlresolvers import reverse from django.contrib.auth.models import User from django.http import Http404 +from django.db.models import Q from django.core.validators import validate_email from rest_framework.views import APIView @@ -26,9 +27,10 @@ IsStaffOrOwner, ApiKeyHeaderPermissionIsAuthenticated ) +from student.forms import get_registration_extension_form from student.views import create_account_with_params from student.models import CourseEnrollment, EnrollmentClosedError, \ - CourseFullError, AlreadyEnrolledError + CourseFullError, AlreadyEnrolledError, UserProfile from course_modes.models import CourseMode from courseware.courses import get_course_by_id @@ -241,6 +243,107 @@ def post(self, request): return response +class UpdateUserAccount(APIView): + """ HTTP endpoint for updating and user account """ + + authentication_classes = OAuth2AuthenticationAllowInactiveUser, + permission_classes = IsStaffOrOwner, + + def post(self, request): + """ + This endpoint allows to change user attributes including email, profile + attributes and extended profile fields. Receives one mandatory param + user_lookup that can be an email or username to lookup the user to + update and the rest of parameters are option. Any attribute to update + must be sent in key:val JSON format. + + URL: /appsembler_api/v0/accounts/update_user + Arguments: + request (HttpRequest) + JSON (application/json) + { + "user_lookup": email or username to lookup the user to update, + # mandatory ex: "staff4@example.com" or "staff4" + + "email": "staff@example.com", + "bio": "this is my bio", + "country": "BR" + } + Returns: + HttpResponse: 200 on success, {"success ": "list of updated params"} + HttpResponse: 404 if the doesn't exists + HttpResponse: 400 Incorrect parameters, basically if username or + email parameter is not sent + """ + data = request.data + + if data['user_lookup'].strip() == "": + errors = {"lookup_error": "No user lookup has been provided"} + return Response(errors, status=400) + + user = User.objects.filter( + Q(username=data['user_lookup']) | Q(email=data['user_lookup']) + ) + + if user: + user = user[0] + else: + errors = { + "user_not_found": "The user for the Given username or email doesn't exists" + } + return Response(errors, status=404) + + updated_fields = {} + + # update email + if 'email' in data and data['email'] != user.email: + user_exists = check_account_exists(email=data['email']) + if user_exists: + errors = {"integrity_error": "the user email you're trying to set already belongs to another user"} + return Response(errors, status=400) + + user.email = data['email'] + user.save() + updated_fields.update({'email': data['email']}) + + # update profile fields + profile_fields = [ + "name", "level_of_education", "gender", "mailing_address", "city", + "country", "goals", "bio", "year_of_birth", "language" + ] + + profile_fields_to_update = {} + for field in profile_fields: + if field in data: + profile_fields_to_update[field] = data[field] + + if len(profile_fields_to_update): + UserProfile.objects.filter(user=user).update(**profile_fields_to_update) + updated_fields.update(profile_fields_to_update) + + # If there is an exension form fields installed update them too + custom_profile_fields_to_update = {} + custom_form = get_registration_extension_form() + + if custom_form is not None: + for custom_field in custom_form.fields: + if custom_field in data: + custom_profile_fields_to_update[custom_field] = data[custom_field] + updated_fields.update(custom_profile_fields_to_update) + + if len(custom_profile_fields_to_update): + custom_form.Meta.model.objects.filter(user=user).update( + **custom_profile_fields_to_update) + + return Response( + {"success": "The following fields has been updated: {}".format( + ', '.join( + '{}={}'.format(f, v) for f, v in updated_fields.items()) + ) + }, + status=200) + + class GetUserAccountView(APIView): authentication_classes = OAuth2AuthenticationAllowInactiveUser, permission_classes = IsStaffOrOwner, @@ -260,7 +363,6 @@ def get(self, request, username): """ try: account_settings = User.objects.select_related('profile').get(username=username) - print account_settings except User.DoesNotExist: return Response( status=status.HTTP_404_NOT_FOUND