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)
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
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()
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)
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)
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)
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
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)