def to_internal_value(self, data): # Validate when passed as attached object or message attachment ret = self.to_internal_attached_value(data) if ret: return ret validated_data = super(MessageSerializer, self).to_internal_value(data) attachments = validated_data.get('attachments') text = validated_data.get('text') errors = OrderedDict() if not text and not attachments: # Todo: check why having string as the detail results in exception # raise serializers.ValidationError("Provide 'text' or 'attachments'") raise serializers.ValidationError({'': _("Provide 'text' or 'attachments'")}) if attachments is not None: if isinstance(attachments, list) and len(attachments): i = 0 errors['attachments'] = [] valid_types = ['shout', 'location', 'profile', 'images', 'videos'] types = ", ".join(map(str, valid_types)) for attachment in attachments: attachment_error = None if not any_in(valid_types, attachment): attachment_error = {'': _("Should have any of these properties: %(types)s") % {'types': types}} errors['attachments'].insert(i, attachment_error) i += 1 continue if 'location' in attachment: if 'latitude' not in attachment['location'] or 'longitude' not in attachment['location']: attachment_error = {'location': _("location object should have 'latitude' and 'longitude'")} if 'images' in attachment or 'videos' in attachment: images = attachment.get('images') videos = attachment.get('videos') if not (images or videos): attachment_error = {'': _("Should have at least one item in 'images' or 'videos'")} # Todo (mo): passing `partial=False` should take care of videos validations if videos: for v in videos: vs = VideoSerializer(data=v) if not vs.is_valid(): attachment_error = {'videos': unicode(vs.errors)} errors['attachments'].insert(i, attachment_error or None) i += 1 if not any(errors['attachments']): del errors['attachments'] if text is not None and text == "" and attachments is None: errors['text'] = _("Can not be empty") # Todo: Raise errors directly that is fine if errors: raise serializers.ValidationError(errors) return validated_data
def to_internal_value(self, data): validated_data = super(ReportSerializer, self).to_internal_value(data) attached_object = validated_data['attached_object'] if not any_in(['attached_profile', 'attached_shout', 'attached_conversation'], attached_object): error_tuple = (_("Should have either 'profile', 'shout' or 'conversation'"), ERROR_REASON.REQUIRED) raise serializers.ValidationError({'attached_object': error_tuple}) if 'attached_profile' in attached_object: validated_data['type'] = REPORT_TYPE_PROFILE elif 'attached_shout' in attached_object: validated_data['type'] = REPORT_TYPE_SHOUT elif 'attached_conversation' in attached_object: validated_data['type'] = REPORT_TYPE_CONVERSATION return validated_data
def save_message_attachments(message, attachments): conversation = message.conversation for attachment in attachments: # todo: map the content types to models if MESSAGE_ATTACHMENT_TYPE_SHOUT.text in attachment: object_id = attachment[MESSAGE_ATTACHMENT_TYPE_SHOUT.text]['id'] content_type = ContentType.objects.get_for_model(Shout) ma_type = MESSAGE_ATTACHMENT_TYPE_SHOUT MessageAttachment.create(message_id=message.id, conversation_id=conversation.id, content_type=content_type, object_id=object_id, type=ma_type).save() if MESSAGE_ATTACHMENT_TYPE_PROFILE.text in attachment: object_id = attachment[MESSAGE_ATTACHMENT_TYPE_PROFILE.text]['id'] content_type = ContentType.objects.get_for_model(User) ma_type = MESSAGE_ATTACHMENT_TYPE_PROFILE MessageAttachment.create(message_id=message.id, conversation_id=conversation.id, content_type=content_type, object_id=object_id, type=ma_type).save() if MESSAGE_ATTACHMENT_TYPE_LOCATION.text in attachment: location = attachment['location'] sl = SharedLocation(latitude=location['latitude'], longitude=location['longitude']) sl.save() object_id = sl.id content_type = ContentType.objects.get_for_model(SharedLocation) ma_type = MESSAGE_ATTACHMENT_TYPE_LOCATION MessageAttachment.create(message=message, conversation=conversation, content_type=content_type, object_id=object_id, type=ma_type) if any_in(['images', 'videos'], attachment): ma_type = MESSAGE_ATTACHMENT_TYPE_MEDIA images = attachment.get('images', []) or [] videos = attachment.get('videos', []) or [] ma = MessageAttachment.create(type=ma_type, message=message, conversation=conversation, images=images) for v in videos: # todo: better handling try: video = Video.create(url=v['url'], thumbnail_url=v['thumbnail_url'], provider=v['provider'], id_on_provider=v['id_on_provider'], duration=v['duration']) ma.videos.add(video) except Exception: error_logger.warn("Error creating video", exc_info=True)
def list(self, request, *args, **kwargs): """ List shouts. [Shouts Pagination](https://github.com/shoutit/shoutit-api/wiki/Searching-Shouts#pagination) ###Response <pre><code> { "count": 59, // number of results "next": null, // next results page url "previous": null, // previous results page url "results": [] // list of {ShoutSerializer} "related_searches": [] // list of keywords related to the current search [currently dummy text is being returned] } </code></pre> --- serializer: ShoutSerializer parameters: - name: search description: space or comma separated keywords to search in title, text, tags paramType: query - name: shout_type paramType: query defaultValue: all enum: - request - offer - all - name: country paramType: query - name: city paramType: query - name: state paramType: query - name: category description: the category slug paramType: query - name: tags description: space or comma separated tags. returned shouts will contain ALL of them. passing single tag is also possible to list its shouts paramType: query - name: discover description: discover item id to list its shouts paramType: query - name: profile description: profile username to list its shouts paramType: query - name: min_price paramType: query - name: max_price paramType: query - name: down_left_lat description: -90 to 90, can not be greater than up_right_lat paramType: query - name: down_left_lng description: -180 to 180, can not be greater than up_right_lng paramType: query - name: up_right_lat description: -90 to 90 paramType: query - name: up_right_lng description: -180 to 180 paramType: query """ shouts = self.filter_queryset(self.get_index_search()) page = self.paginate_queryset(shouts) serializer = self.get_serializer(page, many=True) result = self.get_paginated_response(serializer.data) # Todo: add actual data result.data['web_url'] = settings.SITE_LINK + 'search?src=api' result.data['related_searches'] = [] # Track, skip when requests shouts of a Profile, Tag or Discover search_data = getattr(shouts, 'search_data', {}) if not any_in(['profile', 'tag', 'discover'], search_data.keys()): search_data.update({ 'num_results': result.data.get('count'), 'api_client': getattr(request, 'api_client', None), 'api_version': request.version, }) event_name = 'search' if 'search' in search_data else 'browse' mixpanel_controller.track(request.user.pk, event_name, search_data) return result
def list(self, request, *args, **kwargs): """ List shouts. [Shouts Pagination](https://docs.google.com/document/d/1Zp9Ks3OwBQbgaDRqaULfMDHB-eg9as6_wHyvrAWa8u0/edit#heading=h.97r3lxfv95pj) ###Response <pre><code> { "next": null, // next results page url "previous": null, // previous results page url "results": [] // list of {ShoutSerializer} "related_searches": [] // list of keywords related to the current search [currently dummy text is being returned] } </code></pre> --- serializer: ShoutSerializer parameters: - name: search description: space or comma separated keywords to search in title, text, tags paramType: query - name: shout_type paramType: query defaultValue: all enum: - request - offer - all - name: country paramType: query - name: city paramType: query - name: min_price type: float paramType: query type: float - name: max_price paramType: query - name: down_left_lat description: -90 to 90, can not be greater than up_right_lat type: float paramType: query - name: down_left_lng description: -180 to 180, can not be greater than up_right_lng type: float paramType: query - name: up_right_lat description: -90 to 90 type: float paramType: query - name: up_right_lng description: -180 to 180 type: float paramType: query - name: category description: the category name paramType: query - name: tags description: space or comma separated tags. returned shouts will contain ALL of them. passing single tag is also possible to list its shouts paramType: query - name: discover description: discover item id to list its shouts paramType: query - name: user description: user username to list his shouts paramType: query """ shouts = self.filter_queryset(self.get_index_search()) page = self.paginate_queryset(shouts) serializer = self.get_serializer(page, many=True) result = self.get_paginated_response(serializer.data) result.data['related_searches'] = [ 'HP', 'Laptops', 'Lenovo', 'Macbook Pro' ] # Track, skip when requests shouts of a Profile, Tag or Discover search_data = getattr(shouts, 'search_data', {}) if not any_in(['user', 'tag', 'discover'], search_data.keys()): search_data.update({ 'num_results': result.data.get('count'), 'api_client': getattr(request, 'api_client', None), 'api_version': request.version, }) event_name = 'search' if 'search' in search_data else 'browse' mixpanel_controller.track(request.user.pk, event_name, search_data) return result