added api endpoints, updated location

This commit is contained in:
Alexander Karpov 2023-05-21 20:46:07 +03:00
parent 9c2beb8cd7
commit 3bf8042ec2
18 changed files with 2179549 additions and 30 deletions

View File

@ -1,11 +1,18 @@
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from passfinder.recomendations.api.views import TinderView, PersonalRecommendation
from passfinder.users.api.views import UserViewSet
router = DefaultRouter()
router.register('tinder', TinderView)
router.register("recommendations", PersonalRecommendation)
router.register("user", UserViewSet)
app_name = "api"
urlpatterns = router.urls
urlpatterns = [
path("", include("passfinder.events.api.urls")),
path("auth/", include("passfinder.users.api.urls")),
]
urlpatterns += router.urls

View File

@ -39,7 +39,11 @@
# DATABASES
# ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#databases
DATABASES = {"default": env.db("DATABASE_URL", "postgres://postgres:Ilvas2006@localhost:5432/passfinder")}
DATABASES = {
"default": env.db(
"DATABASE_URL", "postgres://postgres:Ilvas2006@localhost:5432/passfinder"
)
}
DATABASES["default"]["ATOMIC_REQUESTS"] = True
# https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-DEFAULT_AUTO_FIELD
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
@ -74,11 +78,7 @@
"polymorphic",
]
LOCAL_APPS = [
"passfinder.users",
"passfinder.events",
"passfinder.recomendations"
]
LOCAL_APPS = ["passfinder.users", "passfinder.events", "passfinder.recomendations"]
# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
@ -316,8 +316,7 @@
# django-rest-framework - https://www.django-rest-framework.org/api-guide/settings/
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework.authentication.SessionAuthentication",
"rest_framework.authentication.TokenAuthentication",
"rest_framework_simplejwt.authentication.JWTAuthentication",
),
"DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
@ -334,8 +333,8 @@
"VERSION": "1.0.0",
"SERVE_PERMISSIONS": [],
"SERVERS": [
{"url": "https://dev2.akarpov.ru", "description": "Production server"},
{"url": "http://127.0.0.1:8000", "description": "Local Development server"},
{"url": "https://akarpov.ru", "description": "Production server"},
],
}

View File

@ -11,7 +11,7 @@
default="dh0qndI7XExEDZIdMDh2VPHbW9VNq0jsjsI2ImR9EyDQfNMYudHUaJswMggfhotG",
)
# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts
ALLOWED_HOSTS = ["localhost", "0.0.0.0", "127.0.0.1"]
ALLOWED_HOSTS = ["localhost", "0.0.0.0", "127.0.0.1", "dev2.akarpov.ru"]
CORS_ORIGIN_ALLOW_ALL = True
# CACHES

View File

@ -2,10 +2,7 @@
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import include, path
from django.views import defaults as default_views
from django.views.generic import TemplateView
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
from rest_framework.authtoken.views import obtain_auth_token
urlpatterns = [
# Django Admin, use {% url 'admin:index' %}
@ -17,8 +14,6 @@
urlpatterns += [
# API base url
path("api/", include("config.api_router")),
# DRF auth token
path("api/auth/token/", obtain_auth_token),
path("api/schema/", SpectacularAPIView.as_view(), name="api-schema"),
path(
"api/docs/",

2179283
data.json

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
from rest_framework import serializers
from passfinder.events.models import Hotel, HotelPhone, City, Event
from passfinder.events.models import Hotel, HotelPhone, City, Event, BasePoint
class HotelPhoneSerializer(serializers.ModelSerializer):
@ -34,4 +34,14 @@ class Meta:
class EventSerializer(serializers.ModelSerializer):
class Meta:
model = Event
fields = ('type', 'title', 'description', 'city', 'oid')
fields = ("type", "title", "description", "city", "oid")
class PointSerializer(serializers.ModelSerializer):
# location = serializers.ListSerializer(
# child=serializers.FloatField(), source="bare_location", max_length=2
# )
class Meta:
model = BasePoint
fields = ["title", "description", "location", "icon"]

View File

@ -0,0 +1,9 @@
from django.urls import path
from passfinder.events.api.views import BuildRouteApiView
app_name = "events"
urlpatterns = [
path("route/build", BuildRouteApiView.as_view(), name="build_route")
]

View File

@ -0,0 +1,20 @@
from django_filters import DateFilter
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from passfinder.events.api.serializers import PointSerializer
from passfinder.events.models import BasePoint
class BuildRouteApiView(GenericAPIView):
filter_backends = (DjangoFilterBackend,)
filterset_class = DateFilter
serializer_class = PointSerializer
def get(self, request):
return Response(
data=PointSerializer(many=True).to_representation(
BasePoint.objects.order_by("?")[:10]
)
)

View File

@ -0,0 +1,5 @@
from django_filters import rest_framework as filters
class DateFilter(filters.FilterSet):
date = filters.DateRangeFilter(field_name="date")

View File

@ -0,0 +1,43 @@
# Generated by Django 4.2.1 on 2023-05-21 17:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("events", "0014_remove_excursionroute_excursion_and_more"),
]
operations = [
migrations.AddField(
model_name="basepoint",
name="lat",
field=models.FloatField(db_index=True, default=0),
),
migrations.AddField(
model_name="basepoint",
name="lon",
field=models.FloatField(db_index=True, default=0),
),
migrations.AddField(
model_name="city",
name="lat",
field=models.FloatField(db_index=True, default=0),
),
migrations.AddField(
model_name="city",
name="lon",
field=models.FloatField(db_index=True, default=0),
),
migrations.AddField(
model_name="place",
name="lat",
field=models.FloatField(db_index=True, default=0),
),
migrations.AddField(
model_name="place",
name="lon",
field=models.FloatField(db_index=True, default=0),
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 4.2.1 on 2023-05-21 17:44
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("events", "0015_basepoint_lat_basepoint_lon_city_lat_city_lon_and_more"),
]
operations = [
migrations.RemoveField(
model_name="basepoint",
name="location",
),
migrations.RemoveField(
model_name="city",
name="location",
),
migrations.RemoveField(
model_name="place",
name="location",
),
]

View File

@ -1,6 +1,5 @@
from django.contrib.postgres.fields import ArrayField
from django.db import models
from location_field.models.plain import PlainLocationField
from polymorphic.models import PolymorphicModel
from passfinder.utils.choices import count_max_length
@ -38,7 +37,12 @@ class City(OIDModel):
region = models.ForeignKey(
"Region", related_name="cities", null=True, on_delete=models.SET_NULL
)
location = PlainLocationField(zoom=6)
lon = models.FloatField(default=0, db_index=True)
lat = models.FloatField(default=0, db_index=True)
@property
def location(self):
return [self.lon, self.lat]
def __str__(self):
return self.title
@ -55,11 +59,16 @@ class Place(OIDModel):
)
title = models.CharField(max_length=250)
description = models.TextField()
location = PlainLocationField(zoom=6)
lon = models.FloatField(default=0, db_index=True)
lat = models.FloatField(default=0, db_index=True)
sites = ArrayField(models.CharField(max_length=250), null=True)
phones = ArrayField(models.CharField(max_length=250), null=True)
working_time = models.JSONField(null=True)
@property
def location(self):
return [self.lon, self.lat]
class Tag(OIDModel):
name = models.CharField(max_length=250)
@ -82,10 +91,20 @@ class BasePoint(OIDModel, PolymorphicModel):
"Place", related_name="points", null=True, on_delete=models.SET_NULL
)
sort = models.IntegerField(default=0)
location = PlainLocationField(zoom=6)
lon = models.FloatField(default=0, db_index=True)
lat = models.FloatField(default=0, db_index=True)
can_buy = models.BooleanField(default=True)
priority = models.BooleanField(default=False)
@property
def location(self):
return [self.lon, self.lat]
@property
def icon(self):
# TODO: change to icon/first image
return "https://akarpov.ru/media/uploads/files/qMO4dDfIXP.webp"
def __str__(self):
return self.title

View File

@ -0,0 +1,5 @@
from django.contrib import admin
from passfinder.users.models import User
admin.site.register(User)

View File

@ -7,8 +7,25 @@
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ["username", "name", "url"]
fields = ["username", "email"]
class UserRegisterSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ("username", "email", "password")
extra_kwargs = {
"url": {"view_name": "api:user-detail", "lookup_field": "username"}
"password": {"write_only": True},
"email": {"required": True},
}
def create(self, validated_data):
user = User.objects.create(
username=validated_data["username"],
email=validated_data["email"],
)
user.set_password(validated_data["password"])
user.save()
return user

View File

@ -0,0 +1,10 @@
from django.urls import path
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
from .views import RegisterApiView
urlpatterns = [
path("token/", TokenObtainPairView.as_view(), name="token_obtain_pair"),
path("refresh/", TokenRefreshView.as_view(), name="token_refresh"),
path("register/", RegisterApiView.as_view(), name="user_register"),
]

View File

@ -1,11 +1,12 @@
from django.contrib.auth import get_user_model
from rest_framework import status
from drf_spectacular.utils import extend_schema
from rest_framework import status, generics, permissions
from rest_framework.decorators import action
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin, UpdateModelMixin
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet
from .serializers import UserSerializer
from .serializers import UserSerializer, UserRegisterSerializer
User = get_user_model()
@ -23,3 +24,16 @@ def get_queryset(self, *args, **kwargs):
def me(self, request):
serializer = UserSerializer(request.user, context={"request": request})
return Response(status=status.HTTP_200_OK, data=serializer.data)
class RegisterApiView(generics.CreateAPIView):
"""Creates new user and sends verification email"""
serializer_class = UserRegisterSerializer
permission_classes = [permissions.AllowAny]
@extend_schema(
operation_id="auth_user_register",
)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)

60
poetry.lock generated
View File

@ -848,6 +848,21 @@ files = [
[package.dependencies]
Django = ">=3.2"
[[package]]
name = "django-filter"
version = "23.2"
description = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "django-filter-23.2.tar.gz", hash = "sha256:2fe15f78108475eda525692813205fa6f9e8c1caf1ae65daa5862d403c6dbf00"},
{file = "django_filter-23.2-py3-none-any.whl", hash = "sha256:d12d8e0fc6d3eb26641e553e5d53b191eb8cec611427d4bdce0becb1f7c172b5"},
]
[package.dependencies]
Django = ">=3.2"
[[package]]
name = "django-ipware"
version = "5.0.0"
@ -1013,6 +1028,31 @@ files = [
django = ">=3.0"
pytz = "*"
[[package]]
name = "djangorestframework-simplejwt"
version = "5.2.2"
description = "A minimal JSON Web Token authentication plugin for Django REST Framework"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "djangorestframework_simplejwt-5.2.2-py3-none-any.whl", hash = "sha256:4c0d2e2513e12587d93501ac091781684a216c3ee614eb3b5a10586aef5ca845"},
{file = "djangorestframework_simplejwt-5.2.2.tar.gz", hash = "sha256:d27d4bcac2c6394f678dea8b4d0d511c6e18a7f2eb8aaeeb8a7de601aeb77c42"},
]
[package.dependencies]
django = "*"
djangorestframework = "*"
pyjwt = ">=1.7.1,<3"
[package.extras]
crypto = ["cryptography (>=3.3.1)"]
dev = ["Sphinx (>=1.6.5,<2)", "cryptography", "flake8", "ipython", "isort", "pep8", "pytest", "pytest-cov", "pytest-django", "pytest-watch", "pytest-xdist", "python-jose (==3.3.0)", "sphinx-rtd-theme (>=0.1.9)", "tox", "twine", "wheel"]
doc = ["Sphinx (>=1.6.5,<2)", "sphinx-rtd-theme (>=0.1.9)"]
lint = ["flake8", "isort", "pep8"]
python-jose = ["python-jose (==3.3.0)"]
test = ["cryptography", "pytest", "pytest-cov", "pytest-django", "pytest-xdist", "tox"]
[[package]]
name = "djangorestframework-stubs"
version = "1.10.0"
@ -2010,6 +2050,24 @@ files = [
[package.extras]
plugins = ["importlib-metadata"]
[[package]]
name = "pyjwt"
version = "2.7.0"
description = "JSON Web Token implementation in Python"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "PyJWT-2.7.0-py3-none-any.whl", hash = "sha256:ba2b425b15ad5ef12f200dc67dd56af4e26de2331f965c5439994dad075876e1"},
{file = "PyJWT-2.7.0.tar.gz", hash = "sha256:bd6ca4a3c4285c1a2d4349e5a035fdf8fb94e04ccd0fcbe6ba289dae9cc3e074"},
]
[package.extras]
crypto = ["cryptography (>=3.4.0)"]
dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"]
docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"]
tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
[[package]]
name = "pylint"
version = "2.17.4"
@ -2930,4 +2988,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
content-hash = "8461316ade8f4a6256022b829102583a19138c47a7d5c20ccb47d7ba50ec1a1f"
content-hash = "9abf5a717109289c45bdc20e08a2b6ad6d3e0ef05dc56d194fa3a77450648a57"

View File

@ -49,6 +49,8 @@ sentry-sdk = "^1.12.0"
django-location-field = "^2.7.0"
django-polymorphic = "^3.1.0"
annoy = "^1.17.2"
django-filter = "^23.2"
djangorestframework-simplejwt = "^5.2.2"
[build-system]