Error logging, uprava dotazovani na seznam letu u kopilota

This commit is contained in:
Michal Kváček
2023-11-07 17:45:26 +01:00
parent e3ab6ba7f0
commit bd5b84c9e7
17 changed files with 88 additions and 136 deletions
-1
View File
@@ -7,7 +7,6 @@ from logger import log
async def download_weather(date_time: datetime, flight_id: int, airport_id: int, type_: Literal['landing', 'takeoff']):
log.error("AAAAAAAAAAAAAAAAAAAAAAAAAAA")
async with get_session() as db:
airport = await models.Airport.get_one(db, airport_id)
gps = (airport.gps_latitude, airport.gps_longitude)
+4
View File
@@ -1,4 +1,5 @@
from functools import wraps
from fastapi import HTTPException
from graphql import GraphQLError
from sqlalchemy.exc import NoResultFound
@@ -10,5 +11,8 @@ def error_logging(func):
return await func(*args, **kwargs)
except NoResultFound as e:
raise GraphQLError("Not found", original_error=e)
except HTTPException as e:
if e.status_code == 401:
raise GraphQLError("Not authorized", original_error=e)
return decorator
@@ -29,6 +29,17 @@ flights_by_copilot_dataloader = DataLoader(
cache=False
)
public_flights_by_copilot_dataloader = DataLoader(
load_fn=MultiModelsDataloader(
models.Flight,
relationship_column=models.Copilot.id,
extra_join=[models.Flight.copilots],
filters=[models.Flight.is_public.is_(True)],
order_by=[models.Flight.takeoff_datetime.desc()]
).load,
cache=False
)
flights_by_aircraft_dataloader = DataLoader(
load_fn=MultiModelsDataloader(
models.Flight,
+5
View File
@@ -1,6 +1,7 @@
from typing import List
import strawberry
from decorators.endpoints import authenticated_user_only
from decorators.error_logging import error_logging
from .resolvers.aircraft import AircraftMutationResolver, AircraftQueryResolver
from graphql_schema.entities.types.mutation_input import CreateAircraftInput, EditAircraftInput
from graphql_schema.entities.types.types import Aircraft
@@ -9,6 +10,7 @@ from graphql_schema.entities.types.types import Aircraft
@strawberry.type
class AircraftQueries:
@strawberry.field()
@error_logging
@authenticated_user_only()
async def aircrafts(root, info) -> List[Aircraft]:
return await AircraftQueryResolver().get_list(
@@ -17,6 +19,7 @@ class AircraftQueries:
)
@strawberry.field()
@error_logging
@authenticated_user_only()
async def aircraft(root, info, id: int) -> Aircraft:
return await AircraftQueryResolver().get_one(
@@ -27,11 +30,13 @@ class AircraftQueries:
@strawberry.type
class AircraftMutation:
@strawberry.mutation
@error_logging
@authenticated_user_only()
async def create_aircraft(root, info, input: CreateAircraftInput) -> Aircraft:
return await AircraftMutationResolver().create_new(input, info.context.user_id)
@strawberry.mutation
@error_logging
@authenticated_user_only()
async def edit_aircraft(root, info, id: int, input: EditAircraftInput) -> Aircraft:
return await AircraftMutationResolver().edit(id, user_id=info.context.user_id, data=input)
+3
View File
@@ -1,6 +1,7 @@
from typing import List
import strawberry
from database import models
from decorators.error_logging import error_logging
from decorators.endpoints import authenticated_user_only
from graphql_schema.entities.resolvers.base import BaseQueryResolver
from graphql_schema.entities.types.types import Airport
@@ -9,11 +10,13 @@ from graphql_schema.entities.types.types import Airport
@strawberry.type
class AirportQueries:
@strawberry.field()
@error_logging
@authenticated_user_only()
async def airports(root, info) -> List[Airport]:
return await BaseQueryResolver(Airport, models.Airport).get_list(info.context.user_id)
@strawberry.field()
@error_logging
@authenticated_user_only()
async def airport(root, info, id: int) -> Airport:
return await BaseQueryResolver(Airport, models.Airport).get_one(id, user_id=info.context.user_id)
+8 -3
View File
@@ -2,8 +2,9 @@ from typing import List, Optional
import strawberry
from fastapi import HTTPException
from starlette.status import HTTP_401_UNAUTHORIZED
from strawberry.types import Info
from database import models
from decorators.error_logging import error_logging
from decorators.endpoints import authenticated_user_only
from graphql_schema.entities.resolvers.base import BaseMutationResolver
from graphql_schema.entities.resolvers.copilot import CopilotQueryResolver
@@ -14,12 +15,14 @@ from graphql_schema.entities.types.types import Copilot
@strawberry.type
class CopilotQueries:
@strawberry.field()
@error_logging
@authenticated_user_only()
async def copilots(root, info) -> List[Copilot]:
async def copilots(root, info: Info) -> List[Copilot]:
return await CopilotQueryResolver().get_list(info.context.user_id)
@strawberry.field()
async def copilot(root, info, id: int, pilot_username: Optional[str] = None) -> Copilot:
@error_logging
async def copilot(root, info: Info, id: int, pilot_username: Optional[str] = None) -> Copilot:
if not info.context.user_id and not pilot_username:
raise HTTPException(HTTP_401_UNAUTHORIZED)
@@ -33,6 +36,7 @@ class CopilotQueries:
@strawberry.type
class CopilotMutation:
@strawberry.mutation
@error_logging
@authenticated_user_only()
async def create_copilot(root, info, input: CreateCopilotInput) -> Copilot:
return await BaseMutationResolver(Copilot, models.Copilot).create(
@@ -41,6 +45,7 @@ class CopilotMutation:
)
@strawberry.mutation
@error_logging
@authenticated_user_only()
async def edit_copilot(root, info, id: int, input: EditCopilotInput) -> Copilot:
return await BaseMutationResolver(Copilot, models.Copilot).update(id, input, info.context.user_id)
+5
View File
@@ -4,6 +4,7 @@ from fastapi import HTTPException
from starlette.status import HTTP_401_UNAUTHORIZED
from database import models
from decorators.endpoints import authenticated_user_only
from decorators.error_logging import error_logging
from graphql_schema.entities.resolvers.base import BaseMutationResolver
from graphql_schema.entities.resolvers.event import EventQueryResolver
from graphql_schema.entities.types.mutation_input import CreateEventInput, EditEventInput
@@ -13,6 +14,7 @@ from graphql_schema.entities.types.types import Event
@strawberry.type
class EventQueries:
@strawberry.field()
@error_logging
async def events(root, info, username: Optional[str] = None) -> List[Event]:
if not info.context.user_id and not username:
raise HTTPException(HTTP_401_UNAUTHORIZED)
@@ -24,6 +26,7 @@ class EventQueries:
)
@strawberry.field()
@error_logging
async def event(root, info, id: int, username: Optional[str] = None) -> Event:
if not info.context.user_id and not username:
raise HTTPException(HTTP_401_UNAUTHORIZED)
@@ -38,11 +41,13 @@ class EventQueries:
@strawberry.type
class EventMutation:
@strawberry.mutation
@error_logging
@authenticated_user_only()
async def create_event(root, info, input: CreateEventInput) -> Event:
return await BaseMutationResolver(Event, models.Event).create(input.to_dict(), info.context.user_id)
@strawberry.mutation
@error_logging
@authenticated_user_only()
async def edit_event(root, info, id: int, input: EditEventInput) -> Event:
return await BaseMutationResolver(Event, models.Event).update(id, input, info.context.user_id)
+4
View File
@@ -18,6 +18,7 @@ from graphql_schema.entities.types.types import Flight
class FlightQueries:
@strawberry.field()
@error_logging
async def flights(root, info, username: Optional[str] = None) -> List[Flight]:
if not info.context.user_id and not username:
raise HTTPException(HTTP_401_UNAUTHORIZED)
@@ -36,6 +37,7 @@ class FlightQueries:
@strawberry.type
class FlightMutation:
@strawberry.mutation
@error_logging
@authenticated_user_only()
async def create_flight(self, info, input: CreateFlightInput) -> Flight:
data = input.to_dict()
@@ -77,11 +79,13 @@ class FlightMutation:
return flight
@strawberry.mutation
@error_logging
@authenticated_user_only()
async def edit_flight(self, info, id: int, input: EditFlightInput) -> Flight:
return await FlightMutationResolver().update(info.context, id, input)
@strawberry.mutation
@error_logging
@authenticated_user_only()
async def delete_flight(self, info, id: int) -> Flight:
return await FlightMutationResolver().delete(info.context.user_id, id)
@@ -6,6 +6,7 @@ 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.types.mutation_input import CreateOrganizationInput, EditOrganizationInput
from graphql_schema.entities.types.types import Organization
@@ -14,6 +15,7 @@ from graphql_schema.entities.types.types import Organization
@strawberry.type
class OrganizationQueries:
@strawberry.field()
@error_logging
@authenticated_user_only()
async def organizations(root, info) -> List[Organization]:
return await BaseQueryResolver(Organization, models.Organization).get_list(
@@ -21,6 +23,7 @@ class OrganizationQueries:
)
@strawberry.field()
@error_logging
@authenticated_user_only()
async def organization(root, info, id: int) -> Organization:
return await BaseQueryResolver(Organization, models.Organization).get_one(id, info.context.user_id)
@@ -30,6 +33,7 @@ class OrganizationQueries:
class OrganizationMutation:
@strawberry.mutation
@error_logging
@authenticated_user_only()
async def create_organization(root, info, input: CreateOrganizationInput) -> Organization:
return await BaseMutationResolver(Organization, models.Organization).create(
@@ -38,6 +42,7 @@ class OrganizationMutation:
)
@strawberry.mutation
@error_logging
@authenticated_user_only()
async def edit_organization(root, info, id: int, input: EditOrganizationInput) -> Organization:
async with get_session() as db:
@@ -55,6 +60,7 @@ class OrganizationMutation:
class OrganizationUserMutation:
@strawberry.mutation
@error_logging
@authenticated_user_only()
async def add_to_organization(root, info, organization_id: int) -> Organization:
async with get_session() as db:
@@ -75,6 +81,7 @@ class OrganizationUserMutation:
return Organization(**organization.as_dict())
@strawberry.mutation
@error_logging
@authenticated_user_only()
async def remove_from_organization(root, info, organization_id: int) -> Organization:
async with get_session() as db:
+8
View File
@@ -2,6 +2,7 @@ from typing import List
import strawberry
from database import models
from decorators.endpoints import authenticated_user_only
from decorators.error_logging import error_logging
from graphql_schema.entities.resolvers.base import BaseQueryResolver
from graphql_schema.entities.resolvers.photo import PhotoMutationResolver
from graphql_schema.entities.types.types import Photo
@@ -11,10 +12,12 @@ from graphql_schema.entities.types.mutation_input import EditPhotoInput, UploadP
@strawberry.type
class PhotoQueries:
@strawberry.field()
@error_logging
async def photos(root, info) -> List[Photo]:
return await BaseQueryResolver(Photo, models.Photo).get_list(user_id=info.context.user_id)
@strawberry.field()
@error_logging
async def photo(root, info, id: int) -> Photo:
return await BaseQueryResolver(Photo, models.Photo).get_one(id, user_id=info.context.user_id)
@@ -22,16 +25,19 @@ class PhotoQueries:
@strawberry.type
class PhotoMutation:
@strawberry.mutation
@error_logging
@authenticated_user_only()
async def upload_photo(self, info, input: UploadPhotoInput) -> Photo:
return await PhotoMutationResolver().upload(info, input)
@strawberry.mutation()
@error_logging
@authenticated_user_only()
async def edit_photo(self, info, id: int, input: EditPhotoInput) -> Photo:
return await PhotoMutationResolver().update(id, input, info.context.user_id)
@strawberry.mutation()
@error_logging
@authenticated_user_only()
async def change_orientation(self, info, id: int, direction: str) -> Photo:
return await PhotoMutationResolver().change_orientation(
@@ -42,11 +48,13 @@ class PhotoMutation:
)
@strawberry.mutation()
@error_logging
@authenticated_user_only()
async def adjust_photo(self, info, id: int, adjustment: AdjustmentInput) -> Photo:
return await PhotoMutationResolver().adjust(id, info=info, user_id=info.context.user_id, adjustment=adjustment)
@strawberry.mutation()
@error_logging
@authenticated_user_only()
async def delete_photo(self, info, id: int) -> Photo:
return await PhotoMutationResolver().delete(user_id=info.context.user_id, id=id)
+5
View File
@@ -3,6 +3,7 @@ import strawberry
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.helpers.combobox import handle_combobox_save
from graphql_schema.entities.resolvers.base import BaseQueryResolver, BaseMutationResolver
from graphql_schema.entities.types.types import PointOfInterest
@@ -12,6 +13,7 @@ from graphql_schema.entities.types.mutation_input import CreatePointOfInterestIn
@strawberry.type
class PointOfInterestQueries:
@strawberry.field()
@error_logging
@authenticated_user_only()
async def points_of_interest(root, info) -> List[PointOfInterest]:
return await BaseQueryResolver(PointOfInterest, models.PointOfInterest).get_list(info.context.user_id)
@@ -25,6 +27,7 @@ class PointOfInterestQueries:
@strawberry.type
class PointOfInterestMutation:
@strawberry.mutation
@error_logging
@authenticated_user_only()
async def create_point_of_interest(root, info, input: CreatePointOfInterestInput) -> PointOfInterest:
input_data = input.to_dict()
@@ -40,6 +43,7 @@ class PointOfInterestMutation:
)
@strawberry.mutation
@error_logging
@authenticated_user_only()
async def edit_point_of_interest(root, info, id: int, input: EditPointOfInterestInput) -> PointOfInterest:
input_data = input.to_dict()
@@ -59,6 +63,7 @@ class PointOfInterestMutation:
return PointOfInterest(**updated_poi.as_dict())
@strawberry.mutation
@error_logging
@authenticated_user_only()
async def delete_point_of_interest(self, info, id: int) -> PointOfInterest:
return await BaseMutationResolver(PointOfInterest, models.PointOfInterest).delete(info.context.user_id, id=id)
+3
View File
@@ -2,6 +2,7 @@ from typing import List
import strawberry
from database import models
from decorators.endpoints import authenticated_user_only
from decorators.error_logging import error_logging
from graphql_schema.entities.resolvers.base import BaseQueryResolver
from graphql_schema.entities.types.types import PointOfInterestType
@@ -10,11 +11,13 @@ from graphql_schema.entities.types.types import PointOfInterestType
class PointOfInterestTypeQueries:
@strawberry.field()
@error_logging
@authenticated_user_only()
async def point_of_interest_types(root, info) -> List[PointOfInterestType]:
return await BaseQueryResolver(PointOfInterestType, models.PointOfInterestType).get_list(info.context.user_id)
@strawberry.field()
@error_logging
@authenticated_user_only()
async def point_of_interest_type(root, info, id: int) -> PointOfInterestType:
return await BaseQueryResolver(PointOfInterestType, models.PointOfInterestType).get_one(
+9 -2
View File
@@ -10,7 +10,7 @@ from graphql_schema.dataloaders.multi_models import (
poi_photos_dataloader, flight_by_poi_dataloader, flight_copilots_dataloader, flight_track_dataloader,
photos_dataloader, flights_by_aircraft_dataloader, users_in_organization_dataloader,
aircrafts_from_organization_dataloader, user_organizations_dataloader, flights_by_event_dataloader,
flights_by_copilot_dataloader, public_flights_by_event_dataloader
flights_by_copilot_dataloader, public_flights_by_event_dataloader, public_flights_by_copilot_dataloader
)
from graphql_schema.dataloaders.single_model import (
poi_dataloader, poi_type_dataloader, event_dataloader, aircraft_dataloader, airport_dataloader, cover_photo_loader,
@@ -145,7 +145,14 @@ class Flight:
@strawberry_sqlalchemy_type(models.Copilot)
class Copilot:
flights: List[Flight] = strawberry.field(resolver=lambda root: flights_by_copilot_dataloader.load(root.id))
async def resolve_flights(root, info):
dataloader = public_flights_by_copilot_dataloader
if info.context.user_id:
dataloader = flights_by_event_dataloader
return await dataloader.load(root.id)
flights: List[Flight] = strawberry.field(resolver=resolve_flights)
@strawberry_sqlalchemy_type(models.Aircraft)
+3 -3
View File
@@ -4,13 +4,13 @@ from graphql import GraphQLError
from passlib.hash import bcrypt
from sqlalchemy import select
from strawberry.file_uploads import Upload
from background_jobs.photo import resize_photo
from database import models
from decorators.endpoints import authenticated_user_only
from decorators.error_logging import error_logging
from database.transaction import get_session
from graphql_schema.entities.types.types import User
from utils.file import delete_file
from utils.image import resize_image
from utils.upload import handle_file_upload
@@ -72,7 +72,7 @@ class EditUserMutation:
data['avatar_image_filename'] = await handle_file_upload(input.avatar_image, user_image_path)
info.context.background_tasks.add_task(
resize_image, path=user_image_path, filename=data['avatar_image_filename'], new_width=400
resize_photo, path=user_image_path, filename=data['avatar_image_filename'], new_width=400
)
if input.title_image:
@@ -81,7 +81,7 @@ class EditUserMutation:
data['title_image_filename'] = await handle_file_upload(input.title_image, user_image_path)
info.context.background_tasks.add_task(
resize_image, path=user_image_path, filename=data['title_image_filename'], new_width=800
resize_photo, path=user_image_path, filename=data['title_image_filename'], new_width=800
)
if input.old_password and input.new_password:
+6 -1
View File
@@ -3,6 +3,7 @@ from datetime import timedelta
from typing import Optional
from fastapi import FastAPI, APIRouter, Depends, Security
from fastapi_jwt import JwtAuthorizationCredentials, JwtAccessBearerCookie, JwtRefreshBearerCookie
from graphql import GraphQLError
from sqlalchemy import select
from starlette.background import BackgroundTasks
from starlette.middleware.cors import CORSMiddleware
@@ -35,7 +36,11 @@ class App:
def create_app(self):
if SENTRY_DSN:
sentry_sdk.init(dsn=SENTRY_DSN, enable_tracing=True)
sentry_sdk.init(
dsn=SENTRY_DSN,
enable_tracing=True,
ignore_errors = [GraphQLError]
)
app = FastAPI()
+4 -2
View File
@@ -10,5 +10,7 @@ def delete_file(path: str, silent: bool = False):
def check_directories(path: str):
if not os.path.isdir(path):
os.makedirs(path)
try:
os.makedirs(path, exist_ok=True)
except OSError:
pass
+3 -124
View File
@@ -1,10 +1,9 @@
import io
import math
from datetime import datetime
from typing import Optional, Tuple
import exif
from PIL import Image, UnidentifiedImageError
from PIL import Image
from PIL.ImageEnhance import Brightness, Contrast, Color, Sharpness
from utils.file import check_directories
from utils.gps import gps_to_decimal
@@ -130,129 +129,9 @@ class PhotoEditor:
def write_to_file(
self, quality: int = 90, dest_path: Optional[str] = None, dest_filename: Optional[str] = None
) -> str:
check_directories(dest_path or self.path)
dest = f"{dest_path or self.path}/{dest_filename or self.filename}"
check_directories(dest)
self.img.save(dest, 'JPEG', quality=quality)
return dest
# -----------
# odsud niz to bude vse asi na smazani, vse by mela umet trida PhotoEditor
async def resize_image(
path: str, filename: str, new_width: int, quality: int = 90,
dest_path: Optional[str] = None,
dest_filename: Optional[str] = None
):
if not dest_path:
dest_path = path
if not dest_filename:
dest_filename = filename
try:
image = Image.open(f"{path}/{filename}")
width, height = image.size
new_height = int(new_width * height / width)
image = image.resize((new_width, new_height), Image.LANCZOS)
check_directories(dest_path)
image.save(f"{dest_path}/{dest_filename}", 'JPEG', quality=quality)
return new_width, new_height
except UnidentifiedImageError:
pass
def crop_image_after_rotate(
original_width: int, original_height: int, rotated_width: int, rotated_height: int, degrees: float
) -> Tuple[int, int]:
aspect_ratio = float(original_width) / original_height
rotated_aspect_ratio = float(rotated_width) / rotated_height
angle = math.fabs(degrees) * math.pi / 180
if aspect_ratio < 1:
total_height = float(original_width) / rotated_aspect_ratio
else:
total_height = float(original_height)
h = total_height / (aspect_ratio * math.sin(angle) + math.cos(angle))
w = h * aspect_ratio
return round(w), round(h)
async def rotate_image_no_crop(
path: str,
filename: str,
angle: float,
dest_path: Optional[str] = None,
dest_filename: Optional[str] = None
):
if not dest_path:
dest_path = path
if not dest_filename:
dest_filename = filename
img = Image.open(f"{path}/{filename}")
rotated_img = img.rotate(angle, Image.BICUBIC, expand=True)
check_directories(dest_path)
rotated_img.save(f"{dest_path}/{dest_filename}", 'JPEG', quality=100)
async def adjust_image(
path: str,
filename: str,
rotate: float,
brightness: float,
contrast: float,
saturation: float,
sharpness: float,
crop: dict,
dest_path: Optional[str] = None,
dest_filename: Optional[str] = None
):
if not dest_path:
dest_path = path
if not dest_filename:
dest_filename = filename
img = Image.open(f"{path}/{filename}")
if rotate:
rotated_img = img.rotate(rotate, Image.BICUBIC, expand=True)
width, height = img.size
rotated_width, rotated_height = rotated_img.size
new_width, new_height = crop_image_after_rotate(width, height, rotated_width, rotated_height, rotate)
left = round((rotated_width - new_width) / 2)
top = round((rotated_height - new_height) / 2)
img = rotated_img.crop((left, top, left + new_width, top + new_height))
adjustments = [
(Brightness, brightness),
(Contrast, contrast),
(Color, saturation),
(Sharpness, sharpness)
]
for adj, value in adjustments:
img = adj(img).enhance(value)
if crop:
w, h = img.size
left = crop['left'] * w
top = crop['top'] * h
width = crop['width'] * w
height = crop['height'] * h
img = img.crop((left, top, left + width, top + height))
img.save(f"{dest_path}/{dest_filename}", 'JPEG', quality=100)