fixes to purchase_email and readme update

This commit is contained in:
Sam
2021-03-09 13:47:18 +00:00
parent 4218d94a26
commit f0a076057c
3 changed files with 193 additions and 42 deletions

176
README.md
View File

@@ -7,7 +7,11 @@ This README aims to document functionality of backend as well as required steps
- [First Steps](#first-steps) - [First Steps](#first-steps)
- [Load location data](#load-location-data) - [Load location data](#load-location-data)
- [Load taxonomy data](#load-taxonomy-data) - [Load taxonomy data](#load-taxonomy-data)
- [Endpoints](#endpoints) - [Company Endpoints](#company-endpoints)
- [Product Endpoints](#product-endpoints)
- [Core Endpoints](#core-endpoints)
- [History Endpoints](#history-endpoints)
- [Stats Endpoints](#stats-endpoints)
- [Shop Integrations](#shop-integrations) - [Shop Integrations](#shop-integrations)
- [WooCommerce](#woocommerce) - [WooCommerce](#woocommerce)
- [Product Search](#product-search) - [Product Search](#product-search)
@@ -57,25 +61,158 @@ This data serves as initial Tags
To load initial set of tags: `python manage.py addtaxonomy` To load initial set of tags: `python manage.py addtaxonomy`
## Endpoints ## Company Endpoints
### CompanyViewSet
Queryset: validated Company instances only
Permissions:
- anon user: safe methods
- auth user: full access where user is company creator
### MyCompanyViewSet
Queryset: Company instances where user is creator
Permissions:
- anon user: no access
- auth user: full access
### AdminCompanyViewSet
Queryset: all Company instances, validated or not
Permissions: only accesible to authenticated users with role `SITE_ADMIN`
## Pagination ### random_company_sample
By default a `LimitOffsetPagination` pagination is enabled Method view that returns a randome sample of companies
Examples: `http://127.0.0.1:8000/api/v1/products/?limit=10&offset=0` By default it returns 6 instances, but can be customized through parameter `size`
The response data has the following keys:
``` ## Product Endpoints
dict_keys(['count', 'next', 'previous', 'results'])
``` ### ProductViewSet
Endpoint url: `/api/v1/products/`
Queryset: active Product instances only
Permissions:
- anon user: safe methods
- auth user: full access where user is product creator
### MyProductsViewSet
Endpoint url: `/api/v1/my_products/`
Queryset: Product instances where user is creator
Permissions:
- anon user: no access
- auth user: full access
### AdminProductsViewSet
Endpoint url: `/api/v1/admin_products/`
Queryset: all Product instances, acgtive or not
Permissions: only accesible to authenticated users with role `SITE_ADMIN`
### load_coop_products [POST]
Endpoint url: `/api/v1/load_products/`
Method view that reads a CSV file.
### product_search [GET]
Endpoint url: `/api/v1/search_products/`
Allows searching of Products to all users
Parameters:
- q: used for search [MANDATORY]
- limit: max number of returned instances [OPTIONAL]
- offset: where to start counting results [OPTIONAL]
- shipping_cost: true/false
- discount: true/false
- category: string
- tags: string
- order: string (newest/oldest)
- price_min: int
- price_max: int
### purchase_email [POST]
Endpoint url: `/api/v1/purchase_email/`
Sends email to company manager about the product that the user wants to purchase, and sends confirmation email to user.
Parameters:
- email: mandatory for anonymous users
- telephone
- company
- product
- comment
## Core Endpoints
### CustomUserViewSet
Endpoint url: `/api/v1/users/`
Queryset: all CustomUser instances
Permissions:
- anon user: only POST to register new user
- auth user: no access
- admin user: full access
### ChangeUserPasswordView
Ednpoint url: `/api/v1/user/change_password/<int:pk>/`
Permissions: only accessible for your own user instance
### UpdateUserView
Endpoint url: `/api/v1/user/update/`
Permissions: only accessible for your own user instance
### my_user [GET]
Endpoint url: `/api/v1/my_user/`
Returns instance of authenticated user
### load_coop_managers [POST]
Ednpoint url: `/api/v1/load_coops/`
For each row it creates a Company instance, and a user instance linked to the company, with role `COOP_MANAGER`
### User Management ### User Management
Creation: Creation:
- endpoint: /api/v1/users/ - endpoint: `/api/v1/users/`
- method: GET - method: GET
- payload: - payload:
```json ```json
@@ -149,32 +286,21 @@ To create user:
``` ```
### Companies ## History Endpoints
Endpoint url: `/api/v1/companies/`
To get company linked to authenticated user: `/api/v1/my_company/`
### Products
Endpoint url: `/api/v1/products/`
To get products linked to authenticated user: `/api/v1/my_products/`
### History
Endpoint url: `/api/v1/history/`: Endpoint url: `/api/v1/history/`:
Historical records about product importation Historical records about product importation
### Stats ## Stats Endpoints
Endpoint url: `/api/v1/stats/` Endpoint url: `/api/v1/stats/`
logs about user interaction with products links logs about user interaction with products links
### Locations ## Location Endpoints
Location ednpoints: Location ednpoints:

View File

@@ -1210,12 +1210,13 @@ class PurchaseEmailTest(APITestCase):
self.password = ''.join(random.choices(string.ascii_uppercase, k = 10)) self.password = ''.join(random.choices(string.ascii_uppercase, k = 10))
self.user = CustomUserFactory(email=self.email, is_active=True) self.user = CustomUserFactory(email=self.email, is_active=True)
self.user.set_password(self.password) self.user.set_password(self.password)
# self.user.role = 'SITE_ADMIN'
self.user.save() self.user.save()
def test_anon_user_can_use(self): def test_anon_user_can_use(self):
company = CompanyFactory() company = CompanyFactory()
self.user.role = 'COOP_MANAGER'
self.user.company = company
self.user.save()
product = ProductFactory(company=company) product = ProductFactory(company=company)
data = { data = {
@@ -1225,8 +1226,29 @@ class PurchaseEmailTest(APITestCase):
'product': product.id, 'product': product.id,
'comment': '', 'comment': '',
} }
response = self.client.post(self.endpoint, json=data) response = self.client.post(self.endpoint, data=data, format='json')
import ipdb; ipdb.set_trace() # assertions
self.assertEquals(response.status_code, 200)
self.assertEquals(2, len(mail.outbox))
def test_auth_user_can_use(self):
company = CompanyFactory()
self.user.role = 'COOP_MANAGER'
self.user.company = company
self.user.save()
product = ProductFactory(company=company)
data = {
'telephone': '123123123',
'company': company.id,
'product': product.id,
'comment': '',
}
# Authenticate
token = get_tokens_for_user(self.user)
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")
response = self.client.post(self.endpoint, data=data, format='json')
# assertions # assertions
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)
self.assertEquals(2, len(mail.outbox)) self.assertEquals(2, len(mail.outbox))

View File

@@ -6,6 +6,8 @@ import json
from django.db.models import Q from django.db.models import Q
from django.core import serializers from django.core import serializers
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.template.loader import render_to_string
from django.core.mail import EmailMessage
# Create your views here. # Create your views here.
from rest_framework import status from rest_framework import status
@@ -24,6 +26,7 @@ from dal import autocomplete
from products.models import Product, CategoryTag from products.models import Product, CategoryTag
from products.serializers import ProductSerializer, TagFilterSerializer, SearchResultSerializer from products.serializers import ProductSerializer, TagFilterSerializer, SearchResultSerializer
from companies.models import Company 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, ReadOnly
from .utils import extract_search_filters, find_related_products_v6, product_loader, find_related_products_v7 from .utils import extract_search_filters, find_related_products_v6, product_loader, find_related_products_v7
from utils.tag_serializers import TaggitSerializer from utils.tag_serializers import TaggitSerializer
@@ -247,19 +250,21 @@ class CategoryTagAutocomplete(autocomplete.Select2QuerySetView):
return qs # [x.label for x in qs] return qs # [x.label for x in qs]
@permission_classes([AllowAny,])
@api_view(['POST']) @api_view(['POST'])
@permission_classes([AllowAny,])
def purchase_email(request): def purchase_email(request):
"""Notify coop manager and user about item purchase """Notify coop manager and user about item purchase
""" """
data = json.loads(request.body) data = json.loads(request.body)
# check data # check data
if request.user.is_anonymous and 'email' not in data:
return Response({"error": "Anonymous users must include an email parameter value"}, status=status.HTTP_406_NOT_ACCEPTABLE)
try: try:
for param in ('email', 'telephone', 'company', 'product', 'comment'): for param in ('telephone', 'company', 'product', 'comment'):
assert(param in data.keys()) assert(param in data.keys())
except: except:
return Response({"error": "Required parameters for anonymous user: email, telephone"}, status=status.HTTP_406_NOT_ACCEPTABLE) return Response({"error": "Required parameters for anonymous user: telephone, company, product, comment"}, status=status.HTTP_406_NOT_ACCEPTABLE)
if request.user.is_anonymous: if request.user.is_anonymous:
email = data.get('email') email = data.get('email')
@@ -273,7 +278,7 @@ def purchase_email(request):
return Response({"error": "Invalid value for company"}, status=status.HTTP_406_NOT_ACCEPTABLE) return Response({"error": "Invalid value for company"}, status=status.HTTP_406_NOT_ACCEPTABLE)
# get company manager # get company manager
manager = User.objects.filter(company=company).first() manager = User.objects.filter(company=company).first()
if not manager and manager.role != 'COOP_MANAGER': if not manager or manager.role != 'COOP_MANAGER':
return Response({"error": "Company has no managing user"}, status=status.HTTP_406_NOT_ACCEPTABLE) return Response({"error": "Company has no managing user"}, status=status.HTTP_406_NOT_ACCEPTABLE)
# get product # get product
product = Product.objects.filter(id=data['product'], company=company).first() product = Product.objects.filter(id=data['product'], company=company).first()
@@ -297,19 +302,17 @@ def purchase_email(request):
'product': product, 'product': product,
}) })
subject = 'Confirmación de contacto con vendedor' subject = 'Confirmación de contacto con vendedor'
email = EmailMessage(subject, message, to=[request.user.email]) email = EmailMessage(subject, user_message, to=[email])
email.send() email.send()
logging.info(f"Purchase Contact confirmation email sent to {request.user.email}") logging.info(f"Purchase Contact confirmation email sent to {email}")
# create statslog instance to register interaction # create statslog instance to register interaction
stats_data = { stats_data = {
'action_object': instance, 'action_object': product,
'user': None, 'user': request.user if request.user.is_authenticated else None,
'anonymous': True, 'anonymous': request.user.is_anonymous,
'ip_address': client_ip,
'geo': g.geos(client_ip),
'contact': True, 'contact': True,
'shop': instance.shop, 'shop': company.shop,
} }
StatsLog.objects.create(**stats_data) StatsLog.objects.create(**stats_data)