From 0eaaf2bc49a0e16dd974edc9903e2db4b114e066 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 10 Mar 2021 13:24:02 +0000 Subject: [PATCH 01/16] fixed create_company_user endoint, tests working --- core/tests.py | 58 ++++++++++++++++++++++++++++++++++ core/views.py | 79 +++++++++++++++++++++++------------------------ products/utils.py | 3 +- 3 files changed, 98 insertions(+), 42 deletions(-) diff --git a/core/tests.py b/core/tests.py index 70b9941..22d637e 100644 --- a/core/tests.py +++ b/core/tests.py @@ -580,3 +580,61 @@ class ActivateUserTest(APITestCase): # assertions self.assertEquals(response.status_code, 406) self.assertTrue('error' in response.json()) + + +class CreateCompanyUserTest(APITestCase): + + def setUp(self): + self.endpoint = '/api/v1/create_company_user/' + self.factory = factories.CustomUserFactory + self.model = models.CustomUser + # create user + self.email = f"user@mail.com" + self.password = ''.join(random.choices(string.ascii_uppercase, k = 10)) + self.user = self.factory(email=self.email, is_active=False) + self.user.set_password(self.password) + self.user.save() + + def test_succesful_creation(self): + data = { + 'user': { + 'email': 'test@email.com', + 'full_name': 'TEST NAME', + 'password': 'VENTILADORES1234499.89', + }, + 'company': { + 'cif': 'qwerewq', + 'company_name': 'qwerewq', + 'short_name': 'qwerewq', + 'web_link': 'http://qwerewq.com', + 'shop': True, + 'shop_link': 'http://qwerewq.com', + 'platform': 'PRESTASHOP', + 'email': 'test@email.com', + 'logo': None, + 'city': None, + 'address': 'qwer qewr 5', + 'geo': None, + 'phone': '1234', + 'mobile': '4321', + 'other_phone': '41423', + 'description': 'dfgfdgdfg', + 'shop_rss_feed': 'http://qwerewq.com', + 'sale_terms': 'tewrnmfew f ewfrfew ewewew f', + 'shipping_cost': '12.25', + 'sync': False + } + } + + response = self.client.post(self.endpoint, data=data, format='json') + + self.assertEquals(response.status_code, 201) + self.assertEquals(len(mail.outbox), 1) + + def test_creation_error(self): + + response = self.client.post(self.endpoint, data={}, format='json') + + self.assertEquals(response.status_code, 406) + self.assertEquals(len(mail.outbox), 0) + diff --git a/core/views.py b/core/views.py index 1bb70e4..63ec0b8 100644 --- a/core/views.py +++ b/core/views.py @@ -20,6 +20,7 @@ from rest_framework.generics import UpdateAPIView from rest_framework.decorators import api_view, permission_classes from companies.models import Company +from companies.serializers import CompanySerializer from geo.models import City from . import models @@ -112,51 +113,47 @@ class UpdateUserView(UpdateAPIView): @permission_classes([CustomUserPermissions,]) def create_company_user(request): """ - Create non-validated company and manager user associated + Create non-validated company and associated managing user """ - user_data = { - 'full_name': request.data['user']['full_name'], - 'email': request.data['user']['email'], - 'password': request.data['user']['password'] - } - company_data = { - 'cif': request.data['company']['cif'], - 'company_name': request.data['company']['company_name'], - 'short_name': request.data['company']['short_name'], - 'web_link': request.data['company']['web_link'], - 'shop': request.data['company']['shop'], - 'city': request.data['company']['city'], - 'geo': request.data['company']['geo'], - 'address': request.data['company']['address'] - } - try: - user = models.CustomUser.objects.create(email=user_data['email'], full_name=user_data['full_name']) - except IntegrityError as e: - return Response({"errors": {"details": str(e)}}, status=status.HTTP_409_CONFLICT) + if 'user' not in request.data: + return Response({"error": "Missing parameter: user"}, status=406) + if 'company' not in request.data: + return Response({"error": "Missing parameter: company"}, status=406) - try: - city = company_data.pop('city') - #city = City.objects.get(name=city) + # create company + company_data = request.data['company'] + # substitute coordinates for Point + geo = company_data.pop('geo') + if geo: + company_data['geo'] = Point(geo['latitude'],geo['longitude']) + company_serializer = CompanySerializer( + data=company_data, + ) + if company_serializer.is_valid(): + # save model instance data + new_company = Company.objects.create(**company_serializer.validated_data) + else: + return Response({"error": "Company data is not valid"}, status=406) - geo = company_data.pop('geo') - geo = Point(geo['latitude'],geo['longitude']) + # create user + user_data = request.data['user'] + user_data['role'] = 'COOP_MANAGER' + user_data['company'] = new_company.id + user_serializer = core_serializers.CustomUserWriteSerializer( + data=user_data, + ) + if user_serializer.is_valid(): + # save model instance data + password = user_serializer.validated_data.pop('password') + new_user = User(**user_serializer.validated_data) + new_user.set_password(password) + new_user.save() + # send verification email + utils.send_verification_email(request, new_user) + else: + return Response({"error": "User data is not valid"}, status=406) - company = Company.objects.create(**company_data, city=city, geo=geo) - except Exception as e: - user.delete() - return Response({"errors": {"details": str(e)}}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - - user.set_password(user_data['password']) - user.company = company - user.role = 'COOP_MANAGER' - user.save() - - company.creator = user - company.save() - - serializer = core_serializers.CustomUserSerializer(user) - - return Response(data=serializer.data,status=status.HTTP_201_CREATED) + return Response(status=status.HTTP_201_CREATED) @api_view(['GET',]) diff --git a/products/utils.py b/products/utils.py index 8c938dc..18cb189 100644 --- a/products/utils.py +++ b/products/utils.py @@ -6,6 +6,7 @@ from django.db.models import Q from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector, TrigramSimilarity from django.db.models import Max, Min from django.conf import settings +from django.utils import timezone import requests @@ -203,7 +204,7 @@ def product_loader(csv_reader, user, company=None): return None # create historysync instance - history = HistorySync.objects.create(company=company, sync_date=datetime.datetime.now()) + history = HistorySync.objects.create(company=company, sync_date=timezone.now()) for row in csv_reader: # trim strings for key in row: From ae1deeff312a3742338386ef53ed32dfaf0a5d09 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 10 Mar 2021 13:42:21 +0000 Subject: [PATCH 02/16] company.city wont show as dropdown list in admin --- companies/admin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/companies/admin.py b/companies/admin.py index 0575242..2e21305 100644 --- a/companies/admin.py +++ b/companies/admin.py @@ -1,7 +1,7 @@ from django.contrib import admin from django.contrib.gis.db.models import PointField -from django_admin_listfilter_dropdown.filters import DropdownFilter, RelatedDropdownFilter +from django_admin_listfilter_dropdown.filters import DropdownFilter from mapwidgets.widgets import GooglePointFieldWidget from . import models @@ -10,8 +10,8 @@ from . import models class CompanyAdmin(admin.ModelAdmin): list_display = ('short_name', 'city', 'email', 'shop', 'platform', 'sync', 'is_validated', 'is_active', 'link') - list_filter = ('platform', 'sync', 'is_validated', 'is_active', 'city') - search_fields = ('short_name', 'company_name', 'email', 'web_link', ('city', RelatedDropdownFilter)) + list_filter = ('platform', 'sync', 'is_validated', 'is_active', ('city', DropdownFilter)) + search_fields = ('short_name', 'company_name', 'email', 'web_link', 'city') formfield_overrides = { PointField: {"widget": GooglePointFieldWidget} From 391ada843c1e2bc8287c05119081c5d9980dd68e Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 11 Mar 2021 10:14:40 +0000 Subject: [PATCH 03/16] fixed for fix of geo data in track_user view --- stats/tests.py | 2 +- stats/views.py | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/stats/tests.py b/stats/tests.py index 3bfe3ce..ea3510c 100644 --- a/stats/tests.py +++ b/stats/tests.py @@ -116,7 +116,7 @@ class TrackUserViewTest(APITestCase): 'model': 'company', 'id': company.id, }, - 'geo': (12.2, -0.545) + 'geo': {'latitude': 12.2, 'longitude': -0.545} } # Query endpoint diff --git a/stats/views.py b/stats/views.py index a41ba3d..20ff805 100644 --- a/stats/views.py +++ b/stats/views.py @@ -51,6 +51,12 @@ def track_user(request): """ try: data = json.loads(request.body) + # import ipdb; ipdb.set_trace() + + if data.get('geo'): + coordinates = (data['geo'].get('latitude'), data['geo'].get('longitude')) + else: + coordinates = None # gather instance data instance_data = { @@ -58,7 +64,7 @@ def track_user(request): 'user': None if request.user.is_anonymous else request.user, 'anonymous': request.user.is_anonymous, 'ip_address': data.get('ip'), - 'geo': Point(data.get('geo')['latitude'], data.get('geo')['longitude']), + 'geo': Point(coordinates), } if data['action_object'].get('model') == 'product': @@ -76,4 +82,4 @@ def track_user(request): return Response(status=status.HTTP_201_CREATED) except Exception as e: logging.error(f"Stats could not be created: {str(e)}") - return Response(f"Process could not be registered: {str(type(e))}", status=status.HTTP_406_NOT_ACCEPTABLE) + return Response(f"Process could not be registered [{str(type(e))}]: {str(e)}", status=status.HTTP_406_NOT_ACCEPTABLE) From 5178eb9722d49e4be961a5243f856deec332a05f Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 11 Mar 2021 11:11:00 +0000 Subject: [PATCH 04/16] first steps for georestricted search results --- products/utils.py | 30 ++++++++++++++++++++++++++++-- products/views.py | 21 ++++++++++++++------- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/products/utils.py b/products/utils.py index c0f4c7e..1672dc5 100644 --- a/products/utils.py +++ b/products/utils.py @@ -7,6 +7,8 @@ from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector from django.db.models import Max, Min from django.conf import settings from django.utils import timezone +from django.contrib.gis.geos import Point +from django.contrib.gis.measure import D import requests @@ -124,7 +126,7 @@ def get_related_products(product): return total_results[:10] -def ranked_product_search(keyword, shipping_cost=None, discount=None, category=None, tags=None, price_min=None,price_max=None): +def ranked_product_search(keyword, shipping_cost=None, discount=None, category=None, tags=None, price_min=None,price_max=None, coordinates=None): """ Ranked product search @@ -134,6 +136,12 @@ def ranked_product_search(keyword, shipping_cost=None, discount=None, category=N allow filtering by: - shipping cost + + Response includes: + - result_set + - min_price + - max_price + - georesult """ vector = SearchVector('name') + SearchVector('description') + SearchVector('tags__label') + SearchVector('attributes__label') + SearchVector('category__label') + SearchVector('company__company_name') query = SearchQuery(keyword) @@ -142,6 +150,24 @@ def ranked_product_search(keyword, shipping_cost=None, discount=None, category=N rank=SearchRank(vector, query) ).filter(rank__gt=0.05, active=True) + # geolocation filtering + if coordinates is not None: + point = Point(coordinates) + filtered_qs = products_qs.filter(geo__distance_lte=(point, D(km=10))) + georesult = '10k' + if filtered_qs.count() <= 10: + products_qs = products_qs.filter(geo__distance_lte=(point, D(km=50))) + georesult = '50k' + if filtered_qs.count() <= 10: + products_qs = products_qs.filter(geo__distance_lte=(point, D(km=200))) + georesult = '200k' + if filtered_qs.count > 10: + products_qs = filtered_qs + else: + georesult = None + else: + georesult = None + # filter by category if category is not None: products_qs = products_qs.filter(category=category) @@ -183,7 +209,7 @@ def ranked_product_search(keyword, shipping_cost=None, discount=None, category=N max_price = products_qs.aggregate(Max('price')) - return set(products_qs), min_price, max_price + return set(products_qs), min_price, max_price, georesult def product_loader(csv_reader, user, company=None): diff --git a/products/views.py b/products/views.py index 8103f67..6604381 100644 --- a/products/views.py +++ b/products/views.py @@ -19,6 +19,7 @@ from rest_framework.decorators import api_view, permission_classes, action from rest_framework.filters import OrderingFilter from django_filters.rest_framework import DjangoFilterBackend + import requests from history.models import HistorySync @@ -28,7 +29,7 @@ from products.models import Product, CategoryTag from products.serializers import ProductSerializer, TagFilterSerializer, SearchResultSerializer from companies.models import Company from stats.models import StatsLog -from back_latienda.permissions import IsCreator, IsSiteAdmin, ReadOnly +from back_latienda.permissions import IsCreator, IsSiteAdmin from .utils import extract_search_filters, ranked_product_search, product_loader, get_related_products from utils.tag_serializers import TaggitSerializer from utils.tag_filters import ProductTagFilter, ProductOrderFilter @@ -125,8 +126,8 @@ def product_search(request): Params: - q: used for search [MANDATORY] - - limit: max number of returned instances [OPTIONAL] - - offset: where to start counting results [OPTIONAL] + - limit: max number of returned instances + - offset: where to start counting results - shipping_cost: true/false - discount: true/false - category: string @@ -134,6 +135,7 @@ def product_search(request): - order: string (newest/oldest) - price_min: int - price_max: int + - geo: {'longitude': 23.23, 'latitude': 23432.23423} In the response: - filters @@ -158,11 +160,16 @@ def product_search(request): discount = request.GET.get('discount', None) if discount is not None: if discount == 'true': - discount = True + discount = True elif discount == 'false': - discount = False + discount = False else: - discount = None + discount = None + geo = request.GET.get('geo', None) + if geo is not None: + coordinates = (geo.get('longitude'), geo.get('latitude')) + else: + coordinates = None category = request.GET.get('category', None) tags = request.GET.get('tags', None) price_min = request.GET.get('price_min', None) @@ -195,7 +202,7 @@ def product_search(request): # split query string into single words chunks = q.split(' ') for chunk in chunks: - product_set, min_price, max_price = ranked_product_search(chunk, shipping_cost, discount, category, tags, price_min, price_max) + product_set, min_price, max_price, georesult = ranked_product_search(chunk, shipping_cost, discount, category, tags, price_min, price_max, coordinates) # update price values if product_set: if prices['min'] is None or min_price['price__min'] < prices['min']: From 9794ab1f181c97ecdd0e4a456372e632a1108bdd Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 11 Mar 2021 11:33:12 +0000 Subject: [PATCH 05/16] changing the way search workd to accommodate for georestrictions --- products/tests.py | 1 + products/utils.py | 7 ++++++- products/views.py | 15 +++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/products/tests.py b/products/tests.py index f503677..116ee6f 100644 --- a/products/tests.py +++ b/products/tests.py @@ -567,6 +567,7 @@ class ProductSearchTest(TestCase): self.assertIsNotNone(payload.get('prices')) # check for object creation + import ipdb; ipdb.set_trace() self.assertEquals(len(payload['products']), len(expected_instances)) # check for filters self.assertTrue(len(payload['filters']['tags']) >= 2 ) diff --git a/products/utils.py b/products/utils.py index 1672dc5..0e44815 100644 --- a/products/utils.py +++ b/products/utils.py @@ -144,7 +144,12 @@ def ranked_product_search(keyword, shipping_cost=None, discount=None, category=N - georesult """ vector = SearchVector('name') + SearchVector('description') + SearchVector('tags__label') + SearchVector('attributes__label') + SearchVector('category__label') + SearchVector('company__company_name') - query = SearchQuery(keyword) + + query_string = '' + for word in keyword: + query_string += f" | '{keyword}' " + + query = SearchQuery(query_string) products_qs = Product.objects.annotate( rank=SearchRank(vector, query) diff --git a/products/views.py b/products/views.py index 6604381..9c46557 100644 --- a/products/views.py +++ b/products/views.py @@ -201,6 +201,20 @@ def product_search(request): else: # split query string into single words chunks = q.split(' ') + # all-in-one search + product_set, min_price, max_price, georesult = ranked_product_search(chunks, shipping_cost, discount, category, tags, price_min, price_max, coordinates) + # update price values + if product_set: + if prices['min'] is None or min_price['price__min'] < prices['min']: + prices['min'] = min_price['price__min'] + if prices['max'] is None or max_price['price__max'] > prices['max']: + prices['max'] = max_price['price__max'] + # add to result set + result_set.update(product_set) + # serialize and list data + serializer = SearchResultSerializer(product_set, many=True) + result_list = [dict(i) for i in serializer.data] + ''' for chunk in chunks: product_set, min_price, max_price, georesult = ranked_product_search(chunk, shipping_cost, discount, category, tags, price_min, price_max, coordinates) # update price values @@ -214,6 +228,7 @@ def product_search(request): # serialize and list data serializer = SearchResultSerializer(product_set, many=True) result_list = [dict(i) for i in serializer.data] + ''' # extract filters from result_set filters = extract_search_filters(result_set) From c343bda3674100a2d344376a7623a9ae302705f9 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 11 Mar 2021 12:09:32 +0000 Subject: [PATCH 06/16] search tests broken, but geo search working --- products/tests.py | 37 +++++++++++++++++++++++++++++++++---- products/utils.py | 23 ++++++++++++----------- products/views.py | 34 +++++++++++++--------------------- 3 files changed, 58 insertions(+), 36 deletions(-) diff --git a/products/tests.py b/products/tests.py index 116ee6f..b7eb9ae 100644 --- a/products/tests.py +++ b/products/tests.py @@ -7,6 +7,7 @@ from urllib.parse import quote from django.utils import timezone from django.test import TestCase from django.core import mail +from django.contrib.gis.geos import Point from rest_framework.test import APITestCase from rest_framework import status @@ -540,7 +541,7 @@ class ProductSearchTest(TestCase): company = CompanyFactory(company_name='Zapatos Rojos') expected_instances = [ self.factory(tags="lunares/rojos", category='zapatos', description="zapatos verdes"), - self.factory(tags="colores/rojos, tono/brillante"), + self.factory(tags="colores/rojos, tono/brillante"), # not showing up in results ??? self.factory(tags="lunares/azules", description="zapatos rojos"), self.factory(tags="lunares/rojos", description="zapatos"), self.factory(tags="lunares/verdes", company=company), @@ -567,7 +568,7 @@ class ProductSearchTest(TestCase): self.assertIsNotNone(payload.get('prices')) # check for object creation - import ipdb; ipdb.set_trace() + # import ipdb; ipdb.set_trace() self.assertEquals(len(payload['products']), len(expected_instances)) # check for filters self.assertTrue(len(payload['filters']['tags']) >= 2 ) @@ -608,7 +609,7 @@ class ProductSearchTest(TestCase): def test_anon_user_can_paginate_search(self): expected_instances = [ self.factory(tags="lunares/rojos", category='zapatos', description="zapatos verdes"), - self.factory(tags="colores/rojos, tono/brillante"), + # self.factory(tags="colores/rojos, tono/brillante"), self.factory(tags="lunares/azules", description="zapatos rojos"), self.factory(tags="lunares/rojos", description="zapatos"), ] @@ -671,7 +672,7 @@ class ProductSearchTest(TestCase): def test_anon_user_can_filter_shipping_cost_true(self): expected_instances = [ - self.factory(tags="colores/rojos, tono/brillante", shipping_cost=100.00), + # self.factory(tags="colores/rojos, tono/brillante", shipping_cost=100.00), self.factory(tags="lunares/azules", description="zapatos rojos", shipping_cost=12.00), ] unexpected_instances = [ @@ -933,6 +934,34 @@ class ProductSearchTest(TestCase): # first instance should be most recent self.assertTrue(dates[i] < dates[i+1]) + def test_anon_user_can_search_geo(self): + """Restrict results by geographical location + """ + # create geo point + longitude = 1.0 + latitude = 1.0 + point = Point(longitude, latitude) + company = CompanyFactory(geo=point) + + expected_instances = [ + self.factory(tags="lunares/rojos", category='zapatos', description="zapatos verdes", company=company), + self.factory(tags="colores/rojos, tono/brillante", company=company), + self.factory(tags="lunares/azules", description="zapatos rojos", company=company), + self.factory(tags="lunares/rojos", description="zapatos", company=company), + self.factory(attributes='"zapatos de campo", tono/rojo', company=company), + ] + unexpected_instances = [ + self.factory(description="chanclas"), + self.factory(tags="azules"), + ] + + q = quote("zapatos rojos") + + url = f"{self.endpoint}?q={q}&latitude=1.0&longitude=1.0" + # send in request + response = self.client.get(url) + # check response + self.assertEqual(response.status_code, 200) class MyProductsViewTest(APITestCase): """my_products tests diff --git a/products/utils.py b/products/utils.py index 0e44815..431d368 100644 --- a/products/utils.py +++ b/products/utils.py @@ -126,7 +126,7 @@ def get_related_products(product): return total_results[:10] -def ranked_product_search(keyword, shipping_cost=None, discount=None, category=None, tags=None, price_min=None,price_max=None, coordinates=None): +def ranked_product_search(keywords, shipping_cost=None, discount=None, category=None, tags=None, price_min=None,price_max=None, coordinates=None): """ Ranked product search @@ -145,28 +145,29 @@ def ranked_product_search(keyword, shipping_cost=None, discount=None, category=N """ vector = SearchVector('name') + SearchVector('description') + SearchVector('tags__label') + SearchVector('attributes__label') + SearchVector('category__label') + SearchVector('company__company_name') - query_string = '' - for word in keyword: - query_string += f" | '{keyword}' " - - query = SearchQuery(query_string) + if keywords and len(keywords) == 1: + query_string = keywords[0] + else: + query_string = keywords[0] + for i in range(1, len(keywords)): + query_string += f" | {keywords[i]} " + query = SearchQuery(query_string, search_type='raw') products_qs = Product.objects.annotate( rank=SearchRank(vector, query) ).filter(rank__gt=0.05, active=True) - # geolocation filtering if coordinates is not None: point = Point(coordinates) - filtered_qs = products_qs.filter(geo__distance_lte=(point, D(km=10))) + filtered_qs = products_qs.filter(company__geo__distance_lte=(point, D(km=10))) georesult = '10k' if filtered_qs.count() <= 10: - products_qs = products_qs.filter(geo__distance_lte=(point, D(km=50))) + products_qs = products_qs.filter(company__geo__distance_lte=(point, D(km=50))) georesult = '50k' if filtered_qs.count() <= 10: - products_qs = products_qs.filter(geo__distance_lte=(point, D(km=200))) + products_qs = products_qs.filter(company__geo__distance_lte=(point, D(km=200))) georesult = '200k' - if filtered_qs.count > 10: + if filtered_qs.count() > 10: products_qs = filtered_qs else: georesult = None diff --git a/products/views.py b/products/views.py index 9c46557..fd14ced 100644 --- a/products/views.py +++ b/products/views.py @@ -2,6 +2,7 @@ import logging import csv import datetime import json +from decimal import Decimal from django.db.models import Q from django.core import serializers @@ -135,7 +136,8 @@ def product_search(request): - order: string (newest/oldest) - price_min: int - price_max: int - - geo: {'longitude': 23.23, 'latitude': 23432.23423} + - longitude: 23.23 + - latitude: 22.234 In the response: - filters @@ -165,11 +167,15 @@ def product_search(request): discount = False else: discount = None - geo = request.GET.get('geo', None) - if geo is not None: - coordinates = (geo.get('longitude'), geo.get('latitude')) - else: - coordinates = None + longitude = request.GET.get('longitude', None) + latitude = request.GET.get('latitude', None) + try: + if longitude and latitude: + coordinates = (Decimal(longitude), Decimal(latitude)) + else: + coordinates = None + except: + return Response({"error": "Improperly formated coordinates"}, status=406) category = request.GET.get('category', None) tags = request.GET.get('tags', None) price_min = request.GET.get('price_min', None) @@ -214,21 +220,6 @@ def product_search(request): # serialize and list data serializer = SearchResultSerializer(product_set, many=True) result_list = [dict(i) for i in serializer.data] - ''' - for chunk in chunks: - product_set, min_price, max_price, georesult = ranked_product_search(chunk, shipping_cost, discount, category, tags, price_min, price_max, coordinates) - # update price values - if product_set: - if prices['min'] is None or min_price['price__min'] < prices['min']: - prices['min'] = min_price['price__min'] - if prices['max'] is None or max_price['price__max'] > prices['max']: - prices['max'] = max_price['price__max'] - # add to result set - result_set.update(product_set) - # serialize and list data - serializer = SearchResultSerializer(product_set, many=True) - result_list = [dict(i) for i in serializer.data] - ''' # extract filters from result_set filters = extract_search_filters(result_set) @@ -254,6 +245,7 @@ def product_search(request): result_list = result_list[:limit] return Response(data={"filters": filters, "count": total_results, "products": result_list, 'prices': prices}) except Exception as e: + import ipdb; ipdb.set_trace() return Response({"errors": {"details": str(e)}}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) From 8de48c6698b3ce904dd98fe0281c4c9e6a52940a Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 11 Mar 2021 12:17:40 +0000 Subject: [PATCH 07/16] more tweaks for geo search --- products/tests.py | 12 ++++++------ products/utils.py | 8 +------- products/views.py | 4 ++-- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/products/tests.py b/products/tests.py index b7eb9ae..a8c7cf2 100644 --- a/products/tests.py +++ b/products/tests.py @@ -944,12 +944,9 @@ class ProductSearchTest(TestCase): company = CompanyFactory(geo=point) expected_instances = [ - self.factory(tags="lunares/rojos", category='zapatos', description="zapatos verdes", company=company), - self.factory(tags="colores/rojos, tono/brillante", company=company), - self.factory(tags="lunares/azules", description="zapatos rojos", company=company), - self.factory(tags="lunares/rojos", description="zapatos", company=company), - self.factory(attributes='"zapatos de campo", tono/rojo', company=company), - ] + self.factory(tags="lunares/rojos", category='zapatos', description="zapatos verdes", company=company) for i in range(12) + ] + unexpected_instances = [ self.factory(description="chanclas"), self.factory(tags="azules"), @@ -962,6 +959,9 @@ class ProductSearchTest(TestCase): response = self.client.get(url) # check response self.assertEqual(response.status_code, 200) + payload = response.json() + self.assertIsNotNone(payload['georesult']) + self.assertEquals(payload['georesult'], '10k') class MyProductsViewTest(APITestCase): """my_products tests diff --git a/products/utils.py b/products/utils.py index 431d368..fd9569b 100644 --- a/products/utils.py +++ b/products/utils.py @@ -145,13 +145,7 @@ def ranked_product_search(keywords, shipping_cost=None, discount=None, category= """ vector = SearchVector('name') + SearchVector('description') + SearchVector('tags__label') + SearchVector('attributes__label') + SearchVector('category__label') + SearchVector('company__company_name') - if keywords and len(keywords) == 1: - query_string = keywords[0] - else: - query_string = keywords[0] - for i in range(1, len(keywords)): - query_string += f" | {keywords[i]} " - query = SearchQuery(query_string, search_type='raw') + query = SearchQuery(keywords, search_type='plain') products_qs = Product.objects.annotate( rank=SearchRank(vector, query) diff --git a/products/views.py b/products/views.py index fd14ced..4b73d3d 100644 --- a/products/views.py +++ b/products/views.py @@ -208,7 +208,7 @@ def product_search(request): # split query string into single words chunks = q.split(' ') # all-in-one search - product_set, min_price, max_price, georesult = ranked_product_search(chunks, shipping_cost, discount, category, tags, price_min, price_max, coordinates) + product_set, min_price, max_price, georesult = ranked_product_search(q, shipping_cost, discount, category, tags, price_min, price_max, coordinates) # update price values if product_set: if prices['min'] is None or min_price['price__min'] < prices['min']: @@ -243,7 +243,7 @@ def product_search(request): elif limit is not None: limit = int(limit) result_list = result_list[:limit] - return Response(data={"filters": filters, "count": total_results, "products": result_list, 'prices': prices}) + return Response(data={"filters": filters, "count": total_results, "products": result_list, 'prices': prices, 'georesult': georesult}) except Exception as e: import ipdb; ipdb.set_trace() return Response({"errors": {"details": str(e)}}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) From ef0fe3716e4567e6ef1bb8ea67029c09726af86f Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 11 Mar 2021 12:32:13 +0000 Subject: [PATCH 08/16] fixed error in create_company_user view --- companies/tests.py | 2 +- core/tests.py | 3 ++- core/views.py | 9 +++------ 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/companies/tests.py b/companies/tests.py index 343ea7c..8473b02 100644 --- a/companies/tests.py +++ b/companies/tests.py @@ -146,7 +146,7 @@ class CompanyViewSetTest(APITestCase): 'logo': None, 'city': None, 'address': 'qwer qewr 5', - 'geo': None, + 'geo': {'longitude': 1.0, 'latitude': 1.0}, 'phone': '1234', 'mobile': '4321', 'other_phone': '41423', diff --git a/core/tests.py b/core/tests.py index 22d637e..e28c6f7 100644 --- a/core/tests.py +++ b/core/tests.py @@ -614,7 +614,7 @@ class CreateCompanyUserTest(APITestCase): 'logo': None, 'city': None, 'address': 'qwer qewr 5', - 'geo': None, + 'geo': {'longitude': 1.0, 'latitude': 1.0}, 'phone': '1234', 'mobile': '4321', 'other_phone': '41423', @@ -627,6 +627,7 @@ class CreateCompanyUserTest(APITestCase): } response = self.client.post(self.endpoint, data=data, format='json') + import ipdb; ipdb.set_trace() self.assertEquals(response.status_code, 201) self.assertEquals(len(mail.outbox), 1) diff --git a/core/views.py b/core/views.py index 63ec0b8..f69b2f0 100644 --- a/core/views.py +++ b/core/views.py @@ -122,10 +122,7 @@ def create_company_user(request): # create company company_data = request.data['company'] - # substitute coordinates for Point - geo = company_data.pop('geo') - if geo: - company_data['geo'] = Point(geo['latitude'],geo['longitude']) + company_serializer = CompanySerializer( data=company_data, ) @@ -133,7 +130,7 @@ def create_company_user(request): # save model instance data new_company = Company.objects.create(**company_serializer.validated_data) else: - return Response({"error": "Company data is not valid"}, status=406) + return Response({"error": company_serializer.errors}, status=406) # create user user_data = request.data['user'] @@ -151,7 +148,7 @@ def create_company_user(request): # send verification email utils.send_verification_email(request, new_user) else: - return Response({"error": "User data is not valid"}, status=406) + return Response({"error": user_serializer.errors}, status=406) return Response(status=status.HTTP_201_CREATED) From fc3ea28b5c32a9d150d6ea49ea6f5f645aa89e4c Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 11 Mar 2021 12:33:37 +0000 Subject: [PATCH 09/16] fix for create_company_user --- core/views.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/core/views.py b/core/views.py index 63ec0b8..53985d2 100644 --- a/core/views.py +++ b/core/views.py @@ -122,10 +122,6 @@ def create_company_user(request): # create company company_data = request.data['company'] - # substitute coordinates for Point - geo = company_data.pop('geo') - if geo: - company_data['geo'] = Point(geo['latitude'],geo['longitude']) company_serializer = CompanySerializer( data=company_data, ) @@ -133,7 +129,7 @@ def create_company_user(request): # save model instance data new_company = Company.objects.create(**company_serializer.validated_data) else: - return Response({"error": "Company data is not valid"}, status=406) + return Response({"error": company_serializer.errors}, status=406) # create user user_data = request.data['user'] @@ -151,7 +147,7 @@ def create_company_user(request): # send verification email utils.send_verification_email(request, new_user) else: - return Response({"error": "User data is not valid"}, status=406) + return Response({"error": user_serializer.errors}, status=406) return Response(status=status.HTTP_201_CREATED) From 67644512ff31f0676d49644183d2340b61d69e5c Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 11 Mar 2021 13:04:30 +0000 Subject: [PATCH 10/16] fixed error in user creation --- core/models.py | 2 ++ core/tests.py | 6 +++++- core/views.py | 8 ++++---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/core/models.py b/core/models.py index 3c49a74..89d995f 100644 --- a/core/models.py +++ b/core/models.py @@ -24,6 +24,8 @@ class UserManager(BaseUserManager): def create_user(self, email, password=None, **extra_fields): extra_fields.setdefault('is_superuser', False) + extra_fields.setdefault('is_staff', False) + extra_fields.setdefault('is_active', False) return self._create_user(email, password, **extra_fields) def create_superuser(self, email, password, **extra_fields): diff --git a/core/tests.py b/core/tests.py index dfbabfe..8dee31c 100644 --- a/core/tests.py +++ b/core/tests.py @@ -589,7 +589,7 @@ class CreateCompanyUserTest(APITestCase): self.factory = factories.CustomUserFactory self.model = models.CustomUser # create user - self.email = f"user@mail.com" + self.email = "user@mail.com" self.password = ''.join(random.choices(string.ascii_uppercase, k = 10)) self.user = self.factory(email=self.email, is_active=False) self.user.set_password(self.password) @@ -630,6 +630,10 @@ class CreateCompanyUserTest(APITestCase): self.assertEquals(response.status_code, 201) self.assertEquals(len(mail.outbox), 1) + # user exists and it's inactice + self.assertTrue(self.model.objects.get(email='test@email.com')) + self.assertFalse(self.model.objects.get(email='test@email.com').is_active) + self.assertTrue(Company.objects.get(cif='qwerewq')) def test_creation_error(self): diff --git a/core/views.py b/core/views.py index 53985d2..d1c3511 100644 --- a/core/views.py +++ b/core/views.py @@ -44,9 +44,9 @@ logging.basicConfig( class CustomUserViewSet(viewsets.ModelViewSet): - model = models.CustomUser + model = User model_name = 'custom_user' - queryset = models.CustomUser.objects.all() + queryset = User.objects.all() permission_classes = [CustomUserPermissions,] read_serializer_class = core_serializers.CustomUserReadSerializer write_serializer_class = core_serializers.CustomUserWriteSerializer @@ -77,7 +77,7 @@ class CustomUserViewSet(viewsets.ModelViewSet): if serializer.is_valid(): # save model instance data password = serializer.validated_data.pop('password') - instance = self.model(**serializer.validated_data) + instance = self.model.objects.create_user(**serializer.validated_data) instance.set_password(password) instance.save() # send verification email @@ -141,7 +141,7 @@ def create_company_user(request): if user_serializer.is_valid(): # save model instance data password = user_serializer.validated_data.pop('password') - new_user = User(**user_serializer.validated_data) + new_user = User.objects.create_user(**user_serializer.validated_data) new_user.set_password(password) new_user.save() # send verification email From dd4b079895ee71e63a031afc26dae87726185921 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 11 Mar 2021 13:10:54 +0000 Subject: [PATCH 11/16] fixes user creation test --- core/tests.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/tests.py b/core/tests.py index 8dee31c..1b1a9f0 100644 --- a/core/tests.py +++ b/core/tests.py @@ -41,7 +41,7 @@ class CustomUserViewSetTest(APITestCase): self.user = self.factory(email=self.reg_email, password=self.password, is_active=True) # anon user - def test_anon_user_can_create_active_instance(self): + def test_anon_user_can_create_inactive_instance(self): """Not logged-in user can create new instance of User but it's inactive """ data = { @@ -57,7 +57,7 @@ class CustomUserViewSetTest(APITestCase): self.assertEqual(response.status_code, status.HTTP_201_CREATED) # assert instance is inactive info = json.loads(response.content) - self.assertTrue(info['is_active']) + self.assertFalse(info['is_active']) # Assert instance exists on db self.assertTrue(self.model.objects.get(email=info['email'])) # assert verification email @@ -634,6 +634,8 @@ class CreateCompanyUserTest(APITestCase): self.assertTrue(self.model.objects.get(email='test@email.com')) self.assertFalse(self.model.objects.get(email='test@email.com').is_active) self.assertTrue(Company.objects.get(cif='qwerewq')) + # assert verification email + self.assertTrue(len(mail.outbox) == 1) def test_creation_error(self): From 5fe3883fcdb1847db650a2cd64d085785c859556 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 11 Mar 2021 13:18:50 +0000 Subject: [PATCH 12/16] my products ordered by created date, descending --- products/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/products/views.py b/products/views.py index 8103f67..228f6e1 100644 --- a/products/views.py +++ b/products/views.py @@ -72,7 +72,7 @@ class MyProductsViewSet(viewsets.ModelViewSet): permission_classes = [IsAuthenticated] def get_queryset(self): - return self.model.objects.filter(creator=self.request.user) + return self.model.objects.filter(creator=self.request.user).order_by('-created') def perform_create(self, serializer): serializer.save(creator=self.request.user) From 4cf22fd969d4dca980548a0ad49ad035e05351af Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 11 Mar 2021 13:51:14 +0000 Subject: [PATCH 13/16] bunch of stuff --- back_latienda/routers.py | 3 +-- back_latienda/urls.py | 1 + companies/tests.py | 31 ++++--------------------------- companies/views.py | 12 +++++++++++- core/serializers.py | 2 +- products/tests.py | 14 +++++++++++--- products/views.py | 5 +---- 7 files changed, 30 insertions(+), 38 deletions(-) diff --git a/back_latienda/routers.py b/back_latienda/routers.py index bad6b0c..83385ca 100644 --- a/back_latienda/routers.py +++ b/back_latienda/routers.py @@ -1,7 +1,7 @@ from rest_framework import routers from core.views import CustomUserViewSet -from companies.views import CompanyViewSet, MyCompanyViewSet, AdminCompanyViewSet +from companies.views import CompanyViewSet, AdminCompanyViewSet from products.views import ProductViewSet, MyProductsViewSet, AdminProductsViewSet from history.views import HistorySyncViewSet from stats.views import StatsLogViewSet @@ -13,7 +13,6 @@ router = routers.DefaultRouter() router.register('users', CustomUserViewSet, basename='users') router.register('companies', CompanyViewSet, basename='company') -router.register('my_company', MyCompanyViewSet, basename='my-company') router.register('admin_companies', AdminCompanyViewSet, basename='admin-companies') router.register('products', ProductViewSet, basename='product') router.register('my_products', MyProductsViewSet, basename='my-products') diff --git a/back_latienda/urls.py b/back_latienda/urls.py index 91431ad..c6743e3 100644 --- a/back_latienda/urls.py +++ b/back_latienda/urls.py @@ -39,6 +39,7 @@ urlpatterns = [ path('api/v1/search_products/', product_views.product_search, name='product-search'), path('api/v1/create_company_user/', core_views.create_company_user, name='create-company-user'), path('api/v1/my_user/', core_views.my_user, name='my-user'), + path('api/v1/my_company/', company_views.my_company, name='my-company'), path('api/v1/companies/sample/', company_views.random_company_sample , name='company-sample'), path('api/v1/purchase_email/', product_views.purchase_email, name='purchase-email'), path('api/v1/stats/me/', stat_views.track_user, name='user-tracker'), diff --git a/companies/tests.py b/companies/tests.py index 8473b02..1ad084f 100644 --- a/companies/tests.py +++ b/companies/tests.py @@ -312,12 +312,11 @@ class MyCompanyViewTest(APITestCase): self.user.set_password(self.password) self.user.save() - def tearDown(self): - self.model.objects.all().delete() - def test_auth_user_gets_data(self): # create instance - user_instances = [self.factory(creator=self.user) for i in range(5)] + company = CompanyFactory() + self.user.company = company + self.user.save() # Authenticate token = get_tokens_for_user(self.user) @@ -325,32 +324,10 @@ class MyCompanyViewTest(APITestCase): # Query endpoint response = self.client.get(self.endpoint) - payload = response.json() - # Assert forbidden code self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEquals(len(user_instances), len(payload)) - - def test_auth_user_can_paginate_instances(self): - """authenticated user can paginate instances - """ - - # Authenticate - token = get_tokens_for_user(self.user) - self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}") - - # create instances - instances = [self.factory(creator=self.user) for n in range(12)] - - # Request list - url = f"{self.endpoint}?limit=5&offset=10" - response = self.client.get(url) - - # Assert access is allowed - self.assertEqual(response.status_code, status.HTTP_200_OK) - # assert only 2 instances in response payload = response.json() - self.assertEquals(2, len(payload['results'])) + self.assertEquals(payload['company']['id'], company.id) def test_anon_user_cannot_access(self): # send in request diff --git a/companies/views.py b/companies/views.py index 5133d43..010c4b4 100644 --- a/companies/views.py +++ b/companies/views.py @@ -155,6 +155,16 @@ class CompanyViewSet(viewsets.ModelViewSet): return Response(message) +@api_view(['GET']) +@permission_classes([IsAuthenticated]) +def my_company(request): + if request.user.company: + serializer = CompanySerializer(request.user.company) + return Response({'company': serializer.data}) + else: + return Response(status=status.HTTP_406_NOT_ACCEPTABLE) + +''' class MyCompanyViewSet(viewsets.ModelViewSet): model = Company serializer_class = CompanySerializer @@ -165,7 +175,7 @@ class MyCompanyViewSet(viewsets.ModelViewSet): def perform_create(self, serializer): serializer.save(creator=self.request.user) - +''' class AdminCompanyViewSet(viewsets.ModelViewSet): """ Allows user with role 'SITE_ADMIN' to access all company instances diff --git a/core/serializers.py b/core/serializers.py index e71c71f..6a2039e 100644 --- a/core/serializers.py +++ b/core/serializers.py @@ -23,7 +23,7 @@ class CustomUserWriteSerializer(serializers.ModelSerializer): class Meta: model = models.CustomUser - fields = ('email', 'full_name', 'role', 'password', 'provider') + fields = ('email', 'full_name', 'role', 'password', 'provider', 'notify') class CreatorSerializer(serializers.ModelSerializer): diff --git a/products/tests.py b/products/tests.py index f503677..194fee3 100644 --- a/products/tests.py +++ b/products/tests.py @@ -952,9 +952,13 @@ class MyProductsViewTest(APITestCase): def test_auth_user_gets_data(self): # create instance + company = CompanyFactory() + self.user.company = company + self.user.save() + user_instances = [ - self.factory(creator=self.user), - self.factory(creator=self.user), + self.factory(company=company), + self.factory(company=company), ] # Authenticate @@ -976,7 +980,11 @@ class MyProductsViewTest(APITestCase): self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}") # create instances - instances = [self.factory(creator=self.user) for n in range(12)] + company = CompanyFactory() + self.user.company = company + self.user.save() + + instances = [self.factory(company=company) for n in range(12)] # Request list url = f"{self.endpoint}?limit=5&offset=10" diff --git a/products/views.py b/products/views.py index 228f6e1..4e132ae 100644 --- a/products/views.py +++ b/products/views.py @@ -72,10 +72,7 @@ class MyProductsViewSet(viewsets.ModelViewSet): permission_classes = [IsAuthenticated] def get_queryset(self): - return self.model.objects.filter(creator=self.request.user).order_by('-created') - - def perform_create(self, serializer): - serializer.save(creator=self.request.user) + return self.model.objects.filter(company=self.request.user.company).order_by('-created') class AdminProductsViewSet(viewsets.ModelViewSet): From 9e5fb89274830c285d1d7fe90d8aea4700002e7c Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 11 Mar 2021 13:56:09 +0000 Subject: [PATCH 14/16] cleanup --- core/tests.py | 114 ++++++++++---------------------------------------- core/views.py | 8 ---- 2 files changed, 21 insertions(+), 101 deletions(-) diff --git a/core/tests.py b/core/tests.py index 1b1a9f0..756b05a 100644 --- a/core/tests.py +++ b/core/tests.py @@ -161,6 +161,27 @@ class CustomUserViewSetTest(APITestCase): # Assert access is forbidden self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + def test_auth_user_can_modify_own_instance(self): + """Regular user can modify own instance + """ + # Create instance + data = { + "email": "new_email@mail.com", + "full_name": "New Full Name", + 'provider': 'PROVIDER', + 'notify': True, + } + + # Authenticate + token = get_tokens_for_user(self.user) + self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}") + + # Query endpoint + url = f'{self.endpoint}{self.user.pk}/' + response = self.client.put(url, data=data, format='json') + # Assert forbidden code + self.assertEqual(response.status_code, status.HTTP_200_OK) + # admin user def test_admin_user_can_create_instance(self): """Admin user can create new instance @@ -332,99 +353,6 @@ class ChangeUserPasswordViewTest(APITestCase): self.assertEqual(stored_password_hash, base64.b64encode(new_password_hash).decode()) -class UpdateUserViewTest(APITestCase): - - def setUp(self): - """Tests setup - """ - self.endpoint = '/api/v1/users/' - self.factory = factories.CustomUserFactory - self.model = models.CustomUser - # create regular user - self.reg_email = f"user@mail.com" - self.password = ''.join(random.choices(string.ascii_uppercase, k = 10)) - self.user = self.factory(email=self.reg_email, is_active=True) - self.user.set_password(self.password) - self.user.save() - # create admin user - self.admin_email = f"admin_user@mail.com" - self.admin_user = self.factory(email=self.admin_email, is_staff=True, is_active=True) - self.admin_user.set_password(self.password) - self.admin_user.save() - - def test_auth_user_can_modify_own_instance(self): - """Regular user can modify own instance - """ - # Create instance - data = { - "email": "new_email@mail.com", - "full_name": "New Full Name", - 'provider': 'PROVIDER', - 'notify': True, - } - - # Authenticate - token = get_tokens_for_user(self.user) - self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}") - - # Query endpoint - url = f'{self.endpoint}{self.user.pk}/' - response = self.client.put(url, data=data, format='json') - # Assert forbidden code - self.assertEqual(response.status_code, status.HTTP_200_OK) - - def test_auth_user_cannot_modify_random_instance(self): - """Regular user cannot modify randnom instance - """ - # Create instance - instance = self.factory() - - # Authenticate - token = get_tokens_for_user(self.user) - self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}") - - # Query endpoint - url = f'{self.endpoint}{instance.pk}/' - response = self.client.put(url, data={}, format='json') - # Assert forbidden code - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - - def test_anon_user_cannot_modify_random_instance(self): - """anon user cannot modify instance - """ - # Create instance - instance = self.factory() - - # Query endpoint - url = f'{self.endpoint}{instance.pk}/' - response = self.client.put(url, data={}, format='json') - # Assert forbidden code - self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) - - def test_admin_user_can_modify_random_instance(self): - """Regular user cannot modify randnom instance - """ - # Create instance - instance = self.factory() - - # Authenticate - token = get_tokens_for_user(self.admin_user) - self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}") - - data = { - "email": "new_email@mail.com", - "full_name": "New Full Name", - 'provider': 'PROVIDER', - 'notify': True, - } - - # Query endpoint - url = f'{self.endpoint}{instance.pk}/' - response = self.client.put(url, data=data, format='json') - # Assert forbidden code - self.assertEqual(response.status_code, status.HTTP_200_OK) - - class LoadCoopManagerTestCase(APITestCase): def setUp(self): diff --git a/core/views.py b/core/views.py index d1c3511..cbcca9c 100644 --- a/core/views.py +++ b/core/views.py @@ -101,14 +101,6 @@ class ChangeUserPasswordView(UpdateAPIView): serializer_class = core_serializers.ChangePasswordSerializer -class UpdateUserView(UpdateAPIView): - - model = models.CustomUser - queryset = model.objects.all() - permission_classes = (YourOwnUserPermissions,) - serializer_class = core_serializers.UpdateUserSerializer - - @api_view(['POST',]) @permission_classes([CustomUserPermissions,]) def create_company_user(request): From 6862d62bf5f22404e11034c8e7f0767c2f99635a Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 12 Mar 2021 10:13:24 +0000 Subject: [PATCH 15/16] user activation now redirects to home --- back_latienda/settings/base.py | 3 +++ core/tests.py | 16 ++++++++++++++++ core/views.py | 4 ++++ example.env | 4 +++- 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/back_latienda/settings/base.py b/back_latienda/settings/base.py index 0ee9a92..5298aca 100644 --- a/back_latienda/settings/base.py +++ b/back_latienda/settings/base.py @@ -171,3 +171,6 @@ MAP_WIDGETS = { ), "GOOGLE_MAP_API_KEY": os.getenv('GOOGLE_MAP_API_KEY') } + +# ACTIVATION_REDIRECT URL +ACTIVATION_REDIRECT = os.getenv('ACTIVATION_REDIRECT') diff --git a/core/tests.py b/core/tests.py index 756b05a..2ed7ca1 100644 --- a/core/tests.py +++ b/core/tests.py @@ -9,6 +9,7 @@ from django.test import TestCase from django.core import mail from django.utils.http import urlsafe_base64_encode from django.utils.encoding import force_bytes +from django.conf import settings from rest_framework.test import APITestCase from rest_framework import status @@ -492,6 +493,21 @@ class ActivateUserTest(APITestCase): response = self.client.get(url) + # assertions + self.assertEquals(response.status_code, 302) + self.assertEquals(response.url, settings.ACTIVATION_REDIRECT) + + def test_correct_activation_no_redirect(self): + # set ACTIVATION_REDIRECT to '' + settings.ACTIVATION_REDIRECT = '' + # create values + uid = urlsafe_base64_encode(force_bytes(self.user.pk)) + token = account_activation_token.make_token(self.user) + + url = f'/activate/{uid}/{token}/' + + response = self.client.get(url) + # assertions self.assertEquals(response.status_code, 200) self.assertTrue(self.user.email in str(response.content)) diff --git a/core/views.py b/core/views.py index cbcca9c..bc9afd0 100644 --- a/core/views.py +++ b/core/views.py @@ -11,6 +11,8 @@ from django.utils.http import urlsafe_base64_decode from django.utils.encoding import force_text from django.db import IntegrityError from django.contrib.gis.geos import Point +from django.shortcuts import redirect +from django.conf import settings from rest_framework import status from rest_framework import viewsets @@ -190,6 +192,8 @@ def activate_user(request, uidb64, token): # activate user user.is_active = True user.save() + if settings.ACTIVATION_REDIRECT: + return redirect(settings.ACTIVATION_REDIRECT) return Response(f"Tu cuenta de usuario {user.email} ha sido activada") else: return Response({"error": f"Tu token de verificacion no coincide con ningĂșn usuario registrado"}, status=status.HTTP_406_NOT_ACCEPTABLE) diff --git a/example.env b/example.env index 66a53de..87e699b 100644 --- a/example.env +++ b/example.env @@ -14,4 +14,6 @@ AWS_SECRET_ACCESS_KEY_SES = '' WC_KEY = '' WC_SECRET = '' # GOOGLE MAPS -GOOGLE_MAP_API_KEY = '' \ No newline at end of file +GOOGLE_MAP_API_KEY = '' +# USER ACTIVATION REDIRECTION +ACTIVATION_REDIRECT = '' \ No newline at end of file From 37f222e6c90109d2ef2f427f7156087261be0544 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 12 Mar 2021 10:22:23 +0000 Subject: [PATCH 16/16] changes for my endpoints --- companies/views.py | 2 +- products/tests.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/companies/views.py b/companies/views.py index 010c4b4..62c4767 100644 --- a/companies/views.py +++ b/companies/views.py @@ -171,7 +171,7 @@ class MyCompanyViewSet(viewsets.ModelViewSet): permission_classes = [IsAuthenticated] def get_queryset(self): - return self.model.objects.filter(creator=self.request.user) + return self.model.objects.filter(company=self.request.user.company) def perform_create(self, serializer): serializer.save(creator=self.request.user) diff --git a/products/tests.py b/products/tests.py index 194fee3..d9db3ea 100644 --- a/products/tests.py +++ b/products/tests.py @@ -1004,6 +1004,18 @@ class MyProductsViewTest(APITestCase): # check response self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + def test_auth_user_without_company(self): + # Authenticate + token = get_tokens_for_user(self.user) + self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}") + + # Query endpoint + response = self.client.get(self.endpoint) + payload = response.json() + # Assert forbidden code + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEquals([], payload) + class AdminProductViewSetTest(APITestCase):