Esempio n. 1
0
    def video_identity(self, request):
        """
        Retrieve video client identity to make a call
        <pre><code>
        GET: /twilio/video_identity?profile=username
        </code></pre>

        ###REQUIRES AUTH

        ###Response
        <pre><code>
        {
          "identity": "7c6ca4737db3447f936037374473e61f"
        }
        </code></pre>

        Returns 403 if the call is not allowed.
        ---
        omit_serializer: true
        parameters:
            - name: profile
              description: Profile username
              paramType: query
        """
        # Todo: Check whether calling user is allowed to do this call or not
        # Todo: Move the logic to Serializer

        other_username = request.query_params.get('profile')
        if not other_username:
            raise RequiredParameter('profile')
        if other_username == request.user.username:
            raise InvalidParameter('profile', "You can't call your self")

        try:
            other_user = User.objects.get(username=other_username)
        except User.DoesNotExist:
            msg = _("Profile with username '%(username)s' does not exist") % {
                'username': other_username
            }
            raise InvalidParameter('profile', message=msg)

        if hasattr(other_user, 'video_client'):
            video_client = other_user.video_client
        else:
            # Create video client for the other user
            try:
                video_client = create_video_client(other_user)
            except (ValidationError, IntegrityError) as e:
                msg = _("Error calling %(name)s") % {
                    'name': other_username.name
                }
                raise ShoutitBadRequest(message=msg,
                                        developer_message=unicode(e))

        # Notify the other user
        notifications_controller.notify_user_of_incoming_video_call(
            user=other_user, caller=request.user)

        res = {'identity': video_client.identity}
        return Response(res)
Esempio n. 2
0
    def paginate_queryset(self, queryset, request, view=None):
        """
        Paginate a queryset using unix timestamp query params.
        """
        self.page_size = self.get_page_size(request)

        before_query_param = request.query_params.get(self.before_field)
        after_query_param = request.query_params.get(self.after_field)
        if before_query_param and after_query_param:
            msg = _(
                "Using '%(before)s' and '%(after)s' query params together is not allowed"
            ) % {
                'before': self.before_field,
                'after': self.after_field
            }
            raise InvalidParameter('', msg)

        if before_query_param:
            try:
                filters = {
                    self.datetime_attribute + '__lt':
                    utcfromtimestamp(int(before_query_param) - 1)
                }
                queryset = queryset.filter(
                    **filters).order_by('-' + self.datetime_attribute)

            except (TypeError, ValueError):
                raise InvalidParameter(self.before_field,
                                       _("Should be a valid timestamp"))
        elif after_query_param:
            try:
                filters = {
                    self.datetime_attribute + '__gt':
                    utcfromtimestamp(int(after_query_param) + 1)
                }
                queryset = queryset.filter(**filters).order_by(
                    self.datetime_attribute)
            except (TypeError, ValueError):
                raise InvalidParameter(self.after_field,
                                       _("Should be a valid timestamp"))
        else:
            queryset = queryset.order_by('-' + self.datetime_attribute)

        paginator = DjangoPaginator(queryset, self.page_size)
        page_number = 1
        page = paginator.page(page_number)
        # reverse the messages order inside the page itself if needed, so the results are always sorted from oldest to newest.
        # in both cases the object_list queryset should be converted to a list to maintain consistency.
        if not after_query_param:
            page.object_list = list(page.object_list)[::-1]
        else:
            page.object_list = list(page.object_list)

        # explicitly reverse the order if required
        if self.recent_on_top:
            page.object_list = list(page.object_list)[::-1]

        self.page = page
        self.request = request
        return self.page
Esempio n. 3
0
    def video_call(self, request):
        """
        Send the Profile Push about video call
        ###REQUIRES AUTH
        ###Request
        <pre><code>
        {
          "identity": "7c6ca4737db3447f936037374473e61f",
          "missed": true
        }
        </code></pre>

        ---
        """
        # Todo: Move the logic to Serializer
        data = request.data
        identity = data.get('identity')
        if not identity:
            raise RequiredBody('identity')
        try:
            video_client = VideoClient.objects.get(id=identity)
        except VideoClient.DoesNotExist:
            msg = _("Profile with identity %(identity)s does not exist") % {
                'identity': identity
            }
            raise InvalidParameter('identity', message=msg)
        except ValueError:
            msg = _("Invalid identity")
            raise InvalidParameter('identity', message=msg)
        other_user = video_client.user

        missed = data.get('missed', False)

        if missed:
            # Notify the other user about the missed video call
            notifications_controller.notify_user_of_missed_video_call(
                user=other_user, caller=request.user)
        else:
            # Notify the other user about the incoming video call
            notifications_controller.notify_user_of_incoming_video_call(
                user=other_user, caller=request.user)

        # Track
        event_name = NOTIFICATION_TYPE_MISSED_VIDEO_CALL if missed else NOTIFICATION_TYPE_INCOMING_VIDEO_CALL
        caller = request.user
        track_properties = {
            'mp_country_code': caller.ap.country,
            '$region': caller.ap.state,
            '$city': caller.ap.city,
            'api_client': getattr(request, 'api_client', None),
            'called_profile': other_user.pk
        }
        mixpanel_controller.track(caller.pk, str(event_name), track_properties)

        return Response()
Esempio n. 4
0
    def profile(self, request):
        """
        Retrieve profile using its video client identity
        <pre><code>
        GET: /twilio/profile?identity=7c6ca4737db3447f936037374473e61f
        </code></pre>

        ###REQUIRES AUTH

        ###Response
        Profile Object

        ---
        serializer: ProfileSerializer
        parameters:
            - name: identity
              description: Video client identity
              paramType: query
        """
        # Todo: Move the logic to Serializer
        identity = request.query_params.get('identity')
        if not identity:
            raise RequiredParameter('identity')

        try:
            video_client = VideoClient.objects.get(id=identity)
        except VideoClient.DoesNotExist:
            msg = _("Profile with identity %(identity)s does not exist") % {
                'identity': identity
            }
            raise InvalidParameter('identity', message=msg)
        except ValueError:
            msg = _("Invalid identity")
            raise InvalidParameter('identity', message=msg)

        res = ProfileSerializer(video_client.user,
                                context={
                                    'request': request
                                }).data
        return Response(res)
Esempio n. 5
0
    def autocomplete(self, request):
        """
        List autocomplete terms that can be used for search suggestions (to be improved)

        `search` is required while `category` and `country` are optional.
        ###Response
        <pre><code>
        [
          {
            "term": "bmw-z4"
          },
          {
            "term": "bmw"
          },
        ]
        </code></pre>
        ---
        omit_serializer: true
        parameters:
            - name: search
              description: At least two characters
              paramType: query
            - name: category
              description: Slug for the Category to return terms within it
              paramType: query
            - name: country
              description: Code for the Country to return terms used in it
              paramType: query
        """
        # Todo: improve!
        search = request.query_params.get('search', '').strip()
        if not search:
            raise RequiredParameter('search', _("This parameter is required"))
        # category = request.query_params.get('category')
        # country = request.query_params.get('country')
        if len(search) >= 2:
            terms = list(
                Tag.objects.filter(name__istartswith=search).values_list(
                    'name', flat=True)[:10])
            random.shuffle(terms)
            terms = map(lambda t: {'term': strings.human_case(t)}, terms)
        else:
            raise InvalidParameter('search',
                                   _("At least two characters are required"))
        return Response(terms)
Esempio n. 6
0
    def geocode(self, request):
        """
        Retrieve full location attributes for `latlng` query param

        ###Example

        ```
        GET: /misc/geocode?latlng=40.722100,-74.046900
        ```
        """
        latlng = request.query_params.get('latlng')
        if not latlng:
            raise RequiredParameter('latlng')
        try:
            lat = float(latlng.split(',')[0])
            lng = float(latlng.split(',')[1])
        except Exception:
            raise InvalidParameter('latlng', _('Invalid `latlng`'))
        ip = get_real_ip(request)
        location = location_controller.from_location_index(lat, lng, ip)
        return Response(location)
Esempio n. 7
0
    def filter_queryset(self,
                        request,
                        index_queryset,
                        view,
                        extra_query_params=None):
        if not isinstance(index_queryset, Search):
            return index_queryset

        # Copy the query dict to be able to modify it as it is immutable, then update it with extra params
        data = request.query_params.copy()
        if isinstance(extra_query_params, dict):
            data.update(extra_query_params)

        # Update data from discover item shouts query if discover is passed
        discover = data.get('discover')
        if discover:
            try:
                discover_item = DiscoverItem.objects.get(id=discover)
            except ValueError:
                raise InvalidParameter('discover', _("Invalid discover id"))
            except DiscoverItem.DoesNotExist:
                msg = _(
                    "Discover Item with id '%(discover)s' does not exist") % {
                        'discover': discover
                    }
                raise InvalidParameter('discover', msg)
            else:
                data.update(discover_item.shouts_query)

        # Filter shouts by user id if user username is passed in `profile` query param
        user = data.get('profile') or data.get('user')
        if user:
            # Replace `me` with logged in username
            if user == 'me' and request.user.is_authenticated():
                user = request.user.username

            # Get the user id using username
            try:
                user_id = str(
                    User.objects.values('pk').get(username=user)['pk'])
            except User.DoesNotExist:
                msg = _(
                    "Profile with username '%(username)s' does not exist") % {
                        'username': user
                    }
                raise InvalidParameter('profile', msg)
            else:
                index_queryset = index_queryset.filter('term', uid=user_id)

            # When listing user's own shouts show him the expired ones
            if user == request.user.username:
                setattr(view, 'get_expired', True)

        # Exclude shouts using their ids
        exclude = data.get('exclude')
        if isinstance(exclude, basestring):
            exclude = exclude.split(',')
        if exclude and not isinstance(exclude, list):
            exclude = [exclude]
        if exclude:
            index_queryset = index_queryset.filter(
                ~EQ('terms', _id=map(str, exclude)))

        # Shout type
        shout_type = data.get('shout_type')
        if shout_type:
            if shout_type not in ['all', 'offer', 'request']:
                msg = _("Should be `all`, `request` or `offer`")
                raise InvalidParameter('shout_type', msg)
            if shout_type != 'all':
                index_queryset = index_queryset.filter('term', type=shout_type)

        # Search query
        search = data.get('search')
        if search:
            index_queryset = index_queryset.query(
                'multi_match',
                query=search,
                fields=['title', 'text', 'tags'],
                fuzziness='AUTO')

        # Tags
        tags = data.get('tags')
        if tags:
            tags = tags.replace(',', ' ').split()
            tag_names = process_tags(tags)
            index_queryset = index_queryset.filter('terms', tags=tag_names)

        # Location: Country, State, City, Latitude, Longitude
        country = data.get('country', '').upper()
        if country and country != 'all':
            index_queryset = index_queryset.filter('term', country=country)
            # todo: add state
            city = data.get('city')
            if city and city != 'all':
                # todo: use other means of finding the surrounding cities like state.
                try:
                    pd_city = PredefinedCity.objects.filter(city=city,
                                                            country=country)[0]
                except IndexError:
                    pass
                else:
                    nearby_cities = pd_city.get_cities_within(
                        settings.NEARBY_CITIES_RADIUS_KM)
                    cities = map(lambda nc: nc.city, nearby_cities)
                    cities.append(city)
                    cities = arrays.unique(cities)
                    index_queryset = index_queryset.filter('terms',
                                                           city=cities)

        down_left_lat = data.get('down_left_lat')
        down_left_lng = data.get('down_left_lng')
        up_right_lat = data.get('up_right_lat')
        up_right_lng = data.get('up_right_lng')
        latlng_key = ''
        try:
            if down_left_lat:
                latlng_key = 'down_left_lat'
                down_left_lat = float(down_left_lat)
                up_right_lat = up_right_lat or 90
                if down_left_lat > float(up_right_lat) or not (
                        90 >= down_left_lat >= -90):
                    raise InvalidParameter(
                        'down_left_lat',
                        _("Should be between -90 and 90, also not greater than 'up_right_lat'"
                          ))
                index_queryset = index_queryset.filter(
                    'range', **{'latitude': {
                        'gte': down_left_lat
                    }})
            if down_left_lng:
                latlng_key = 'down_left_lng'
                down_left_lng = float(down_left_lng)
                up_right_lng = up_right_lng or 180
                if down_left_lng > float(up_right_lng) or not (
                        180 >= down_left_lng >= -180):
                    raise InvalidParameter(
                        'down_left_lng',
                        _("Should be between -180 and 180, also not greater than 'up_right_lng'"
                          ))
                index_queryset = index_queryset.filter(
                    'range', **{'longitude': {
                        'gte': down_left_lng
                    }})
            if up_right_lat:
                latlng_key = 'up_right_lat'
                if not (90 >= float(up_right_lat) >= -90):
                    raise InvalidParameter('up_right_lat',
                                           _("Should be between -90 and 90"))
                index_queryset = index_queryset.filter(
                    'range', **{'latitude': {
                        'lte': up_right_lat
                    }})
            if up_right_lng:
                latlng_key = 'up_right_lng'
                if not (180 >= float(up_right_lng) >= -180):
                    raise InvalidParameter('up_right_lng',
                                           _("Should be between -180 and 180"))
                index_queryset = index_queryset.filter(
                    'range', **{'longitude': {
                        'lte': up_right_lng
                    }})
        except ValueError:
            raise InvalidParameter(latlng_key, _("Invalid number"))

        # Category and Filters
        category = data.get('category')
        if category and category != 'all':
            try:
                category = Category.objects.prefetch_related('filters').get(
                    slug=category)
            except Category.DoesNotExist:
                msg = _("Category with slug '%(slug)s' does not exist") % {
                    'slug': category
                }
                raise InvalidParameter('category', msg)
            else:
                data['category'] = category.slug
                index_queryset = index_queryset.filter(
                    'terms', category=[category.name, category.slug])
                cat_filters = category.filters.values_list(
                    'slug', 'values_type')
                for cat_f_slug, cat_f_type in cat_filters:
                    if cat_f_type == TAG_TYPE_STR:
                        cat_f_param = data.get(cat_f_slug)
                        if cat_f_param:
                            cat_f_params = cat_f_param.split(',')
                            index_queryset = index_queryset.filter(
                                'terms',
                                **{'filters__%s' % cat_f_slug: cat_f_params})
                    elif cat_f_type == TAG_TYPE_INT:
                        for m1, m2 in [('min', 'gte'), ('max', 'lte')]:
                            cat_f_param = data.get('%s_%s' % (m1, cat_f_slug))
                            if cat_f_param:
                                index_queryset = index_queryset.filter(
                                    'range', **{
                                        'filters__%s' % cat_f_slug: {
                                            m2: cat_f_param
                                        }
                                    })

        # Price
        min_price = data.get('min_price')
        if min_price:
            index_queryset = index_queryset.filter(
                'range', **{'price': {
                    'gte': min_price
                }})

        max_price = data.get('max_price')
        if max_price:
            index_queryset = index_queryset.filter(
                'range', **{'price': {
                    'lte': max_price
                }})

        # Expired
        if not getattr(view, 'get_expired', False):
            now = timezone.now()
            min_published = now - timedelta(days=int(settings.MAX_EXPIRY_DAYS))

            # Recently published and no specified expires_at
            recently_published = EQ('range',
                                    **{'published_at': {
                                        'gte': min_published
                                    }})
            no_expiry_still_valid = EQ(
                'bool',
                filter=[~EQ('exists', field='expires_at'), recently_published])

            # Not expired
            not_expired = EQ('range', **{'expires_at': {'gte': now}})
            expiry_still_valid = EQ(
                'bool', filter=[EQ('exists', field='expires_at'), not_expired])

            index_queryset = index_queryset.filter(no_expiry_still_valid
                                                   | expiry_still_valid)

        # Sorting
        sort = data.get('sort')
        sort_types = {
            None: ('-published_at', ),
            'time': ('-published_at', ),
            'price_asc': ('price', ),
            'price_desc': ('-price', ),
        }
        if sort and sort not in sort_types:
            raise InvalidParameter('sort', _("Invalid sort"))
        # selected_sort = ('-priority',) + sort_types[sort]
        selected_sort = sort_types[sort]
        if search:
            selected_sort = ('_score', ) + selected_sort
        index_queryset = index_queryset.sort(*selected_sort)

        debug_logger.debug(index_queryset.to_dict())
        index_queryset.search_data = {
            k: parse_int(v, 10) or v
            for k, v in data.items()
        }
        return index_queryset
Esempio n. 8
0
    def suggestions(self, request):
        """
        Get suggestions for Users, Pages, Tags and Shouts. `type` query param can be passed to limit the returned fields.

        ###Request
        ```
        GET: /misc/suggestions?type=users,pages,tags,shouts,shout&country=AE&state=Dubai&city=Dubai&page_size=5
        ```

        ###Response
        <pre><code>
        {
            "users": [],
            "pages": [],
            "tags": [],
            "shouts": [],
            "shout": {}
        }
        </code></pre>

        ---
        omit_serializer: true
        omit_parameters:
            - form
        """
        data = request.query_params
        try:
            page_size = int(data.get('page_size', 5))
        except ValueError:
            raise InvalidParameter('page_size', "Invalid `page_size`")
        type_qp = data.get('type', 'users,pages,tags,shouts,shout')
        country = data.get('country', '').upper()
        try:
            types = type_qp.split(',')
        except:
            raise InvalidParameter('type', _("Invalid `type`"))

        suggestions = OrderedDict()

        if 'users' in types:
            users_qs = User.objects.filter(
                type=USER_TYPE_PROFILE,
                is_activated=True).order_by('-date_joined')
            if request.user.is_authenticated():
                users_qs = users_qs.exclude(id=request.user.id)
            if country:
                users_qs = users_qs.filter(profile__country=country)
            users_qs = users_qs.select_related('profile')
            users = ProfileSerializer(users_qs[:page_size],
                                      many=True,
                                      context={
                                          'request': request
                                      }).data
            suggestions['users'] = users
        if 'pages' in types:
            pages_qs = User.objects.filter(
                type=USER_TYPE_PAGE).order_by('-date_joined')
            if request.user.is_authenticated():
                pages_qs = pages_qs.exclude(id=request.user.id)
            if country:
                pages_qs = pages_qs.filter(page__country=country)
            pages_qs = pages_qs.select_related('page')
            pages = ProfileSerializer(pages_qs[:page_size],
                                      many=True,
                                      context={
                                          'request': request
                                      }).data
            suggestions['pages'] = pages
        if 'tags' in types:
            tag_slugs = list(Category.objects.values_list('slug', flat=True))
            random.shuffle(tag_slugs)
            tags_qs = Tag.objects.filter(slug__in=tag_slugs[:page_size])
            tags = TagDetailSerializer(tags_qs,
                                       many=True,
                                       context={
                                           'request': request
                                       }).data
            suggestions['tags'] = tags
        if 'shouts' in types or 'shout' in types:
            shouts_qs = Shout.objects.get_valid_shouts(
                country=country).order_by('-published_at')
            if 'shouts' in types:
                shouts = ShoutSerializer(shouts_qs[:page_size],
                                         many=True,
                                         context={
                                             'request': request
                                         }).data
                suggestions['shouts'] = shouts
            if 'shout' in types:
                shout = shouts_qs.first()
                if shout:
                    shout = ShoutSerializer(shout,
                                            context={
                                                'request': request
                                            }).data
                suggestions['shout'] = shout
        return Response(suggestions)