Pridani sitemap, fix flake

This commit is contained in:
Michal Kváček
2024-02-26 09:44:40 +01:00
parent ff32c36a94
commit 1f8b338297
34 changed files with 590 additions and 205 deletions
+1 -1
View File
@@ -4,4 +4,4 @@ from mailer import Mailer
async def send_email(email: Email):
mailer = Mailer()
return await mailer.send(email)
return await mailer.send(email)
+22 -22
View File
@@ -8,15 +8,15 @@ class QueryBuilder:
self.model = model
def get_simple_query(
self,
extra_select: Optional[list] = None,
created_by_id: Optional[int] = None,
order_by: Optional[list] = None,
only_public: Optional[bool] = False,
only_my: Optional[bool] = False,
# include_others_public: Optional[bool] = False,
url_slug: Optional[str] = None,
include_deleted: bool = False
self,
extra_select: Optional[list] = None,
created_by_id: Optional[int] = None,
order_by: Optional[list] = None,
only_public: Optional[bool] = False,
only_my: Optional[bool] = False,
include_others_public: Optional[bool] = False,
url_slug: Optional[str] = None,
include_deleted: bool = False
):
if not extra_select:
extra_select = []
@@ -37,22 +37,22 @@ class QueryBuilder:
if created_by_id and hasattr(self.model, "created_by_id"):
my_filters.append(self.model.created_by_id == created_by_id)
# others_filters = []
# if include_others_public:
# # TODO: Toto jeste neni implementovane nikde v resolverech!
# if hasattr(self.model, "is_public"):
# others_filters.append(self.model.is_public.is_(True))
#
# if hasattr(self.model, "created_by_id"):
# others_filters.append(or_(
# self.model.created_by_id != created_by_id,
# self.model.created_by_id.is_(None)
# ))
others_filters = []
if include_others_public:
# TODO: Toto jeste neni implementovane nikde v resolverech!
if hasattr(self.model, "is_public"):
others_filters.append(self.model.is_public.is_(True))
if hasattr(self.model, "created_by_id"):
others_filters.append(or_(
self.model.created_by_id != created_by_id,
self.model.created_by_id.is_(None)
))
if my_filters:
ownership_filters.append(and_(*my_filters))
# if others_filters:
# ownership_filters.append(and_(*others_filters))
if others_filters:
ownership_filters.append(and_(*others_filters))
query = query.filter(or_(*ownership_filters))
+3 -1
View File
@@ -6,7 +6,9 @@ from pydantic import EmailStr
class Email(MessageSchema):
template: str
def __init__(self, to: list[EmailStr | str], subject: str, template: str, template_variables: dict, **data: Any) -> None:
def __init__(
self, to: list[EmailStr | str], subject: str, template: str, template_variables: dict, **data: Any
) -> None:
data['subtype'] = MessageType.html
data['recipients'] = to
data['template'] = template
+1 -2
View File
@@ -1,8 +1,7 @@
from fastapi import BackgroundTasks
from pydantic import BaseModel
from starlette.responses import Response
from background_jobs.send_email import send_email
from emails.emails import WelcomeEmail, ContactFormEmail
from emails.emails import ContactFormEmail
class ContactInput(BaseModel):
-3
View File
@@ -1,8 +1,5 @@
import os
import random
import string
import binascii
from datetime import datetime, timedelta
from fastapi import BackgroundTasks
from passlib.hash import bcrypt
+133
View File
@@ -0,0 +1,133 @@
from pydantic import BaseModel
from sqlalchemy import select
from starlette.responses import Response
from database import models
from database.transaction import get_session
class SitemapURL(BaseModel):
url: str
priority: float
class SitemapEndpoint():
def get_static_routes(self):
routes = [
("", 1),
("kontakt", 0.5),
("napoveda", 0.7),
("podminky", 0.5),
("mista", 0.8),
("o-projektu", 0.8),
("osobni-udaje", 0.4),
("funkce", 0.8)
]
return [SitemapURL(url=url, priority=priority) for url, priority in routes]
async def get_public_pilots(self):
async with get_session() as db:
pilots = (await db.scalars(
select(models.User.public_username)
.filter(models.User.public_username != '')
))
return [SitemapURL(url=username, priority=0.8) for username in pilots]
async def get_public_flights(self):
async with get_session() as db:
flights = (await db.execute(
select(models.Flight, models.User.public_username)
.join(models.Flight.created_by)
.filter(models.Flight.is_public.is_(True))
.filter(models.Flight.url_slug != '')
.filter(models.User.public_username != '')
)).fetchall()
return [SitemapURL(
url=f"{pilot_username}/{flight.url_slug}",
priority=0.8
) for flight, pilot_username in flights]
async def get_public_copilots(self):
async with get_session() as db:
copilots = (await db.execute(
select(models.Copilot, models.User.public_username)
.join(models.Copilot.created_by)
.filter(models.Copilot.is_public.is_(True))
.filter(models.Copilot.url_slug != '')
.filter(models.User.public_username != '')
)).fetchall()
return [SitemapURL(
url=f"{pilot_username}/a/{copilot.url_slug}",
priority=0.5
) for copilot, pilot_username in copilots]
async def get_public_aircrafts(self):
async with get_session() as db:
aircrafts = (await db.scalars(
select(models.Aircraft.call_sign)
.filter(models.Aircraft.is_public.is_(True))
)).fetchall()
return [SitemapURL(url=f"letadlo/{call_sign}", priority=0.6) for call_sign in aircrafts]
async def get_public_places(self):
async with get_session() as db:
pois = (await db.scalars(
select(models.PointOfInterest)
.filter(models.PointOfInterest.is_public.is_(True))
.filter(models.PointOfInterest.url_slug != '')
)).fetchall()
return [SitemapURL(url=f"mista/{poi.url_slug}", priority=0.6) for poi in pois]
async def get_public_events(self):
async with get_session() as db:
data = (await db.execute(
select(models.Event, models.User.public_username)
.join(models.Event.created_by)
.filter(models.Event.is_public.is_(True))
.filter(models.User.public_username != '')
))
pilots_with_events = set()
routes = []
for event, pilot_username in data:
if pilot_username not in pilots_with_events:
routes.append(SitemapURL(url=f"{pilot_username}/udalosti", priority=0.7))
pilots_with_events.add(pilot_username)
routes.append(SitemapURL(url=f"{pilot_username}/udalosti/{event.url_slug}", priority=0.7))
return routes
async def on_get(self):
my_sitemap = """<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">"""
sitemap_entries = [
*self.get_static_routes(),
*(await self.get_public_pilots()),
*(await self.get_public_flights()),
*(await self.get_public_aircrafts()),
*(await self.get_public_events()),
*(await self.get_public_places()),
*(await self.get_public_copilots()),
]
# copilots = self.get_public_copilots()
for sitemap_entry in sitemap_entries:
my_sitemap += f"""
<url>
<loc>https://poletani.cz/{sitemap_entry.url}</loc>
<priority>{sitemap_entry.priority}</priority>
</url>
"""
my_sitemap += "</urlset>"
return Response(content=my_sitemap, media_type="application/xml")
+2 -1
View File
@@ -47,7 +47,8 @@ class Weather:
return await resp.json()
async def get_weather_for_hour(
self, date_time: datetime.datetime, gps: Tuple[float, float]) -> Dict[str, float | str]:
self, date_time: datetime.datetime, gps: Tuple[float, float]
) -> Dict[str, float | str]:
data = await self.download_weather_for_day(date_time.date(), gps)
# TODO: kontrola timezone!
+9 -9
View File
@@ -6,9 +6,9 @@ from database.query_builder import QueryBuilder
class BaseDataloader:
def __init__(
self,
model: Type[models.BaseModel],
relationship_column, filters: Optional[list] = None
self,
model: Type[models.BaseModel],
relationship_column, filters: Optional[list] = None
) -> None:
super().__init__()
self.model = model
@@ -39,12 +39,12 @@ class SingleModelByIdDataloader(BaseDataloader):
class MultiModelsDataloader(BaseDataloader):
def __init__(
self,
model: Type[models.BaseModel],
relationship_column=None,
filters: Optional[list] = None,
extra_join: Optional[list] = None,
order_by: Optional[list] = None,
self,
model: Type[models.BaseModel],
relationship_column=None,
filters: Optional[list] = None,
extra_join: Optional[list] = None,
order_by: Optional[list] = None,
):
super().__init__(model, relationship_column, filters)
+4 -4
View File
@@ -30,10 +30,10 @@ class AircraftQueries:
@error_logging
@allow_public
async def aircraft(
root, info,
id: Optional[int] = None,
call_sign: Optional[str] = None,
public: Optional[bool] = False
root, info,
id: Optional[int] = None,
call_sign: Optional[str] = None,
public: Optional[bool] = False
) -> Aircraft:
filter_params = {}
if id:
+5 -6
View File
@@ -23,13 +23,12 @@ class CopilotQueries:
@error_logging
@allow_public
async def copilot(
root, info: Info,
id: Optional[int] = None,
url_slug: Optional[str] = None,
pilot_username: Optional[str] = None,
public: Optional[bool] = False
root, info: Info,
id: Optional[int] = None,
url_slug: Optional[str] = None,
pilot_username: Optional[str] = None,
public: Optional[bool] = False
) -> Copilot:
filter_params = get_detail_filters(id, url_slug)
if pilot_username:
filter_params['pilot_username'] = pilot_username
+12 -12
View File
@@ -1,4 +1,4 @@
from typing import List, Optional
from typing import Optional
import strawberry
from database import models
from decorators.endpoints import authenticated_user_only, allow_public
@@ -17,12 +17,12 @@ class EventQueries:
@error_logging
@allow_public
async def events(
root,
info,
limit: int,
offset: int = 0,
username: Optional[str] = None,
public: Optional[bool] = False,
root,
info,
limit: int,
offset: int = 0,
username: Optional[str] = None,
public: Optional[bool] = False,
) -> PaginationWindow[Event]:
query = EventQueryResolver().get_query(
info.context.user_id,
@@ -42,11 +42,11 @@ class EventQueries:
@error_logging
@allow_public
async def event(
root, info,
id: Optional[int] = None,
url_slug: Optional[str] = None,
username: Optional[str] = None,
public: Optional[bool] = False
root, info,
id: Optional[int] = None,
url_slug: Optional[str] = None,
username: Optional[str] = None,
public: Optional[bool] = False
) -> Event:
filter_params = get_detail_filters(id, url_slug)
if username:
+14 -14
View File
@@ -16,15 +16,15 @@ class FlightQueries:
@error_logging
@allow_public
async def flights(
root, info,
limit: int,
offset: int = 0,
username: Optional[str] = None,
event_id: Optional[int] = None,
public: Optional[bool] = False,
copilot_id: Optional[int] = None,
point_of_interest_id: Optional[int] = None,
aircraft_id: Optional[int] = None,
root, info,
limit: int,
offset: int = 0,
username: Optional[str] = None,
event_id: Optional[int] = None,
public: Optional[bool] = False,
copilot_id: Optional[int] = None,
point_of_interest_id: Optional[int] = None,
aircraft_id: Optional[int] = None,
) -> PaginationWindow[Flight]:
query = FlightQueryResolver().get_query(
user_id=info.context.user_id,
@@ -47,11 +47,11 @@ class FlightQueries:
@error_logging
@allow_public
async def flight(
root, info,
id: Optional[int] = None,
url_slug: Optional[str] = None,
username: Optional[str] = None,
public: Optional[bool] = False
root, info,
id: Optional[int] = None,
url_slug: Optional[str] = None,
username: Optional[str] = None,
public: Optional[bool] = False
) -> Flight:
filter_params = get_detail_filters(id, url_slug)
if username:
@@ -5,12 +5,12 @@ from graphql_schema.entities.types.mutation_input import ComboboxInput
async def handle_combobox_save(
db: AsyncSession,
model: Type[models.BaseModel],
input: ComboboxInput,
user_id: int,
name_column: str = "name",
extra_data: Optional[dict] = None
db: AsyncSession,
model: Type[models.BaseModel],
input: ComboboxInput,
user_id: int,
name_column: str = "name",
extra_data: Optional[dict] = None
) -> int:
if input.id:
return input.id
@@ -18,10 +18,10 @@ class PaginationWindow(Generic[Item]):
async def get_pagination_window(
query: Select,
item_type: type,
limit: int,
offset: int = 0,
query: Select,
item_type: type,
limit: int,
offset: int = 0,
) -> PaginationWindow:
if limit <= 0:
raise Exception(f"limit ({limit}) must be > 0")
+2 -2
View File
@@ -1,13 +1,13 @@
from typing import List
import strawberry
from sqlalchemy import delete, select
from sqlalchemy import delete
from sqlalchemy.dialects.mysql import insert
from sqlalchemy.exc import IntegrityError
from database import models
from decorators.endpoints import authenticated_user_only
from database.transaction import get_session
from decorators.error_logging import error_logging
from graphql_schema.entities.resolvers.base import BaseQueryResolver, BaseMutationResolver
from graphql_schema.entities.resolvers.base import BaseMutationResolver
from graphql_schema.entities.resolvers.organization import OrganizationQueryResolver
from graphql_schema.entities.types.mutation_input import CreateOrganizationInput, EditOrganizationInput
from graphql_schema.entities.types.types import Organization
+7 -7
View File
@@ -15,12 +15,12 @@ class PhotoQueries:
@error_logging
@allow_public
async def photos(
root, info,
flight_id: Optional[int] = None,
copilot_id: Optional[int] = None,
point_of_interest_id: Optional[int] = None,
aircraft_id: Optional[int] = None,
public: Optional[bool] = False,
root, info,
flight_id: Optional[int] = None,
copilot_id: Optional[int] = None,
point_of_interest_id: Optional[int] = None,
aircraft_id: Optional[int] = None,
public: Optional[bool] = False,
) -> List[Photo]:
return await PhotoQueryResolver().get_list(
public=public,
@@ -35,7 +35,7 @@ class PhotoQueries:
@strawberry.field()
@error_logging
@allow_public
async def photo(root, info, id: int, public: Optional[bool] = False,) -> Photo:
async def photo(root, info, id: int, public: Optional[bool] = False, ) -> Photo:
return await BaseQueryResolver(Photo, models.Photo).get_one(
object_id=id,
user_id=info.context.user_id,
+15 -12
View File
@@ -1,7 +1,5 @@
from typing import List, Optional
from typing import Optional
import strawberry
from graphql import GraphQLError
from database import models
from decorators.endpoints import authenticated_user_only, allow_public
from database.transaction import get_session
@@ -13,19 +11,24 @@ from graphql_schema.entities.resolvers.base import BaseQueryResolver, BaseMutati
from graphql_schema.entities.types.types import PointOfInterest
from graphql_schema.entities.types.mutation_input import CreatePointOfInterestInput, EditPointOfInterestInput
@strawberry.type
class PointOfInterestQueries:
@strawberry.field()
@error_logging
@allow_public
async def points_of_interest(
root, info,
limit: int, offset: int = 0,
search: Optional[str] = None,
public: bool = False
root, info,
limit: int, offset: int = 0,
search: Optional[str] = None,
public: bool = False
) -> PaginationWindow[PointOfInterest]:
query = BaseQueryResolver(PointOfInterest, models.PointOfInterest).get_query(
info.context.user_id, only_public=public, search=search,
info.context.user_id,
only_my=bool(info.context.user_id),
include_others_public=True,
only_public=public,
search=search,
)
return await get_pagination_window(
query=query,
@@ -37,10 +40,10 @@ class PointOfInterestQueries:
@strawberry.field()
@allow_public
async def point_of_interest(
root, info,
url_slug: Optional[str] = None,
id: Optional[int] = None,
public: bool = False
root, info,
url_slug: Optional[str] = None,
id: Optional[int] = None,
public: bool = False
) -> PointOfInterest:
filter_params = get_detail_filters(id, url_slug)
@@ -16,13 +16,13 @@ class AircraftQueryResolver(BaseQueryResolver):
super().__init__(graphql_type=Aircraft, model=models.Aircraft)
def get_query(
self,
user_id: Optional[int] = None,
object_id: Optional[int] = None,
order_by: Optional[list] = None,
organization_ids: Optional[Set[int]] = None,
*args,
**kwargs,
self,
user_id: Optional[int] = None,
object_id: Optional[int] = None,
order_by: Optional[list] = None,
organization_ids: Optional[Set[int]] = None,
*args,
**kwargs,
):
filters = {}
if object_id:
+10 -8
View File
@@ -30,19 +30,21 @@ class BaseQueryResolver(BaseResolver):
return self.graphql_type(**data.as_dict())
def get_query(
self,
user_id: Optional[int] = None,
object_id: Optional[int] = None,
order_by: Optional[list] = None,
only_public: Optional[bool] = False,
only_my: Optional[bool] = False,
url_slug: Optional[str] = None,
**kwargs,
self,
user_id: Optional[int] = None,
object_id: Optional[int] = None,
order_by: Optional[list] = None,
only_public: Optional[bool] = False,
only_my: Optional[bool] = False,
include_others_public: Optional[bool] = False,
url_slug: Optional[str] = None,
**kwargs,
):
query = self.query_builder.get_simple_query(
created_by_id=user_id,
order_by=order_by,
only_public=only_public,
include_others_public=include_others_public,
only_my=only_my,
url_slug=url_slug
)
@@ -9,12 +9,12 @@ class CopilotQueryResolver(BaseQueryResolver):
super().__init__(Copilot, models.Copilot)
def get_query(
self,
user_id: Optional[int] = None,
object_id: Optional[int] = None,
order_by: Optional[list] = None,
only_public: Optional[bool] = False,
**kwargs
self,
user_id: Optional[int] = None,
object_id: Optional[int] = None,
order_by: Optional[list] = None,
only_public: Optional[bool] = False,
**kwargs
):
pilot_username = kwargs.pop("pilot_username", None)
@@ -9,13 +9,13 @@ class EventQueryResolver(BaseQueryResolver):
super().__init__(graphql_type=Event, model=models.Event)
def get_query(
self,
user_id: Optional[int] = None,
object_id: Optional[int] = None,
order_by: Optional[list] = None,
only_public: Optional[bool] = True,
*args,
**kwargs,
self,
user_id: Optional[int] = None,
object_id: Optional[int] = None,
order_by: Optional[list] = None,
only_public: Optional[bool] = True,
*args,
**kwargs,
):
query = super().get_query(
user_id, object_id,
+24 -18
View File
@@ -10,8 +10,10 @@ from database.models import flight_has_copilot
from database.transaction import get_session
from external.gpx_parser import GPXParser
from graphql_schema.entities.helpers.combobox import handle_combobox_save
from graphql_schema.entities.resolvers.base import BaseMutationResolver, BaseQueryResolver, GQL_TYPE
from graphql_schema.entities.types.mutation_input import EditFlightInput, TrackItemInput, ComboboxInput, CreateFlightInput
from graphql_schema.entities.resolvers.base import BaseMutationResolver, BaseQueryResolver
from graphql_schema.entities.types.mutation_input import (
EditFlightInput, TrackItemInput, ComboboxInput, CreateFlightInput
)
from graphql_schema.entities.types.types import Flight
from paths import FLIGHT_GPX_TRACK_PATH
from utils.file import delete_file
@@ -23,13 +25,13 @@ class FlightQueryResolver(BaseQueryResolver):
super().__init__(graphql_type=Flight, model=models.Flight)
def get_query(
self,
user_id: Optional[int] = None,
object_id: Optional[int] = None,
url_slug: Optional[str] = None,
only_public: Optional[bool] = False,
*args,
**kwargs
self,
user_id: Optional[int] = None,
object_id: Optional[int] = None,
url_slug: Optional[str] = None,
only_public: Optional[bool] = False,
*args,
**kwargs
):
filters = {}
if object_id:
@@ -77,20 +79,22 @@ class FlightMutationResolver(BaseMutationResolver):
super().__init__(Flight, models.Flight)
async def get_airport_id_by_gps(self, gps_lat: float, gps_lng: float) -> Optional[int]:
async with get_session() as db:
query = select(
models.Airport,
func.coalesce((6371 * func.acos(
async with (get_session() as db):
query = (
select(models.Airport, func.coalesce(6371 * func.acos(
func.cos(func.radians(gps_lat)) *
func.cos(func.radians(models.Airport.gps_latitude)) *
func.cos(func.radians(models.Airport.gps_longitude) - func.radians(gps_lng)) +
func.sin(func.radians(gps_lat)) *
func.sin(func.radians(models.Airport.gps_latitude))
)), 9999).label("distance")
).filter(models.Airport.use_in_gpx_guess.is_(True)).order_by("distance").having(text("distance < 1")).limit(1)
), 9999).label("distance"))
.filter(models.Airport.use_in_gpx_guess.is_(True))
.order_by("distance")
.having(text("distance < 1"))
.limit(1)
)
data = (await db.execute(query)).one_or_none()
if data:
airport, distance = data
return airport.id
@@ -151,11 +155,13 @@ class FlightMutationResolver(BaseMutationResolver):
context.background_tasks.add_task(
download_weather,
flight_id=flight.id, airport_id=flight.takeoff_airport_id, date_time=flight.takeoff_datetime, type_="takeoff"
flight_id=flight.id, airport_id=flight.takeoff_airport_id, date_time=flight.takeoff_datetime,
type_="takeoff"
)
context.background_tasks.add_task(
download_weather,
flight_id=flight.id, airport_id=flight.landing_airport_id, date_time=flight.landing_datetime, type_="landing"
flight_id=flight.id, airport_id=flight.landing_airport_id, date_time=flight.landing_datetime,
type_="landing"
)
return flight
@@ -11,11 +11,11 @@ class OrganizationQueryResolver(BaseQueryResolver):
super().__init__(Organization, models.Organization)
def get_query(
self,
object_id: Optional[int] = None,
order_by: Optional[list] = None,
**kwargs):
self,
object_id: Optional[int] = None,
order_by: Optional[list] = None,
**kwargs
):
query = (
select(models.Organization)
.filter(models.Organization.deleted.is_(False))
@@ -25,12 +25,12 @@ class PhotoQueryResolver(BaseQueryResolver):
super().__init__(Photo, models.Photo)
def get_query(
self,
user_id: Optional[int] = None,
object_id: Optional[int] = None,
order_by: Optional[list] = None,
only_public: Optional[bool] = False,
*args, **kwargs
self,
user_id: Optional[int] = None,
object_id: Optional[int] = None,
order_by: Optional[list] = None,
only_public: Optional[bool] = False,
*args, **kwargs
):
query = super().get_query(
@@ -80,7 +80,7 @@ class PhotoMutationResolver(BaseMutationResolver):
async with get_session() as db:
photo = await self._get_one(db, id, created_by_id=user_id)
flight_id = photo.flight_id
filename = photo.filename+"."+photo.filename_extension
filename = photo.filename + "." + photo.filename_extension
path = get_photo_basepath(flight_id)
return PhotoDetailInfo(
+1 -5
View File
@@ -1,11 +1,7 @@
from __future__ import annotations
from datetime import datetime
from typing import Optional, List, Any
from typing import Optional, List
import strawberry
from graphql import GraphQLError
from strawberry.permission import PermissionExtension, BasePermission
from strawberry.types import Info
from database import models
from decorators.endpoints import authenticated_user_only
from external.gpx_parser import GPXParser
@@ -44,9 +44,10 @@ def strawberry_sqlalchemy_type(model, exclude_fields: Optional[typing.Union[List
def strawberry_sqlalchemy_input(
model,
exclude_fields: Optional[typing.Union[List, typing.Tuple]] = None,
all_optional: bool = False) -> typing.Callable[[...], strawberry.object_type]:
model,
exclude_fields: Optional[typing.Union[List, typing.Tuple]] = None,
all_optional: bool = False
) -> typing.Callable[[...], strawberry.object_type]:
if exclude_fields is None:
exclude_fields = []
+33 -19
View File
@@ -16,6 +16,7 @@ from endpoints.graphql import setup_graphql_endpoint
from endpoints.login import LoginEndpoint, LoginInput, RefreshEndpoint, LogoutEndpoint
from endpoints.photo_editor_preview import PhotoEditorEndpoint
from endpoints.registration import RegistrationInput, RegistrationEndpoint
from endpoints.sitemap import SitemapEndpoint
class StaticFilesCache(StaticFiles):
@@ -111,8 +112,8 @@ class App:
@self.api_router.post("/refresh", summary="Refresh access token")
async def refresh(
resp: Response,
credentials: JwtAuthorizationCredentials = Security(self.refresh_security)
resp: Response,
credentials: JwtAuthorizationCredentials = Security(self.refresh_security)
):
return await RefreshEndpoint(
access_token=self.access_security,
@@ -126,34 +127,47 @@ class App:
refresh_token=self.refresh_security
).on_post(resp)
@self.api_router.get("/forgotten-password/token/{token}")
@self.api_router.get(
"/forgotten-password/token/{token}",
summary="Info about token used for resetting password"
)
async def token_info(token: str):
return await ForgottenPasswordEndpoint().token_info(token)
@self.api_router.post("/forgotten-password/request")
async def password_reset(input: ForgottenPasswordRequest, background_tasks: BackgroundTasks):
@self.api_router.post(
"/forgotten-password/request",
summary="Request password change, e-mail will be sent to validate your request."
)
async def request_password_change(input: ForgottenPasswordRequest, background_tasks: BackgroundTasks):
return await ForgottenPasswordEndpoint().request(input, background_tasks)
@self.api_router.post("/forgotten-password/reset")
async def password_reset(input: ChangeForgottenPassword):
@self.api_router.post(
"/forgotten-password/reset",
summary="Set new password after successfull token validation"
)
async def reset_password(input: ChangeForgottenPassword):
return await ForgottenPasswordEndpoint().change_password(input)
@self.api_router.post("/contact", summary="Send email from contact form")
async def contact(input: ContactInput, background_tasks: BackgroundTasks):
async def contact_form_message(input: ContactInput, background_tasks: BackgroundTasks):
return await ContactEndpoint().on_post(input, background_tasks)
@self.api_router.get("/photo/editor-preview/{photo_id}")
@self.api_router.get("/sitemap.xml")
async def sitemap():
return await SitemapEndpoint().on_get()
@self.api_router.get("/photo/editor-preview/{photo_id}", summary="Photo editor preview")
async def photo_editor_preview(
photo_id: int,
brightness: Optional[float] = None,
contrast: Optional[float] = None,
saturation: Optional[float] = None,
sharpness: Optional[float] = None,
rotate: Optional[float] = None,
crop_left: Optional[float] = None,
crop_top: Optional[float] = None,
crop_width: Optional[float] = None,
crop_height: Optional[float] = None,
photo_id: int,
brightness: Optional[float] = None,
contrast: Optional[float] = None,
saturation: Optional[float] = None,
sharpness: Optional[float] = None,
rotate: Optional[float] = None,
crop_left: Optional[float] = None,
crop_top: Optional[float] = None,
crop_width: Optional[float] = None,
crop_height: Optional[float] = None,
):
return await PhotoEditorEndpoint(
access_token=self.access_security,
+1 -1
View File
@@ -39,7 +39,7 @@ def get_photo_thumbnail_url(root) -> str:
log.warning(f"Missing thumbnail {thumbnail} in flight ID={root.flight_id}")
# TODO: doplnit chybejici nahled, tohle by se ale nikdy nemelo stat! Vzdy musi existovat alespon originalni fotka
return get_public_url(f"photos/missing-thumbnail.webp")
return get_public_url("photos/missing-thumbnail.webp")
def get_avatar_url(user) -> str:
+7 -7
View File
@@ -101,11 +101,11 @@ class PhotoEditor:
return self
def adjust(
self,
brightness: Optional[float] = None,
contrast: Optional[float] = None,
saturation: Optional[float] = None,
sharpness: Optional[float] = None
self,
brightness: Optional[float] = None,
contrast: Optional[float] = None,
saturation: Optional[float] = None,
sharpness: Optional[float] = None
):
adjustments = [
(Brightness, brightness),
@@ -127,8 +127,8 @@ class PhotoEditor:
return img_io
def write_to_file(
self, quality: int = 90, dest_path: Optional[str] = None, dest_filename: Optional[str] = None,
format_: Optional[str] = "JPEG"
self, quality: int = 90, dest_path: Optional[str] = None, dest_filename: Optional[str] = None,
format_: Optional[str] = "JPEG"
) -> str:
check_directories(dest_path or self.path)
+8 -3
View File
@@ -2,18 +2,23 @@ import os
import re
import uuid
from strawberry.file_uploads import Upload
from utils.file import check_directories
async def handle_file_upload(file: Upload, path: str, filename_maxlength: int = 64, uid_prefix: bool = True, overwrite: bool = True) -> str:
async def handle_file_upload(
file: Upload,
path: str,
filename_maxlength: int = 64,
uid_prefix: bool = True,
overwrite: bool = True
) -> str:
check_directories(path)
prefix = f"{uuid.uuid4()}-" if uid_prefix else ""
filename = f"{prefix}{file.filename}"[-1 * filename_maxlength:]
# sanitize filename
filename = re.sub('[^\w_. -]', '', filename).replace(" ", "-")
filename = re.sub(r'[^\w_. -]', '', filename).replace(" ", "-")
target_path = f"{path}/{filename}"