Skip to content

Commit

Permalink
add image processing
Browse files Browse the repository at this point in the history
  • Loading branch information
Ferrariic committed May 26, 2022
1 parent 9e9c345 commit fa135ac
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 91 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ celerybeat.pid
# SageMath parsed files
*.sage.py

# Image Folder
/images/

# Environments
.venv
env/
Expand Down
2 changes: 2 additions & 0 deletions api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
user_rating_history,
user_stats,
user_token,
user_images,
users,
)

Expand All @@ -19,6 +20,7 @@
app.include_router(user_rating_history.router)
app.include_router(user_stats.router)
app.include_router(user_token.router)
app.include_router(user_images.router)
app.include_router(users.router)


Expand Down
16 changes: 13 additions & 3 deletions api/database/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@
import logging
import random
import re
import string
import sys
import traceback
from asyncio.tasks import create_task
from collections import namedtuple
from datetime import datetime, timedelta
from typing import List

from api.database.database import USERDATA_ENGINE, Engine, EngineType
from api.database.models import Users, UserToken
from fastapi import HTTPException
from sqlalchemy import Text, text
from sqlalchemy.exc import InternalError, OperationalError
from sqlalchemy.ext.asyncio import AsyncResult, AsyncSession
from sqlalchemy.sql.expression import insert, select

from api.database.models import UserToken, Users

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -175,8 +176,17 @@ async def batch_function(function, data, batch_size=100):
batches = []
for i in range(0, len(data), batch_size):
logger.debug({"batch": {f"{function.__name__}": f"{i}/{len(data)}"}})
batch = data[i: i + batch_size]
batch = data[i : i + batch_size]
batches.append(batch)

await asyncio.gather(*[create_task(function(batch)) for batch in batches])
return


async def image_token_generator(length=10):
return "".join(
random.SystemRandom().choice(
string.ascii_uppercase + string.ascii_lowercase + string.digits
)
for _ in range(length)
)
7 changes: 5 additions & 2 deletions api/database/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from datetime import datetime
from enum import unique

from numpy import integer
from sqlalchemy import (
Expand Down Expand Up @@ -68,7 +69,7 @@ class UserImages(Base):
bio = Column(TINYINT)
banner = Column(TINYINT)
image_key = Column(TINYTEXT)
image = Column(BLOB)
image = Column(TEXT, unique=True)
size = Column(TINYINT)
timestamp = Column(TIMESTAMP)

Expand Down Expand Up @@ -142,7 +143,9 @@ class TrainerIdentificationInformation(Base):
user_id = Column(
ForeignKey("users.user_id", ondelete="RESTRICT", onupdate="RESTRICT")
)
content = Column(BLOB)
image = Column(
ForeignKey("user_images.image", ondelete="RESTRICT", onupdate="RESTRICT")
)
content_type = Column(TINYINT)
timestamp = Column(TIMESTAMP)

Expand Down
74 changes: 0 additions & 74 deletions api/routers/trainer_identification_information.py
Original file line number Diff line number Diff line change
@@ -1,74 +0,0 @@
# from datetime import datetime
# import json
# from typing import Optional
# from urllib.request import Request

# from api.database.functions import USERDATA_ENGINE, EngineType, sqlalchemy_result, verify_token
# from api.database.models import TrainerIdentificationInformation
# from fastapi import APIRouter, HTTPException, Query, status
# from pydantic import BaseModel
# from pydantic.fields import Field
# from pymysql import Timestamp
# from sqlalchemy import DATETIME, TIMESTAMP, func, select
# from sqlalchemy.dialects.mysql import Insert
# from sqlalchemy.ext.asyncio import AsyncSession
# from sqlalchemy.orm import aliased
# from sqlalchemy.sql.expression import Select, select, insert

# router = APIRouter()


# class trainer_identification_information(BaseModel):
# """
# trainer_identification_information base model containing the types and content expected by the database
# """


# @router.get(
# "/V1/trainer-identification-information/",
# tags=["trainer", "trainer identification information"],
# )
# async def get_trainer_identification_information(
# token: str,
# ID: Optional[int],
# user_id: int,
# content_type: Optional[int],
# timestamp=Optional[datetime],
# # content = Column(BLOB),
# row_count: Optional[int] = Query(100, ge=1, le=1000),
# page: Optional[int] = Query(1, ge=1),
# ) -> json:

# table = TrainerIdentificationInformation
# sql: Select = select(table)

# sql = sql.limit(row_count).offset(row_count * (page - 1))

# async with USERDATA_ENGINE.get_session() as session:
# session: AsyncSession = session
# async with session.begin():
# data = await session.execute(sql)

# data = sqlalchemy_result(data)
# return data.rows2dict()


# @router.post(
# "/V1/trainer-identification-information",
# tags=["trainer", "trainer identification information"],
# )
# async def post_trainer_identification_status(
# trainer_identification_information: trainer_identification_information,
# ) -> json:

# values = trainer_identification_information.dict()
# table = TrainerIdentificationInformation
# sql = insert(table).values(values)
# sql = sql.prefix_with("ignore")

# async with USERDATA_ENGINE.get_session() as session:
# session: AsyncSession = session
# async with session.begin():
# data = await session.execute(sql)

# return {"ok": "ok"}
217 changes: 217 additions & 0 deletions api/routers/user_images.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
from genericpath import exists
import json
import os
import shutil
from datetime import datetime
from pickletools import optimize
from typing import Optional
from urllib.request import Request

from api.database.functions import (
USERDATA_ENGINE,
EngineType,
image_token_generator,
sqlalchemy_result,
verify_token,
)
from api.database.models import UserImages
from fastapi import APIRouter, File, HTTPException, Query, UploadFile, status
from pydantic import BaseModel
from pydantic.fields import Field
from pymysql import Timestamp
from pyparsing import Opt
from sqlalchemy import BLOB, DATETIME, TIMESTAMP, func, select
from sqlalchemy.dialects.mysql import Insert
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import aliased
from sqlalchemy.sql.expression import Select, insert, select

from api.routers.users import get_users
from enum import Enum

router = APIRouter()


class image_category(str, Enum):
bio = "bio"
profile = "profile"
banner = "banner"
chat = "chat"
license_front = "license_front"
license_back = "license_back"


@router.get(
"/V1/user-images/",
tags=["user", "images"],
)
async def get_user_images(
token: str,
login: str,
s_user_id: Optional[int] = None,
chat: Optional[bool] = False,
profile: Optional[bool] = False,
bio: Optional[bool] = False,
banner: Optional[bool] = False,
image_key: Optional[str] = None,
size: Optional[int] = None,
timestamp: Optional[datetime] = None,
ID: Optional[int] = None,
image: UploadFile = File(None),
row_count: Optional[int] = Query(100, ge=1, le=1000),
page: Optional[int] = Query(1, ge=1),
) -> json:
"""
Args:\n
token (str): user token information\n
login (str): user login information\n
s_user_id (Optional[int], optional): sending user id. Defaults to None.\n
chat (Optional[bool], optional): True if the content is chat. Defaults to False.\n
profile (Optional[bool], optional): True if the content is a profile. Defaults to False.\n
bio (Optional[bool], optional): True if the content is in a bio. Defaults to False.\n
banner (Optional[bool], optional): True if the content is a banner image. Defaults to False.\n
image_key (Optional[str], optional): The image key for the image. Defaults to None.\n
size (Optional[int], optional): The size of the image in KB. Defaults to None.\n
timestamp (Optional[datetime], optional): timestamp of image creation. Defaults to None.\n
ID (Optional[int], optional): ID of the image, increments automatically. Defaults to None.\n
image (UploadFile, optional): Image path on server itself. Defaults to File(None).\n
row_count (Optional[int], optional): Rows to pull for relevant data. Defaults to Query(100, ge=1, le=1000).\n
page (Optional[int], optional): Pages to pull. Defaults to Query(1, ge=1).\n
Returns:\n
json: _description_\n
"""

if not await verify_token(login=login, token=token, access_level=9):
return

table = UserImages
sql: Select = select(table)

if s_user_id is not None:
sql = sql.where(table.s_user_id == s_user_id)

if chat is not None:
sql = sql.where(table.chat == chat)

if profile is not None:
sql = sql.where(table.profile == profile)

if bio is not None:
sql = sql.where(table.bio == bio)

if banner is not None:
sql = sql.where(table.banner == banner)

if image_key is not None:
sql = sql.where(table.image_key == image_key)

if size is not None:
sql = sql.where(table.size == size)

if timestamp is not None:
sql = sql.where(table.timestamp == timestamp)

if ID is not None:
sql = sql.where(table.ID == ID)

if image is not None:
sql = sql.where(table.image == image)

sql = sql.limit(row_count).offset(row_count * (page - 1))

async with USERDATA_ENGINE.get_session() as session:
session: AsyncSession = session
async with session.begin():
data = await session.execute(sql)

data = sqlalchemy_result(data)
return data.rows2dict()


@router.post(
"/V1/user-images",
tags=["user", "images"],
)
async def post_user_images(
login: str,
token: str,
selection: image_category,
image: UploadFile = File(...),
) -> json:
"""
Args:\n
login (str): Login informatoin\n
token (str): Token informatoin\n
selection (image_category): Image category selection for categorization.\n
image (UploadFile, optional): Uploaded image. Defaults to File(...).\n
Returns:\n
json: {'ok':'ok'}\n
"""

# verify user auth level
if not await verify_token(login=login, token=token, access_level=9):
return

# get user ID
user_data = await get_users(login=login, token=token, self_lookup=True)
user_id = user_data[0]["user_id"]

# generate image key
image_key = await image_token_generator(length=50)

# set directory
directory = f"images/{user_id}"
image_path = f"{directory}/{image_key}"

# create directory
try:
os.mkdir(directory)
except FileExistsError:
pass

# store image in directory
with open(image_path, "wb") as image_buffer:
shutil.copyfileobj(image.file, image_buffer)

# image size in KB
size = os.path.getsize(image_path) / 1000

# set up selection name and relevant values for
selection_choice = selection.name
bio = profile = banner = chat = license_front = license_back = 0

if selection_choice == "bio":
bio = 1
if selection_choice == "profile":
profile = 1
if selection_choice == "banner":
banner = 1
if selection_choice == "chat":
chat = 1
if selection_choice == "license_front":
license_front = 1
if selection_choice == "license_back":
license_back = 1

values = {
"s_user_id": user_id,
"chat": chat,
"profile": profile,
"bio": bio,
"banner": banner,
"image_key": image_key,
"image": image_path,
"size": size,
}

table = UserImages
sql = insert(table).values(values)
sql = sql.prefix_with("ignore")

async with USERDATA_ENGINE.get_session() as session:
session: AsyncSession = session
async with session.begin():
data = await session.execute(sql)

return {"ok": "ok"}
Loading

0 comments on commit fa135ac

Please sign in to comment.