diff --git a/README.md b/README.md index a19af33..db8e50c 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ This README aims to document functionality of backend as well as required steps - [Location Data](#location-data) - [Endpoints](#endpoints) - [Data Load](#data-load) +- [GeoIP Setup](#geoip-setup) - [Development Utils](#development-utils) ## First Steps @@ -177,6 +178,18 @@ CSV headers: `id,nombre-producto,descripcion,imagen,url,precio,gastos-envio,cond Only admin users have access to endoint + +## GeoIP Setup + +Module: `geoip2` + +- Download the `GeoLite2 City` and `GeoLite2 Country` binary datasets from maxmind.com +- Unzip files into `datasets/` folder +- Set `settings.GEOIP_PATH` to datasets folder + +Optional: install `libmaxminddb` C library for improved performance + + ## Development Utils ### Fake product load diff --git a/back_latienda/settings/development.py b/back_latienda/settings/development.py index 32d91b6..fda1182 100644 --- a/back_latienda/settings/development.py +++ b/back_latienda/settings/development.py @@ -21,8 +21,9 @@ DATABASES = { }, } -MEDIA_ROOT = BASE_DIR + '/media/' +MEDIA_ROOT = BASE_DIR + '/../media/' MEDIA_URL = '/media/' +GEOIP_PATH = BASE_DIR + '/../datasets/' # JWT SETTINGS SIMPLE_JWT = { diff --git a/companies/views.py b/companies/views.py index 63f2895..845e8a8 100644 --- a/companies/views.py +++ b/companies/views.py @@ -3,6 +3,7 @@ import logging from django.shortcuts import render, get_object_or_404 from django.core.mail import EmailMessage from django.template.loader import render_to_string +from django.contrib.gis.geoip2 import GeoIP2 # Create your views here. from rest_framework import viewsets @@ -10,6 +11,9 @@ from rest_framework.response import Response from rest_framework.permissions import IsAuthenticatedOrReadOnly, IsAuthenticated from rest_framework.decorators import api_view, permission_classes, action +from ipware import get_client_ip + +from stats.models import StatsLog from companies.models import Company from companies.serializers import CompanySerializer @@ -33,6 +37,11 @@ class CompanyViewSet(viewsets.ModelViewSet): queryset = self.get_custom_queryset(request) instance = queryset.filter(pk=kwargs['pk']).first() if instance: + # IP stuff + client_ip, is_routable = get_client_ip(request) + g = GeoIP2() + + # deserialize payload data = json.loads(request.body) if request.user.is_authenticated: # send email to manager @@ -60,6 +69,15 @@ class CompanyViewSet(viewsets.ModelViewSet): email = EmailMessage(subject, message, to=[request.user.email]) email.send() logging.info(f"Contact confirmation email sent to {request.user.email}") + stats_data = { + 'action_object': instance, + 'user': None, + 'anonymous': True, + 'ip_address': client_ip, + 'geo': g.geos(client_ip), + 'contact': True, + 'shop': instance.shop, + } else: # for unauthenticated users company_message = render_to_string('company_contact.html', { @@ -83,7 +101,18 @@ class CompanyViewSet(viewsets.ModelViewSet): email = EmailMessage(subject, user_message, to=[data['email']]) email.send() logging.info(f"Contact confirmation email sent to anonymous user {data['email']}") - # TODO: create statslog instance to rgister interaction + # statslog data to register interaction + stats_data = { + 'action_object': instance, + 'user': request.user, + 'anonymous': False, + 'ip_address': client_ip, + 'geo': g.geos(client_ip), + 'contact': True, + 'shop': instance.shop, + } + # create statslog instance to register interaction + StatsLog.objects.create(**stats_data) return Response(data=data) else: diff --git a/core/utils.py b/core/utils.py index 29cdab4..a2da2ad 100644 --- a/core/utils.py +++ b/core/utils.py @@ -17,8 +17,10 @@ class AccountActivationTokenGenerator(PasswordResetTokenGenerator): def _make_hash_value(self, user, timestamp): return f"{user.pk}{timestamp}{user.full_name}" + account_activation_token = AccountActivationTokenGenerator() + def get_tokens_for_user(user): refresh = RefreshToken.for_user(user) @@ -39,12 +41,14 @@ def get_auth_token(client, email, password): # logging.error(f"User {email} was refused a token: {response.content}") return None + def create_active_user(email, password): user = User.objects.create_user(email=email, password=password) user.is_active = True user.save() return user + def create_admin_user(email, password): user = User.objects.create_user(email=email, password=password) user.is_staff = True @@ -52,6 +56,7 @@ def create_admin_user(email, password): user.save() return user + def send_verification_email(request, user): try: current_site = get_current_site(request) diff --git a/datasets/GeoLite2-City.mmdb b/datasets/GeoLite2-City.mmdb new file mode 100644 index 0000000..c080c5f Binary files /dev/null and b/datasets/GeoLite2-City.mmdb differ diff --git a/datasets/GeoLite2-Country.mmdb b/datasets/GeoLite2-Country.mmdb new file mode 100644 index 0000000..b52677f Binary files /dev/null and b/datasets/GeoLite2-Country.mmdb differ diff --git a/requirements.txt b/requirements.txt index f62b08f..05d3342 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,4 +10,6 @@ django-cors-headers==3.5.0 django-taggit-serializer==0.1.7 django-tagulous==1.1.0 Pillow==8.1.0 -drf-extra-fields==3.0.4 \ No newline at end of file +drf-extra-fields==3.0.4 +django-ipware==3.0.2 +geoip2==4.1.0 \ No newline at end of file