Resize obrazku, deploy, bugfixing
This commit is contained in:
+13
-8
@@ -1,15 +1,20 @@
|
||||
import os
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
|
||||
def create_db_engine(
|
||||
user: str = "root",
|
||||
password: str = "",
|
||||
host: str = "db",
|
||||
port: int = 3306,
|
||||
database: str = "ull_tracker",
|
||||
):
|
||||
database_url = f'mysql+aiomysql://{user}:{password}@{host}:{port}/{database}?charset=utf8'
|
||||
def get_database_url():
|
||||
user = os.environ.get("MYSQL_USER", "root")
|
||||
password = os.environ.get("MYSQL_PASSWORD", "")
|
||||
host = os.environ.get("MYSQL_HOST", "db")
|
||||
port = int(os.environ.get("MYSQL_PORT", 3306))
|
||||
database = os.environ.get("MYSQL_DATABASE", "ull_tracker")
|
||||
|
||||
return f'mysql+aiomysql://{user}:{password}@{host}:{port}/{database}?charset=utf8'
|
||||
|
||||
|
||||
def create_db_engine():
|
||||
database_url = get_database_url()
|
||||
return create_async_engine(database_url, future=True, echo=True)
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ def authenticated_user_only(raise_when_unauthorized: bool = True, return_value_u
|
||||
raise HTTPException(HTTP_401_UNAUTHORIZED, "Not authorized")
|
||||
else:
|
||||
return return_value_unauthorized
|
||||
|
||||
return await func(*args, **kwargs)
|
||||
|
||||
return decorator
|
||||
|
||||
@@ -5,5 +5,5 @@ async def db_session():
|
||||
async with async_session() as session:
|
||||
async with session.begin():
|
||||
yield session
|
||||
await session.commit()
|
||||
await session.flush()
|
||||
await session.commit()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
from typing import List, Optional, Annotated, TYPE_CHECKING, Tuple
|
||||
import strawberry
|
||||
@@ -161,14 +162,21 @@ class CreateFlightMutation:
|
||||
@authenticated_user_only()
|
||||
async def create_flight(self, info, input: CreateFlightInput) -> Flight:
|
||||
db = info.context.db
|
||||
aircraft_id = await handle_aircraft_save(db, info.context.user_id, input.aircraft)
|
||||
|
||||
data = input.to_dict()
|
||||
takeoff_airport, landing_airport = await get_airports(db, input.takeoff_airport.id, input.landing_airport.id)
|
||||
weather_takeoff = await handle_weather_info(db, data['takeoff_datetime'], takeoff_airport)
|
||||
weather_landing = await handle_weather_info(db, data['landing_datetime'], landing_airport)
|
||||
|
||||
return await models.Flight.create(db, data={
|
||||
takeoff_airport, landing_airport = await get_airports(db, input.takeoff_airport.id, input.landing_airport.id)
|
||||
|
||||
aircraft_id = await handle_aircraft_save(db, info.context.user_id, input.aircraft)
|
||||
weather_takeoff,weather_landing = await asyncio.gather(
|
||||
handle_weather_info(db, data['takeoff_datetime'], takeoff_airport),
|
||||
handle_weather_info(db, data['landing_datetime'], landing_airport)
|
||||
)
|
||||
await db.flush()
|
||||
|
||||
print("XXXXXXXXXXXXX", weather_takeoff, weather_takeoff.id)
|
||||
|
||||
|
||||
flight = await models.Flight.create(db, data={
|
||||
**data,
|
||||
"takeoff_weather_info_id": weather_takeoff.id,
|
||||
"landing_weather_info_id": weather_landing.id,
|
||||
@@ -177,6 +185,7 @@ class CreateFlightMutation:
|
||||
"aircraft_id": aircraft_id,
|
||||
"created_by_id": info.context.user_id
|
||||
})
|
||||
return flight
|
||||
|
||||
|
||||
@strawberry.type
|
||||
|
||||
@@ -16,7 +16,7 @@ weather_api = Weather()
|
||||
async def handle_weather_info(db: AsyncSession, date_time: datetime, airport: models.Airport) -> models.WeatherInfo:
|
||||
weather = await weather_api.get_weather_for_hour(date_time.astimezone(), (airport.gps_latitude, airport.gps_longitude))
|
||||
|
||||
return await models.WeatherInfo.create(db_session=db, data={
|
||||
model = models.WeatherInfo(**{
|
||||
"datetime": weather['datetime'],
|
||||
"qnh": weather['pressure_msl'],
|
||||
"temperature_surface": weather['temperature_2m'],
|
||||
@@ -27,6 +27,9 @@ async def handle_weather_info(db: AsyncSession, date_time: datetime, airport: mo
|
||||
"wind_speed_surface": weather['windspeed_10m'],
|
||||
"wind_direction_surface": weather['winddirection_10m'],
|
||||
})
|
||||
db.add(model)
|
||||
|
||||
return model
|
||||
|
||||
|
||||
async def handle_track_edit(db: AsyncSession, flight: models.Flight, track: List[ComboboxInput], user_id: int):
|
||||
@@ -48,7 +51,7 @@ async def handle_track_edit(db: AsyncSession, flight: models.Flight, track: List
|
||||
poi_object = poi_map.get(item.id)
|
||||
|
||||
if not poi_object:
|
||||
poi_object = await models.PointOfInterest.create(db, data=dict(created_by_id=user_id, name=item.name))
|
||||
poi_object = await models.PointOfInterest.create(db, data=dict(created_by_id=user_id, name=item.name, description=""))
|
||||
await db.flush()
|
||||
|
||||
await models.FlightTrack.create(
|
||||
@@ -63,19 +66,15 @@ async def handle_track_edit(db: AsyncSession, flight: models.Flight, track: List
|
||||
|
||||
|
||||
async def handle_aircraft_save(db: AsyncSession, user_id: int, aircraft: ComboboxInput):
|
||||
if aircraft.id:
|
||||
return aircraft.id
|
||||
else:
|
||||
obj = await models.Aircraft.create(db, {
|
||||
"call_sign": aircraft.name,
|
||||
return await handle_combobox_save(
|
||||
db, models.Aircraft, aircraft, user_id,
|
||||
name_column="call_sign",
|
||||
extra_data={
|
||||
"description": "",
|
||||
"model": "",
|
||||
"seats": 2,
|
||||
"manufacturer": "",
|
||||
"created_by_id": user_id
|
||||
|
||||
})
|
||||
await db.flush()
|
||||
return obj.id
|
||||
|
||||
|
||||
async def get_airports(db, takeoff_airport_id: int, landing_airport_id: int) -> Tuple[models.Airport, models.Airport]:
|
||||
@@ -127,15 +126,21 @@ async def handle_copilots_edit(db: AsyncSession, copilots: List[ComboboxInput],
|
||||
|
||||
|
||||
async def handle_combobox_save(
|
||||
db: AsyncSession, model: Type[models.BaseModel],
|
||||
db: AsyncSession,
|
||||
model: Type[models.BaseModel],
|
||||
input: ComboboxInput,
|
||||
user_id: int,
|
||||
name_column: str = "name"
|
||||
name_column: str = "name",
|
||||
extra_data: Optional[dict] = None
|
||||
) -> int:
|
||||
if input.id:
|
||||
return input.id
|
||||
else:
|
||||
data = {name_column: input.name}
|
||||
|
||||
if not extra_data:
|
||||
extra_data = {}
|
||||
|
||||
data = {name_column: input.name, **extra_data}
|
||||
if hasattr(model, "created_by_id"):
|
||||
data["created_by_id"] = user_id
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ from strawberry.file_uploads import Upload
|
||||
from database import models
|
||||
from decorators.endpoints import authenticated_user_only
|
||||
from graphql_schema.sqlalchemy_to_strawberry_type import strawberry_sqlalchemy_type, strawberry_sqlalchemy_input
|
||||
from upload_utils import get_public_url, handle_file_upload, delete_file, parse_exif_info, generate_thumbnail, file_exists
|
||||
from upload_utils import get_public_url, handle_file_upload, delete_file, parse_exif_info, generate_thumbnail, file_exists, resize_image
|
||||
|
||||
|
||||
@strawberry_sqlalchemy_type(models.Photo)
|
||||
@@ -57,7 +57,9 @@ class UploadPhotoMutation:
|
||||
async def upload_photo(self, info, input: UploadPhotoInput) -> Photo:
|
||||
path = get_photo_basepath(input.flight_id)
|
||||
filename = await handle_file_upload(input.photo, path)
|
||||
info.context.background_tasks.add_task(generate_thumbnail, path=path, filename=filename, size=(300, 200))
|
||||
|
||||
info.context.background_tasks.add_task(resize_image, path=path, filename=filename, new_width=2500)
|
||||
info.context.background_tasks.add_task(generate_thumbnail, path=path, filename=filename, new_width=300)
|
||||
|
||||
exif_info = await parse_exif_info(path, filename)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ from database.models import User
|
||||
from decorators.endpoints import authenticated_user_only
|
||||
from decorators.error_logging import error_logging
|
||||
from graphql_schema.sqlalchemy_to_strawberry_type import strawberry_sqlalchemy_type, strawberry_sqlalchemy_input
|
||||
from upload_utils import handle_file_upload, delete_file, get_public_url
|
||||
from upload_utils import handle_file_upload, delete_file, get_public_url, resize_image
|
||||
|
||||
|
||||
@strawberry_sqlalchemy_type(User, exclude_fields=['password_hashed'])
|
||||
@@ -81,6 +81,9 @@ class EditUserMutation:
|
||||
delete_file(f"{user_image_path}/{user.avatar_image_filename}", silent=True)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
if input.title_image:
|
||||
if user.title_image_filename:
|
||||
@@ -88,6 +91,10 @@ 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
|
||||
)
|
||||
|
||||
if input.old_password and input.new_password:
|
||||
if not bcrypt.verify(input.old_password, user.password_hashed):
|
||||
raise GraphQLError("Bad password")
|
||||
|
||||
+33
-4
@@ -62,15 +62,44 @@ async def parse_exif_info(path: str, filename: str) -> dict:
|
||||
return exif_info
|
||||
|
||||
|
||||
async def generate_thumbnail(path: str, filename: str, size: Tuple[int, int]):
|
||||
async def resize_image(path: str, filename: str, new_width: int, quality: int = 90, dest_path: str = None, dest_filename: str = None):
|
||||
print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
|
||||
if not dest_path:
|
||||
dest_path = path
|
||||
|
||||
if not dest_filename:
|
||||
dest_filename = filename
|
||||
|
||||
try:
|
||||
image = Image.open(f"{path}/{filename}")
|
||||
image.thumbnail(size)
|
||||
|
||||
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)
|
||||
except UnidentifiedImageError as e :
|
||||
pass
|
||||
print(e)
|
||||
|
||||
print("AAA^^^^^^^^^^^^AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
|
||||
|
||||
async def generate_thumbnail(path: str, filename: str, size: Tuple[int, int], quality: int = 90):
|
||||
try:
|
||||
image = Image.open(f"{path}/{filename}")
|
||||
image = image.thumbnail(size, Image.LANCZOS)
|
||||
check_directories(f"{path}/thumbs/")
|
||||
image.save(f"{path}/thumbs/{filename}")
|
||||
image.save(f"{path}/thumbs/{filename}", 'JPEG', quality=quality)
|
||||
|
||||
return await resize_image(path, filename, size, dest_path=f"{path}/thumbs/", dest_filename=filename)
|
||||
except UnidentifiedImageError:
|
||||
pass
|
||||
|
||||
|
||||
def delete_file(path: str, silent: bool = False):
|
||||
os.remove(path)
|
||||
try:
|
||||
os.remove(path)
|
||||
except Exception:
|
||||
if not silent:
|
||||
raise
|
||||
|
||||
Reference in New Issue
Block a user