class DemographicDataSerializer(UuidHyperlinkedModelSerializer): resource_name = "demographics" country = serializers.CharField(default="") date_created = serializers.DateTimeField(read_only=True, source="created_at") url = serializers.HyperlinkedIdentityField( view_name="api:demographicdata-detail", lookup_field="uuid") class Meta: model = DemographicData fields = ( "url", "number_of_children", "child_birthdays", "languages_spoken_at_home", "number_of_guardians", "number_of_guardians_explanation", "race_identification", "age", "gender", "education_level", "spouse_education_level", "annual_income", "former_lookit_annual_income", "lookit_referrer", "number_of_books", "additional_comments", "country", "state", "density", "extra", "date_created", "pk", )
class BaseSerializer(serializers.ModelSerializer): created_at = serializers.DateTimeField(read_only=True) def is_valid(self, *args, **kwargs): # Prime data so the validators are called (and default values filled # if client didn't pass them.) self.initial_data.setdefault("created_by_group", self._default_group()) self.initial_data.setdefault("modified_by_group", self._default_group()) return super().is_valid(*args, **kwargs) def validate(self, *args, **kwargs): validated_data = super().validate(*args, **kwargs) user = self.context["request"].user validated_data["created_by_user"] = user.username validated_data["modified_by_user"] = user.username self.Meta.model.check_permissions(self.context["request"]) if self.instance is not None: self.instance.check_object_permissions(self.context["request"]) return validated_data def validate_created_by_group(self, value): # Created by group can be set on creation, then must remain constant if self.instance: # can't change created_by_group on existing instances return self.instance.created_by_group else: return self._validate_group(value, "created_by_group") def validate_modified_by_group(self, value): # Modified by group is validated against the user's list of groups, # with a fallback to the default group if no value is given return self._validate_group(value or self._default_group(), "modified_by_group") def _validate_group(self, value, field_name): user = self.context["request"].user if value and value not in user.groups: raise ValidationError( f"Given {field_name} '{value}' is not part of user's assigned groups" ) return value def _default_group(self): user = self.context["request"].user return user.group if not isinstance(user, AnonymousUser) else None class Meta: fields = ( "created_at", "created_by_user", "created_by_group", "modified_at", "modified_by_user", "modified_by_group", "meta", )
class BaseSerializer(serializers.ModelSerializer): created_at = serializers.DateTimeField(read_only=True) def validate(self, *args, **kwargs): validated_data = super().validate(*args, **kwargs) self.Meta.model.check_permissions(self.context["request"]) if self.instance is not None: self.instance.check_object_permissions(self.context["request"]) user = self.context["request"].user group = user.group if not isinstance(user, AnonymousUser) else None validated_data["modified_by_user"] = user.username validated_data["modified_by_group"] = group if self.instance is not None: validated_data["created_by_user"] = user.username validated_data["created_by_group"] = group return validated_data class Meta: fields = ( "created_at", "created_by_user", "created_by_group", "modified_at", "modified_by_user", "modified_by_group", "meta", )
class ResponseSerializer(UuidHyperlinkedModelSerializer): """Gets hyperlink related fields. XXX: It's important to keep read_only set to true here - otherwise, a queryset is necessitated, which implicates get_attribute from ResourceRelatedField """ created_on = serializers.DateTimeField(read_only=True, source="date_created") url = serializers.HyperlinkedIdentityField(view_name="api:response-detail", lookup_field="uuid") study = PatchedHyperlinkedRelatedField( read_only=True, related_link_view_name="api:study-detail", related_link_lookup_field="study.uuid", related_link_url_kwarg="uuid", ) user = PatchedHyperlinkedRelatedField( read_only=True, source="child", related_link_view_name="api:user-detail", related_link_lookup_field="child.user.uuid", related_link_url_kwarg="uuid", required=False, ) child = PatchedHyperlinkedRelatedField( read_only=True, related_link_view_name="api:child-detail", related_link_lookup_field="child.uuid", related_link_url_kwarg="uuid", ) demographic_snapshot = PatchedHyperlinkedRelatedField( read_only=True, related_link_view_name="api:demographicdata-detail", related_link_lookup_field="demographic_snapshot.uuid", related_link_url_kwarg="uuid", required=False, ) class Meta: model = Response fields = ( "url", "conditions", "global_event_timings", "exp_data", "sequence", "completed", "child", "user", "study", "completed_consent_frame", "demographic_snapshot", "created_on", "is_preview", "pk", "withdrawn", )
class CollectionSerializer(serializers.Serializer): id = serializers.CharField(read_only=True) title = serializers.CharField(required=True) description = serializers.CharField(required=False, allow_blank=True) tags = serializers.CharField(required=False, allow_blank=True) settings = serializers.JSONField(required=False) submission_settings = serializers.JSONField(required=False) created_by_org = serializers.CharField(allow_blank=True, required=False) created_by = RelationshipField( related_view='user-detail', related_view_kwargs={'user_id': '<created_by.pk>'}, ) date_created = serializers.DateTimeField(read_only=True) date_updated = serializers.DateTimeField(read_only=True) groups = RelationshipField(related_view='collection-group-list', related_view_kwargs={'pk': '<pk>'}) items = RelationshipField(related_view='collection-item-list', related_view_kwargs={'pk': '<pk>'}) class Meta: model = Collection fields = ['id', 'title', 'description', 'tags', 'created_by'] class JSONAPIMeta: resource_name = 'collections' def create(self, validated_data): user = self.context['request'].user collection = Collection.objects.create(created_by=user, **validated_data) assign_perm('api.approve_collection_items', user, collection) return collection def update(self, collection, validated_data): collection.title = validated_data.get('title', collection.title) collection.description = validated_data.get('description', collection.description) collection.tags = validated_data.get('tags', collection.tags) collection.settings = validated_data.get('settings', collection.settings) collection.submission_settings = validated_data.get( 'submission_settings', collection.submission_settings) collection.created_by_org = validated_data.get( 'created_by_org', collection.created_by_org) collection.save() return collection
class MeetingSerializer(CollectionSerializer): location = serializers.CharField(allow_blank=True, allow_null=True, required=False) address = serializers.CharField(allow_blank=True, allow_null=True, required=False) start_date = serializers.DateTimeField(allow_null=True, required=False) end_date = serializers.DateTimeField(allow_null=True, required=False) groups = RelationshipField(related_view='meeting-group-list', related_view_kwargs={'pk': '<pk>'}) items = RelationshipField(related_view='meeting-item-list', related_view_kwargs={'pk': '<pk>'}) class Meta: model = Meeting class JSONAPIMeta: resource_name = 'meetings' def create(self, validated_data): user = self.context['request'].user meeting = Meeting.objects.create(created_by=user, **validated_data) assign_perm('api.approve_meeting_items', user, meeting) return meeting def update(self, meeting, validated_data): meeting.title = validated_data.get('title', meeting.title) meeting.description = validated_data.get('description', meeting.description) meeting.tags = validated_data.get('tags', meeting.tags) meeting.settings = validated_data.get('settings', meeting.settings) meeting.submission_settings = validated_data.get( 'submission_settings', meeting.submission_settings) meeting.created_by_org = validated_data.get('created_by_org', meeting.created_by_org) meeting.location = validated_data.get('location', meeting.location) meeting.start_date = validated_data.get('start_date', meeting.start_date) meeting.end_date = validated_data.get('end_date', meeting.end_date) meeting.save() return meeting
class ProviderRegistrationSerializer(ShareModelSerializer): status = serializers.SerializerMethodField() submitted_at = serializers.DateTimeField(read_only=True) submitted_by = serializers.HiddenField(default=serializers.CurrentUserDefault()) def get_status(self, obj): return ProviderRegistration.STATUS[obj.status] class Meta: model = models.ProviderRegistration fields = '__all__'
class GroupSerializer(serializers.Serializer): id = serializers.CharField(read_only=True) title = serializers.CharField(required=True) description = serializers.CharField(allow_blank=True, required=False) created_by = UserSerializer(read_only=True) date_created = serializers.DateTimeField(read_only=True) date_updated = serializers.DateTimeField(read_only=True) items = RelationshipField( related_view='group-item-list', related_view_kwargs={'pk': '<collection.id>', 'group_id': '<pk>'} ) items = serializers.HyperlinkedRelatedField( many=True, read_only=True, view_name='track-detail' ) class Meta: model = Group class JSONAPIMeta: resource_name = 'groups' def create(self, validated_data): user = self.context['request'].user collection_id = self.context.get('collection_id', None) or self.context['request'].parser_context['kwargs'].get( 'pk', None) collection = CollectionBase.objects.get(id=collection_id) return Group.objects.create( created_by=user, collection=collection, **validated_data ) def update(self, group, validated_data): group.title = validated_data.get('title', None) description = validated_data.get('description', None) if description: group.description = description group.save() return group
class BaseSerializer(serializers.ModelSerializer): created_at = serializers.DateTimeField(read_only=True) modified_at = serializers.DateTimeField(read_only=True) created_by_user = serializers.ResourceRelatedField(read_only=True) def create(self, validated_data): user = self.context["request"].user if not isinstance(user, AnonymousUser): validated_data["created_by_user"] = user return super().create(validated_data) def validate(self, *args, **kwargs): validated_data = super().validate(*args, **kwargs) self.Meta.model.check_permissions(self.context["request"]) if self.instance is not None: self.instance.check_object_permissions(self.context["request"]) return validated_data class Meta: fields = ("created_at", "modified_at", "created_by_user", "meta")
class ResponseSerializer(UUIDSerializerMixin, ModelSerializer): created_on = serializers.DateTimeField(read_only=True, source='date_created') url = serializers.HyperlinkedIdentityField(view_name='response-detail', lookup_field='uuid') study = UUIDResourceRelatedField( queryset=Study.objects, many=False, related_link_view_name='study-detail', related_link_lookup_field='uuid', ) user = UUIDResourceRelatedField(source='child.user', queryset=User.objects, many=False, related_link_view_name='user-list', related_link_lookup_field='uuid', required=False) child = UUIDResourceRelatedField( queryset=Child.objects, many=False, related_link_view_name='child-detail', related_link_lookup_field='uuid', ) demographic_snapshot = UUIDResourceRelatedField( queryset=DemographicData.objects, many=False, related_link_view_name='demographicdata-detail', related_link_lookup_field='uuid', required=False) class Meta: model = Response fields = ( 'url', 'conditions', 'global_event_timings', 'exp_data', 'sequence', 'completed', 'child', 'user', 'study', 'demographic_snapshot', 'created_on', 'pk', )
class ChangedFieldSerializer(serializers.Serializer): title = serializers.CharField(max_length=200, required=False) priority = serializers.IntegerField(min_value=1, max_value=5, required=False) resolution = serializers.CharField(required=False) due_date = serializers.DateTimeField(required=False, allow_null=True) assigned_to = serializers.PrimaryKeyRelatedField( required=False, allow_null=True, queryset=Person.objects.all( ) # TODO restrict this queryset to something more sensible ) signaled_by = serializers.PrimaryKeyRelatedField( required=False, allow_null=True, queryset=Person.objects.all()) organization = serializers.PrimaryKeyRelatedField( required=False, allow_null=True, queryset=Organization.objects.all()) department = serializers.PrimaryKeyRelatedField( required=False, allow_null=True, queryset=Team.objects.all())
class ShipmentActionRequestSerializer(serializers.Serializer): action_type = UpperEnumField(ActionType, lenient=True, ints_as_names=True) tracking_data = serializers.CharField(required=False, allow_null=True) document_id = serializers.CharField(required=False, allow_null=True) raw_asset_physical_id = serializers.CharField(required=False, allow_null=True) asset_physical_id = serializers.CharField(required=False, allow_null=True) action_timestamp = serializers.DateTimeField(required=False) def validate_action_type(self, action_type): shipment = self.context['shipment'] action_type.value.func.__self__ = shipment # Hack for getting dynamic partial funcs to work w/ can_proceed if not can_proceed(action_type.value.func): # Bad state transition raise exceptions.ValidationError(f'Action {action_type.name} not available while Shipment ' f'is in state {TransitState(shipment.state).name}') return action_type def validate_action_timestamp(self, action_timestamp): if settings.PROFILES_ENABLED and not is_internal_call(self.context['request'], 'third-party-integrator'): raise exceptions.ValidationError('Can only manually set timestamp for action on internal calls') if action_timestamp > datetime.now(timezone.utc): raise exceptions.ValidationError('Cannot set action for datetime in the future.') return action_timestamp
class ItemSerializer(serializers.Serializer): id = serializers.CharField(read_only=True) source_id = serializers.CharField() title = serializers.CharField(required=True) type = serializers.ChoiceField( choices=['project', 'preprint', 'registration', 'meeting', 'website']) status = serializers.ChoiceField( choices=['approved', 'pending', 'rejected']) url = serializers.URLField() created_by = UserSerializer(read_only=True) metadata = serializers.JSONField(required=False) date_added = serializers.DateTimeField(read_only=True, allow_null=True) date_submitted = serializers.DateTimeField(read_only=True) class Meta: model = Item class JSONAPIMeta: resource_name = 'items' def create(self, validated_data): user = self.context['request'].user collection_id = self.context.get( 'collection_id', None) or self.context['request'].parser_context['kwargs'].get( 'pk', None) collection = Collection.objects.get(id=collection_id) allow_all = None if collection.settings: collection_settings = json.loads(collection.settings) allow_all = collection_settings.get('allow_all', None) collection_type = collection_settings.get('type', None) if collection_type and validated_data['type'] != collection_type: raise ValueError('Collection only accepts items of type ' + collection_type) status = 'pending' if user.has_perm('api.approve_items', collection) or allow_all: status = 'approved' validated_data['date_added'] = timezone.now() group_id = self.context.get( 'group_id', None) or self.context['request'].parser_context['kwargs'].get( 'group_id', None) if group_id: validated_data['group'] = Group.objects.get(id=group_id) validated_data['status'] = status item = Item.objects.create(created_by=user, collection=collection, **validated_data) return item def update(self, item, validated_data): user = self.context['request'].user status = validated_data.get('status', item.status) collection_id = self.context.get( 'collection_id', None) or self.context['request'].parser_context['kwargs'].get( 'pk', None) if collection_id: collection = Collection.objects.get(id=collection_id) else: collection = item.collection if status != item.status and user.has_perm('api.approve_items', collection): raise exceptions.PermissionDenied( detail='Cannot change submission status.') elif user.id != item.created_by_id and validated_data.keys() != [ 'status' ]: raise exceptions.PermissionDenied( detail='Cannot update another user\'s submission.') group_id = self.context.get( 'group_id', None) or self.context['request'].parser_context['kwargs'].get( 'group_id', None) if group_id: group = Group.objects.get(id=group_id) else: group = None item_type = validated_data.get('type', item.type) if collection.settings: collection_settings = json.loads(collection.settings) collection_type = collection_settings.get('type', None) if collection_type and item_type != collection_type: raise ValueError('Collection only accepts items of type ' + collection_type) item.group = group item.source_id = validated_data.get('source_id', item.source_id) item.title = validated_data.get('title', item.title) item.type = item_type item.status = status item.url = validated_data.get('url', item.url) item.metadata = validated_data.get('metadata', item.metadata) item.save() return item
class BaseSerializer(serializers.ModelSerializer): created_at = serializers.DateTimeField(read_only=True) modified_at = serializers.DateTimeField(read_only=True) created_by_user = serializers.ResourceRelatedField(read_only=True)
class KaznetTaskSerializer(GenericForeignKeySerializer): """ Main Task Serializer class """ start = serializers.DateTimeField(required=False) submission_count = serializers.SerializerMethodField() amount = SerializableAmountField(source='bounty', required=False, write_only=True) total_bounty_payout = SerializableAmountField(read_only=True) current_bounty_amount = SerializableAmountField(read_only=True) bounty = BountySerializer(read_only=True) locations_input = TaskLocationCreateSerializer(many=True, required=False, write_only=True) task_locations = serializers.SerializerMethodField(read_only=True) # pylint: disable=too-few-public-methods class Meta: """ Meta options for KaznetTaskSerializer """ fields = [ 'id', 'created', 'created_by', 'modified', 'name', 'amount', 'parent', 'estimated_time', 'approved_submissions_count', 'pending_submissions_count', 'rejected_submissions_count', 'total_bounty_payout', 'current_bounty_amount', 'required_expertise', 'description', 'xform_title', 'xform_id_string', 'xform_version', 'xform_owner', 'xform_owner_url', 'xform_ona_id', 'xform_project_id', 'status_display', 'required_expertise_display', 'client', 'start', 'end', 'timing_rule', 'total_submission_target', 'user_submission_target', 'status', 'submission_count', 'bounty', 'target_content_type', 'target_id', 'segment_rules', 'locations', 'created_by_name', 'client_name', 'locations_input', 'task_locations', ] model = Task read_only_fields = [ 'locations', 'created_by', 'created_by_name', 'client_name' ] def get_submission_count(self, obj): # pylint: disable=no-self-use """ Add a custom method to get submission count """ try: return obj.submission_count except AttributeError: return obj.submissions # pylint: disable=no-self-use def validate_timing_rule(self, value): """ Validate timing rule """ if value is not None: if validate_rrule(value) is True: return value raise serializers.ValidationError(INVALID_TIMING_RULE) return None def validate_parent(self, value): """ Validate task parent field """ valid_parent = validate_parent_field(self.instance, value) if not valid_parent and self.instance is not None: # tasks cannot be their own parents raise serializers.ValidationError(SAME_PARENT) return value def validate(self, attrs): """ Object level validation method for TaskSerializer """ if not self.instance: attrs = self._validate_new(attrs) # If end date is present we validate that it is greater than start_date if attrs.get('end') is not None: # If end date is lesser than the start date raise an error the_start = attrs.get('start') if the_start is None and self.instance is not None: the_start = self.instance.start if not the_start: raise serializers.ValidationError( {'start': INVALID_START_DATE}) if attrs['end'] < the_start: raise serializers.ValidationError({'end': INVALID_END_DATE}) # if status is active and end date is in the past raise an error if attrs['end'] < timezone.now() and\ attrs.get('status') == Task.ACTIVE: raise serializers.ValidationError({'end': PAST_END_DATE}) # If start date is present and this is an existing object, we validate # that the start date is not greater than the existing end date if attrs.get('start') is not None and self.instance is not None\ and self.instance.end is not None and attrs.get('start') >\ self.instance.end: raise serializers.ValidationError({'start': INVALID_START_DATE}) # set automated statuses # scheduled => tasks which start in the future if attrs.get('start') and attrs.get('start') > timezone.now(): attrs['status'] = Task.SCHEDULED # draft => tasks with no form target_object_id = attrs.get('target_object_id') if target_object_id is None and self.instance is not None: target_object_id = self.instance.target_object_id if target_object_id is None: attrs['status'] = Task.DRAFT return super().validate(attrs) def _validate_new(self, attrs): """ Object level validation method run on New Task Instances """ # get timing_rule from input timing_rule_from_input = attrs.get('timing_rule') # get start from input start_from_input = attrs.get('start') # get timing rules from task locations tasklocation_timing_rules = [] for location_input in attrs.get('locations_input', []): tasklocation_timing_rules.append(location_input['timing_rule']) # get start and end from timing rules timing_rules = [timing_rule_from_input] +\ tasklocation_timing_rules timing_rule_start, timing_rule_end =\ get_start_end_from_timing_rules(timing_rules) if not start_from_input: # start was not input by user, we try and generate it from # timing rules if not timing_rule_start: # we cannot determine a start time raise serializers.ValidationError({ 'timing_rule': MISSING_START_DATE, 'start': MISSING_START_DATE, 'locations_input': MISSING_START_DATE }) attrs['start'] = timing_rule_start # get end attrs['end'] = attrs.get('end', timing_rule_end) return attrs def get_task_locations(self, obj): """ Get serialized TaskLocation objects """ # pylint: disable=no-member queryset = TaskLocation.objects.filter(task=obj) return TaskLocationSerializer(queryset, many=True).data def create(self, validated_data): """ Custom Create method for Task """ # get current user and set that as created_by try: request = self.context['request'] except KeyError: pass else: if request is not None: user = getattr(request, 'user', None) if user.is_authenticated: validated_data['created_by'] = user # get the supplied amount try: amount = validated_data.pop('bounty') except KeyError: amount = None # get the input locations locations_data = validated_data.pop('locations_input', []) # create the task task = super().create(validated_data) # create the bounty object create_bounty(task, amount) # create the TaskLocations for location_data in locations_data: location_data['task'] = task TaskLocationSerializer.create(TaskLocationSerializer(), validated_data=location_data) return task def update(self, instance, validated_data): """ Custom Update method for Task """ # get the supplied amount try: amount = validated_data.pop('bounty') except KeyError: amount = None # get the input locations try: locations_data = validated_data.pop('locations_input') except KeyError: locations_data = None # update the task task = super().update(instance, validated_data) # create the bounty object create_bounty(task, amount) if locations_data is not None: # update the TaskLocations # we assume that this (locations_data) is the one final list of # locations to be linked to this task and that other relationships # should be removed. # If task_locations is empty it means the user is # removing all Task and Location relationships # pylint: disable=no-member TaskLocation.objects.filter(task=task).delete() for location_data in locations_data: location_data['task'] = task TaskLocationSerializer.create(TaskLocationSerializer(), validated_data=location_data) return task
class Meta: start_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S") end_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S") model = ActivityRecord fields = ('id', 'url', 'name', 'locations', 'start_time', 'end_time')
class UserProfileSerializer(serializers.ModelSerializer): """ Serializer class for UserProfile model """ first_name = serializers.CharField(source="user.first_name") last_name = serializers.CharField(source="user.last_name") email = serializers.EmailField(source="user.email") password = serializers.CharField( source="user.password", allow_null=True, default=None, required=False, write_only=True, ) last_login = serializers.DateTimeField( source="user.last_login", read_only=True) submission_count = serializers.SerializerMethodField() amount_earned = SerializableAmountField(read_only=True) avg_amount_earned = SerializableAvgAmountEarnedField(read_only=True) metadata = serializers.JSONField(read_only=True) class Meta: # pylint: disable=too-few-public-methods """ class meta options """ model = UserProfile fields = [ "id", "created", "modified", "role_display", "gender_display", "expertise_display", "first_name", "last_name", "password", "email", "ona_pk", "ona_username", "payment_number", "approved_submissions", "rejected_submissions", "approval_rate", "amount_earned", "last_login", "avg_submissions", "avg_approved_submissions", "avg_rejected_submissions", "avg_approval_rate", "avg_amount_earned", "phone_number", "role", "expertise", "gender", "national_id", "submission_count", "address", "metadata", ] owner_only_fields = ("metadata", ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if self.instance and hasattr(self.Meta, "owner_only_fields"): request = self.context.get("request") is_permitted = (request and request.user and request.user == self.instance.user) if isinstance(self.instance, QuerySet) or not is_permitted or not request: for field in getattr(self.Meta, "owner_only_fields"): self.fields.pop(field) def get_submission_count(self, obj): # pylint: disable=no-self-use """ Get the submission count """ return obj.user.submission_set.count() def validate_password(self, value): """ Custom validation for Password Field """ if not self.instance: # On create of a new user Password shouldn't be none if value is None: raise serializers.ValidationError(NEED_PASSWORD_ON_CREATE) if value is not None: validate_password(value) return value def create(self, validated_data): """ Custom create method to create User object then UserProfile object """ user_data = validated_data.pop("user") user_data["username"] = validated_data.get("ona_username") first_name = user_data.get("first_name") last_name = user_data.get("last_name") email = user_data.get("email") password = user_data.get("password") created, data = create_ona_user( settings.ONA_BASE_URL, user_data["username"], first_name, last_name, email, password, ) if not created: raise serializers.ValidationError(data) if not data: raise serializers.ValidationError(CANNOT_ACCESS_ONADATA) ona_pk = data.get("id") metadata = data.get("metadata") add_team_member(settings.ONA_BASE_URL, user_data["username"], settings.ONA_MEMBERS_TEAM_ID) # make user an admin of organisation at ona if validated_data.get("role") == UserProfile.ADMIN: updated = change_user_role( settings.ONA_BASE_URL, settings.ONA_ORG_NAME, user_data["username"], settings.ONA_OWNER_ROLE, ) if not updated: # default to contributor role incase admin fails validated_data["role"] = UserProfile.CONTRIBUTOR elif validated_data["role"] == UserProfile.CONTRIBUTOR: change_user_role( settings.ONA_BASE_URL, settings.ONA_ORG_NAME, user_data["username"], settings.ONA_CONTRIBUTER_ROLE, ) # set an unusable password by not passing the password to the create # method. Why, you ask? Because we don't want to store passwords # on the Kaznet site. Ona is the source of truth for this. try: del user_data["password"] except KeyError: pass # create the User object user = UserSerializer.create( UserSerializer(), validated_data=user_data) # populate the UserProfile object userprofile = user.userprofile userprofile.ona_pk = ona_pk userprofile.ona_username = user.username userprofile.payment_number = validated_data.get("payment_number") userprofile.phone_number = validated_data.get("phone_number") if validated_data.get("role"): userprofile.role = validated_data.get("role") userprofile.gender = validated_data.get("gender") if validated_data.get("national_id"): userprofile.national_id = validated_data.get("national_id") userprofile.expertise = validated_data.get("expertise") if metadata: userprofile.metadata["last_password_edit"] = metadata.get( settings.ONA_LAST_PASSWORD_EDIT_FIELD) userprofile.metadata["gravatar"] = data.get("gravatar") userprofile.save() return userprofile # pylint: disable=too-many-branches, too-many-statements def update(self, instance, validated_data): """ Custom update method for UserProfiles """ # deal with the user object user = instance.user user_data = validated_data.pop("user") username = user.username first_name = user_data.get("first_name") last_name = user_data.get("last_name") data = {} # you can't change username try: del user_data["username"] except KeyError: pass # you can't change email # this is because Onadata requires your current password when changing # the email. And we cannot get the user's current password try: del user_data["email"] except KeyError: pass # you can't change password # this is because Onadata requires your current password when changing # the password. And we cannot get the user's current password try: del user_data["password"] except KeyError: pass # only pick changed values significant to ona profile update_data = {} if first_name != instance.user.first_name: update_data["first_name"] = first_name if last_name != instance.user.last_name: update_data["last_name"] = last_name # update on ona only if there is are changes made significant to ona if any(update_data): updated, data = update_details(settings.ONA_BASE_URL, username, update_data) if not updated: raise serializers.ValidationError(data) if not data: raise serializers.ValidationError(CANNOT_ACCESS_ONADATA) # change role to admin if the user is not initially an admin if (validated_data.get("role") == UserProfile.ADMIN and instance.role != UserProfile.ADMIN): updated = change_user_role( settings.ONA_BASE_URL, settings.ONA_ORG_NAME, username, settings.ONA_OWNER_ROLE, ) if not updated: # default to previous role incase change to admin fails validated_data["role"] = instance.role # change role to contributor if user was admin initially elif (validated_data.get("role") != UserProfile.ADMIN and instance.role == UserProfile.ADMIN): updated = change_user_role( settings.ONA_BASE_URL, settings.ONA_ORG_NAME, username, settings.ONA_CONTRIBUTER_ROLE, ) if not updated: # default to previous role incase change to contributor fails validated_data["role"] = instance.role UserSerializer().update(instance=user, validated_data=user_data) # deal with the userprofile object instance.payment_number = validated_data.get("payment_number", instance.payment_number) instance.phone_number = validated_data.get("phone_number", instance.phone_number) instance.role = validated_data.get("role", instance.role) instance.gender = validated_data.get("gender", instance.gender) if instance.national_id: instance.national_id = validated_data.get("national_id", instance.national_id) instance.expertise = validated_data.get("expertise", instance.expertise) if any(data): metadata = data.get("metadata") gravatar = data.get("gravatar") try: instance.metadata["last_password_edit"] = metadata.get( settings.ONA_LAST_PASSWORD_EDIT_FIELD) except AttributeError: pass instance.metadata["gravatar"] = gravatar instance.save() return instance