Browse Source

Merge remote-tracking branch 'origin/backend-dev' into front-dev

SyuanYu 2 years ago
parent
commit
010e3383fc

+ 2 - 1
backend/app/app/api/api_v1/api.py

@@ -1,6 +1,6 @@
 from fastapi import APIRouter
 from fastapi import APIRouter
 
 
-from app.api.api_v1.endpoints import  login, users, utils, videos, images, reputations
+from app.api.api_v1.endpoints import  login, users, utils, videos, images, reputations, ser_no
 
 
 api_router = APIRouter()
 api_router = APIRouter()
 api_router.include_router(login.router, tags=["login"])
 api_router.include_router(login.router, tags=["login"])
@@ -9,3 +9,4 @@ api_router.include_router(utils.router, prefix="/utils", tags=["utils"])
 api_router.include_router(videos.router, prefix="/videos", tags=["videos"])
 api_router.include_router(videos.router, prefix="/videos", tags=["videos"])
 api_router.include_router(images.router, prefix="/images", tags=["iamges"])
 api_router.include_router(images.router, prefix="/images", tags=["iamges"])
 api_router.include_router(reputations.router, prefix="/reputations", tags=["reputations"])
 api_router.include_router(reputations.router, prefix="/reputations", tags=["reputations"])
+api_router.include_router(ser_no.router, prefix="/ser_nos", tags=["serial numbers"])

+ 1 - 3
backend/app/app/api/api_v1/endpoints/images.py

@@ -24,7 +24,7 @@ LOCAL_ZIP_STORAGE = Path("/").joinpath(settings.LOCAL_ZIP_STORAGE)
 
 
 
 
 router = APIRouter()
 router = APIRouter()
-
+sr_clients = {}
 
 
 @router.post("/sr")
 @router.post("/sr")
 async def supser_resolution(
 async def supser_resolution(
@@ -106,8 +106,6 @@ def get_image(
 def del_image():
 def del_image():
     pass
     pass
 
 
-sr_clients = {}
-
 @router.websocket("/sr")
 @router.websocket("/sr")
 async def websocket_endpoint(websocket: WebSocket):
 async def websocket_endpoint(websocket: WebSocket):
     await websocket.accept()
     await websocket.accept()

+ 36 - 8
backend/app/app/api/api_v1/endpoints/login.py

@@ -1,10 +1,10 @@
 from datetime import timedelta
 from datetime import timedelta
-from typing import Any
+from typing import Any, Optional
 
 
 from fastapi import APIRouter, Body, Depends, HTTPException
 from fastapi import APIRouter, Body, Depends, HTTPException
 from fastapi.security import OAuth2PasswordRequestForm
 from fastapi.security import OAuth2PasswordRequestForm
 from sqlalchemy.orm import Session
 from sqlalchemy.orm import Session
-
+from datetime import datetime
 from app import crud, models, schemas
 from app import crud, models, schemas
 from app.api import deps
 from app.api import deps
 from app.core import security
 from app.core import security
@@ -22,9 +22,9 @@ from google.auth.transport import requests
 router = APIRouter()
 router = APIRouter()
 
 
 
 
-@router.post("/login/access-token", response_model=schemas.Token)
+@router.post("/login/access-token")#, response_model=schemas.Token)
 def login_access_token(
 def login_access_token(
-    db: Session = Depends(deps.get_db), form_data: OAuth2PasswordRequestForm = Depends()
+    db: Session = Depends(deps.get_db), form_data: OAuth2PasswordRequestForm = Depends(), add_time_code: Optional[str] = None
 ) -> Any:
 ) -> Any:
     """
     """
     OAuth2 compatible token login, get an access token for future requests
     OAuth2 compatible token login, get an access token for future requests
@@ -37,16 +37,30 @@ def login_access_token(
     elif not crud.user.is_active(user):
     elif not crud.user.is_active(user):
         raise HTTPException(status_code=400, detail="Inactive user")
         raise HTTPException(status_code=400, detail="Inactive user")
     access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
     access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
-    return {
+    return_msg = {
         "access_token": security.create_access_token(
         "access_token": security.create_access_token(
             user.id, expires_delta=access_token_expires
             user.id, expires_delta=access_token_expires
         ),
         ),
         "token_type": "bearer",
         "token_type": "bearer",
     }
     }
+    if add_time_code:
+        available_ser_no = crud.serial_number.available(db, ser_no=add_time_code)
+        print(available_ser_no)
+        if available_ser_no:
+            user_in = schemas.UserUpdate(available_time=user.available_time+available_ser_no.time)
+            crud.user.update(db, db_obj=user, obj_in=user_in)
+        
+            ser_no_in = schemas.SerialNumberUpdate(code=available_ser_no.code, is_used=True, used_datetime=str(datetime.now()), owner_id=user.id)
+            crud.serial_number.update(db, db_obj=available_ser_no, obj_in=ser_no_in)
+            print(available_ser_no.time, type(available_ser_no.time))
+            return_msg['time_added'] = available_ser_no.time
+        else:
+            return_msg['time_added'] = -1
+    return return_msg 
 
 
-@router.post("/login/google/access-token", response_model=schemas.Token)
+@router.post("/login/google/access-token")#, response_model=schemas.Token)
 def login_access_token(
 def login_access_token(
-    db: Session = Depends(deps.get_db), form_data: OAuth2PasswordRequestForm = Depends()
+    db: Session = Depends(deps.get_db), form_data: OAuth2PasswordRequestForm = Depends(), add_time_code: Optional[str] = None
 ) -> Any:
 ) -> Any:
     """
     """
     OAuth2 compatible token login, get an access token for future requests
     OAuth2 compatible token login, get an access token for future requests
@@ -60,12 +74,26 @@ def login_access_token(
     elif not crud.user.is_active(user):
     elif not crud.user.is_active(user):
         raise HTTPException(status_code=400, detail="Inactive user")
         raise HTTPException(status_code=400, detail="Inactive user")
     access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
     access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
-    return {
+    return_msg = {
         "access_token": security.create_access_token(
         "access_token": security.create_access_token(
             user.id, expires_delta=access_token_expires
             user.id, expires_delta=access_token_expires
         ),
         ),
         "token_type": "bearer",
         "token_type": "bearer",
     }
     }
+    if add_time_code:
+        available_ser_no = crud.serial_number.available(db, ser_no=add_time_code)
+        print(available_ser_no)
+        if available_ser_no:
+            user_in = schemas.UserUpdate(available_time=user.available_time+available_ser_no.time)
+            crud.user.update(db, db_obj=user, obj_in=user_in)
+        
+            ser_no_in = schemas.SerialNumberUpdate(code=available_ser_no.code, is_used=True, used_datetime=str(datetime.now()), owner_id=user.id)
+            crud.serial_number.update(db, db_obj=available_ser_no, obj_in=ser_no_in)
+            print(available_ser_no.time, type(available_ser_no.time))
+            return_msg['time_added'] = available_ser_no.time
+        else:
+            return_msg['time_added'] = -1
+    return return_msg 
 
 
 
 
 
 

+ 1 - 1
backend/app/app/api/api_v1/endpoints/reputations.py

@@ -36,6 +36,6 @@ async def post_reputation(
     """
     """
     
     
     #print(posted_article)
     #print(posted_article)
-    article = crud.artivle.create_with_owner(db=db, obj_in=posted_article, owner_id=current_user.id, posted_datetime=str(datetime.now()))
+    article = crud.article.create_with_owner(db=db, obj_in=posted_article, owner_id=current_user.id, posted_datetime=str(datetime.now()))
     if article:
     if article:
         return {"id":article.id}
         return {"id":article.id}

+ 41 - 0
backend/app/app/api/api_v1/endpoints/ser_no.py

@@ -0,0 +1,41 @@
+from typing import Any, List
+from fastapi.responses import RedirectResponse
+from fastapi import APIRouter, Body, Depends, HTTPException, Request
+from fastapi.encoders import jsonable_encoder
+from pydantic.networks import EmailStr
+from sqlalchemy.orm import Session
+
+from app import crud, models, schemas
+from app.api import deps
+from app.core.config import settings
+
+from datetime import datetime
+
+router = APIRouter()
+
+@router.get("/add-time")
+def add_time(
+    *,
+    db: Session = Depends(deps.get_db),
+    current_user: models.User = Depends(deps.get_current_active_user),
+    ser_no: models.SerialNumber = Depends(deps.get_avairable_serial_number)
+)-> Any:
+
+  
+    user_in = schemas.UserUpdate(available_time=current_user.available_time+ser_no.time)
+    crud.user.update(db, db_obj=current_user, obj_in=user_in)
+    
+    ser_no_in = schemas.SerialNumberUpdate(code=ser_no.code, is_used=True, used_datetime=str(datetime.now()), owner_id=current_user.id)
+    crud.serial_number.update(db, db_obj=ser_no, obj_in=ser_no_in)
+
+    return {"time_added": ser_no.time}
+
+@router.get("/add-time-no-token")
+def add_time_no_token(
+    request: Request,
+    ser_no: models.SerialNumber = Depends(deps.get_avairable_serial_number)
+)-> Any:
+    print(f"{request.base_url}login?ser_no={ser_no.code}")
+    #return RedirectResponse(f"{request.base_url}login?ser_no={ser_no.code}")
+    return RedirectResponse(f"http://localhost:5173/login?add-time-code={ser_no.code}")
+

+ 105 - 10
backend/app/app/api/api_v1/endpoints/videos.py

@@ -1,10 +1,10 @@
 from typing import Any, List, Optional
 from typing import Any, List, Optional
 import subprocess
 import subprocess
 from fastapi import UploadFile, File, Form
 from fastapi import UploadFile, File, Form
-from fastapi.responses import FileResponse
-from fastapi import APIRouter, Depends, HTTPException
+from fastapi.responses import FileResponse, JSONResponse
+from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks, WebSocket
 from sqlalchemy.orm import Session
 from sqlalchemy.orm import Session
-
+from fastapi.encoders import jsonable_encoder
 import app.crud as crud
 import app.crud as crud
 import app.models as models
 import app.models as models
 import app.schemas as schemas 
 import app.schemas as schemas 
@@ -13,14 +13,16 @@ from app.api import deps
 from app.core.celery_app import celery_app
 from app.core.celery_app import celery_app
 from app.core.config import settings
 from app.core.config import settings
 from pathlib import Path
 from pathlib import Path
+from app.db.session import SessionLocal
 
 
-from app.core.celery_app import celery_app
+import asyncio
 
 
 BACKEND_ZIP_STORAGE = Path("/app").joinpath(settings.BACKEND_ZIP_STORAGE)
 BACKEND_ZIP_STORAGE = Path("/app").joinpath(settings.BACKEND_ZIP_STORAGE)
 LOCAL_ZIP_STORAGE = Path("/").joinpath(settings.LOCAL_ZIP_STORAGE)
 LOCAL_ZIP_STORAGE = Path("/").joinpath(settings.LOCAL_ZIP_STORAGE)
 
 
 
 
 router = APIRouter()
 router = APIRouter()
+video_clients = {}
 
 
 @router.get("/", response_model=List[schemas.Video])
 @router.get("/", response_model=List[schemas.Video])
 def get_video_list(
 def get_video_list(
@@ -40,6 +42,20 @@ def get_video_list(
         )
         )
     return videos
     return videos
 
 
+@router.post("/test")
+def test(
+    *,
+    title: str,
+    anchor_id: int,
+    lang_id: int,
+    current_user: models.User = Depends(deps.get_current_active_user),
+) -> Any:
+    video_data = {"title":title, "anchor_id":anchor_id, "lang_id":lang_id}
+    print(video_data)
+    task = celery_app.send_task("app.worker.make_video_test",  kwargs=video_data, )
+    print(task)
+    return "ok"
+
 @router.post("/", response_model=schemas.Video)
 @router.post("/", response_model=schemas.Video)
 def upload_plot(
 def upload_plot(
     *,
     *,
@@ -49,18 +65,17 @@ def upload_plot(
     lang_id: int=Form(...),
     lang_id: int=Form(...),
     upload_file: UploadFile=File(),
     upload_file: UploadFile=File(),
     current_user: models.User = Depends(deps.get_current_active_user),
     current_user: models.User = Depends(deps.get_current_active_user),
+    background_tasks: BackgroundTasks,
 ) -> Any:
 ) -> Any:
     """
     """
     Create new video.
     Create new video.
     """
     """
     print(title)
     print(title)
     print(upload_file.filename)
     print(upload_file.filename)
-    file_name = crud.video.generate_file_name(db=db, n=20)
-    video_create = schemas.VideoCreate(title=title, progress_state="waiting", stored_file_name=file_name)
-    video = crud.video.create_with_owner(db=db, obj_in=video_create, owner_id=current_user.id)
-
+    filename = crud.video.generate_file_name(db=db, n=20)
+    
     try:
     try:
-        with open(str(Path(BACKEND_ZIP_STORAGE).joinpath(video.stored_file_name+".zip")), 'wb') as f:
+        with open(str(Path(BACKEND_ZIP_STORAGE).joinpath(filename+".zip")), 'wb') as f:
             while contents := upload_file.file.read(1024 * 1024):
             while contents := upload_file.file.read(1024 * 1024):
                 f.write(contents)
                 f.write(contents)
     except Exception as e:
     except Exception as e:
@@ -68,13 +83,75 @@ def upload_plot(
         return {"error": str(e)}
         return {"error": str(e)}
     finally:
     finally:
         upload_file.file.close()
         upload_file.file.close()
+    
+
+    '''
     zip_filename = video.stored_file_name+".zip"
     zip_filename = video.stored_file_name+".zip"
     print(str(BACKEND_ZIP_STORAGE/zip_filename))
     print(str(BACKEND_ZIP_STORAGE/zip_filename))
     r = subprocess.run(["sshpass", "-p", "choozmo9", 
     r = subprocess.run(["sshpass", "-p", "choozmo9", 
                     "scp", "-P", "5722", "-o", "StrictHostKeyChecking=no", f"{str(BACKEND_ZIP_STORAGE/zip_filename)}", f"root@172.104.93.163:{str(LOCAL_ZIP_STORAGE/zip_filename)}"])
                     "scp", "-P", "5722", "-o", "StrictHostKeyChecking=no", f"{str(BACKEND_ZIP_STORAGE/zip_filename)}", f"root@172.104.93.163:{str(LOCAL_ZIP_STORAGE/zip_filename)}"])
     print(r.returncode)
     print(r.returncode)
     celery_app.send_task("app.worker.make_video", args=[video.id, video.stored_file_name, current_user.id, anchor_id, current_user.membership_status, current_user.available_time])
     celery_app.send_task("app.worker.make_video", args=[video.id, video.stored_file_name, current_user.id, anchor_id, current_user.membership_status, current_user.available_time])
-    return video
+    '''
+    video_create = schemas.VideoCreate(title=title, progress_state="PENDING", stored_filename=filename)
+    video = crud.video.create_with_owner(db=db, obj_in=video_create, owner_id=current_user.id)
+    return_msg = {"video_message":"accepted"}
+    video_data = jsonable_encoder(video)
+    video_data['membership_status'] = current_user.membership_status
+    video_data['available_time'] = current_user.available_time
+    video_data['video_id'] = video_data['id']
+    background_tasks.add_task(wait_finish, video_data)
+    return  JSONResponse(return_msg, background=background_tasks)
+
+async def wait_finish(video_data:dict): 
+    zip_filename = video_data['stored_filename']+".zip"
+    process = await asyncio.create_subprocess_exec("sshpass", "-p", "choozmo9", 
+                    "scp", "-P", "5722", "-o", "StrictHostKeyChecking=no", f"{str(BACKEND_ZIP_STORAGE/zip_filename)}", f"root@172.104.93.163:{str(LOCAL_ZIP_STORAGE)}")
+    await process.wait()
+
+    task = celery_app.send_task("app.worker.make_video", kwargs=video_data)
+    while True:
+       await asyncio.sleep(1)
+       if task.state != "PENDING":
+           break
+       
+    db = SessionLocal()
+    video = db.query(models.Video).get(video_data['id'])
+    video.progress_state = "STARTED" 
+    db.commit()
+    db.close()
+    msg_data = f"{video_data['stored_filename']}:STARTED"
+    await publish(msg_data)
+
+    while True:
+        await asyncio.sleep(1)
+        if task.state != "STARTED":
+            break
+
+    if task.state == "SUCCESS":
+        db = SessionLocal()
+        video = db.query(models.Video).get(video_data['id'])
+        user = db.query(models.User).get(video_data['owner_id'])
+        video.progress_state = "SUCCESS" 
+        if task.result:
+            user.available_time -= int(task.result)
+        db.commit()
+        db.close()
+        msg_data = f"{video_data['stored_filename']}:SUCCESS"
+
+    elif task.state == "FAILURE":
+        db = SessionLocal()
+        video = db.query(models.Video).get(video_data['id'])
+        video.progress_state = "FAILURE" 
+        db.commit()
+        db.close()
+        msg_data = f"{video_data['stored_filename']}:FAILURE"
+
+    await publish(msg_data)
+
+async def publish(data):
+    for video_client in video_clients.values():
+        await video_client.send_text(f"{data}")
 
 
 @router.get("/{id}")
 @router.get("/{id}")
 def download_video(
 def download_video(
@@ -86,3 +163,21 @@ def download_video(
     
     
     return {"message":"address"}
     return {"message":"address"}
 
 
+@router.websocket("")
+async def websocket_endpoint(websocket: WebSocket):
+    await websocket.accept()
+    key = websocket.headers.get('sec-websocket-key')
+    video_clients[key] = websocket
+    try:
+        while True:
+            data = await websocket.receive_text()
+            if not data.startswith("subscribe"):
+              del video_clients[key]
+              #for client in sr_clients.values():
+              #      await client.send_text(f"ID: {key} | Message: {data}")
+
+    except:
+        # 接続が切れた場合、当該クライアントを削除する
+        del video_clients[key]
+
+

+ 12 - 0
backend/app/app/api/deps.py

@@ -59,3 +59,15 @@ def get_current_active_superuser(
             status_code=400, detail="The user doesn't have enough privileges"
             status_code=400, detail="The user doesn't have enough privileges"
         )
         )
     return current_user
     return current_user
+
+def get_valid_serial_number(code: str, db: Session = Depends(get_db)):
+    ser_no = db.query(models.SerialNumber).filter(models.SerialNumber.code==code).first()
+    if not ser_no:
+        raise HTTPException(status_code=400, detail="This serial number is invalid")
+    
+    return ser_no
+
+def get_avairable_serial_number(ser_no:models.SerialNumber = Depends(get_valid_serial_number)):
+    if ser_no.is_used:
+        raise HTTPException(status_code=400, detail="This serial number is already used")
+    return ser_no

+ 1 - 1
backend/app/app/core/celery_app.py

@@ -4,4 +4,4 @@ celery_app = Celery("worker", broker="redis://172.104.93.163:16379/0", backend="
 
 
 
 
 
 
-celery_app.conf.task_routes = {"app.worker.make_video": "main-queue", "app.worker.super_resolution": "main-queue"}
+celery_app.conf.task_routes = {"app.worker.make_video": "video", "app.worker.super_resolution": "image", "app.worker.make_video_test": "video"}

+ 1 - 0
backend/app/app/core/celeryconf.py

@@ -0,0 +1 @@
+task_track_started = True

+ 2 - 1
backend/app/app/crud/__init__.py

@@ -1,6 +1,7 @@
 from .crud_user import user
 from .crud_user import user
 from .crud_video import video
 from .crud_video import video
-from .crud_article import artivle
+from .crud_article import article
+from .crud_ser_no import serial_number
 # For a new basic set of CRUD operations you could just do
 # For a new basic set of CRUD operations you could just do
 
 
 # from .base import CRUDBase
 # from .base import CRUDBase

+ 1 - 1
backend/app/app/crud/crud_article.py

@@ -20,4 +20,4 @@ class CRUDArticle(CRUDBase[Article, ArticleCreate, ArticleUpdate]):
         db.refresh(db_obj)
         db.refresh(db_obj)
         return db_obj
         return db_obj
     
     
-artivle = CRUDArticle(Article)
+article = CRUDArticle(Article)

+ 25 - 0
backend/app/app/crud/crud_ser_no.py

@@ -0,0 +1,25 @@
+from typing import List, Optional
+
+from fastapi.encoders import jsonable_encoder
+from sqlalchemy.orm import Session
+
+from app.crud.base import CRUDBase
+from app.models.serial_number import SerialNumber
+from app.schemas.serial_number import SerialNumberCreate, SerialNumberUpdate
+
+from app.utils import random_name
+
+class CRUDSerialNumber(CRUDBase[SerialNumber, SerialNumberCreate, SerialNumberUpdate]):
+    
+    def valid(self, db: Session, *, ser_no:str) -> Optional[SerialNumber]:
+        return db.query(SerialNumber).filter(SerialNumber.code==ser_no).first()
+
+    def available(self, db: Session, *, ser_no:str) -> Optional[SerialNumber]:
+        valid_ser_no = self.valid(db, ser_no=ser_no)
+        if valid_ser_no and not valid_ser_no.is_used:
+            return valid_ser_no
+        else:
+            return None
+        
+    
+serial_number = CRUDSerialNumber(SerialNumber)

+ 3 - 3
backend/app/app/crud/crud_video.py

@@ -35,9 +35,9 @@ class CRUDVideo(CRUDBase[Video, VideoCreate, VideoUpdate]):
       self, db: Session, *, n:int
       self, db: Session, *, n:int
     ) -> bool:
     ) -> bool:
         while True:
         while True:
-            file_name = random_name(n)
-            if not db.query(self.model).filter(Video.stored_file_name==file_name).first():
-              return file_name
+            filename = random_name(n)
+            if not db.query(self.model).filter(Video.stored_filename==filename).first():
+              return filename
 
 
 
 
 video = CRUDVideo(Video)
 video = CRUDVideo(Video)

+ 2 - 0
backend/app/app/db/base.py

@@ -6,3 +6,5 @@ from app.models.video import Video
 from app.models.enum import Progress, Membership
 from app.models.enum import Progress, Membership
 from app.models.character import Character
 from app.models.character import Character
 from app.models.article import Article
 from app.models.article import Article
+from app.models.serial_number import SerialNumber
+from app.models.voice import Voice

+ 3 - 1
backend/app/app/models/__init__.py

@@ -1,4 +1,6 @@
 from .user import User
 from .user import User
 from .video import Video
 from .video import Video
 from .enum import Membership, Progress
 from .enum import Membership, Progress
-from .article import Article
+from .article import Article
+from .serial_number import SerialNumber
+from .voice import Voice

+ 14 - 0
backend/app/app/models/serial_number.py

@@ -0,0 +1,14 @@
+from typing import TYPE_CHECKING
+
+from sqlalchemy import Column, ForeignKey, Integer, String, DateTime, Boolean
+
+from app.db.base_class import Base
+
+class SerialNumber(Base):
+  __tablename__ = "serial_number"
+  code = Column(String(length=20), primary_key=True)
+  time = Column(Integer, nullable=False)
+  created_datetime = Column(DateTime)
+  is_used = Column(Boolean, nullable=False, default=False)
+  used_datetime = Column(DateTime)
+  owner_id = Column(Integer, ForeignKey("user.id"))

+ 1 - 1
backend/app/app/models/video.py

@@ -13,7 +13,7 @@ if TYPE_CHECKING:
 class Video(Base):
 class Video(Base):
   id = Column(Integer, primary_key=True, index=True)
   id = Column(Integer, primary_key=True, index=True)
   title = Column(String(20), index=True, nullable=False)
   title = Column(String(20), index=True, nullable=False)
-  stored_file_name = Column(String(30), unique=True, nullable=False)
+  stored_filename = Column(String(30), unique=True, nullable=False)
   progress_state = Column(String(10), 
   progress_state = Column(String(10), 
                     ForeignKey("progress.state", ondelete="RESTRICT", onupdate="CASCADE"),
                     ForeignKey("progress.state", ondelete="RESTRICT", onupdate="CASCADE"),
                     default="waiting")
                     default="waiting")

+ 10 - 0
backend/app/app/models/voice.py

@@ -0,0 +1,10 @@
+from typing import TYPE_CHECKING
+
+from sqlalchemy import Column, ForeignKey, Integer, String, DateTime, Boolean
+
+from app.db.base_class import Base
+
+class Voice(Base):
+  id = Column(Integer, primary_key=True, index=True,)
+  name = Column(String(30))
+  play_id = Column(String(30))

+ 1 - 0
backend/app/app/schemas/__init__.py

@@ -3,3 +3,4 @@ from .user import User, UserCreate, UserInDB, UserUpdate
 from .msg import Msg
 from .msg import Msg
 from .video import Video, VideoCreate, VideoInDB, VideoUpdate
 from .video import Video, VideoCreate, VideoInDB, VideoUpdate
 from .article import ArticleBase, ArticleCreate, ArticleUpdate
 from .article import ArticleBase, ArticleCreate, ArticleUpdate
+from .serial_number import SerialNumberBase, SerialNumberCreate, SerialNumberUpdate

+ 21 - 0
backend/app/app/schemas/serial_number.py

@@ -0,0 +1,21 @@
+from typing import Optional
+
+from pydantic import BaseModel
+
+
+
+class SerialNumberBase(BaseModel):
+    code: Optional[str] = None
+    time: Optional[int] = None
+    created_datetime: Optional[str] = None
+    is_used: Optional[bool] = False
+    used_datetime: Optional[str] = None
+
+# Properties to receive via API on creation
+class SerialNumberCreate(SerialNumberBase):
+    pass
+
+
+# Properties to receive via API on update
+class SerialNumberUpdate(SerialNumberBase):
+    owner_id: int

+ 1 - 0
backend/app/app/schemas/user.py

@@ -19,6 +19,7 @@ class UserCreate(UserBase):
 # Properties to receive via API on update
 # Properties to receive via API on update
 class UserUpdate(UserBase):
 class UserUpdate(UserBase):
     password: Optional[str] = None
     password: Optional[str] = None
+    available_time: Optional[int] = None
 
 
 
 
 class UserInDBBase(UserBase):
 class UserInDBBase(UserBase):

+ 2 - 2
backend/app/app/schemas/video.py

@@ -8,13 +8,13 @@ from fastapi import UploadFile, File
 class VideoBase(BaseModel):
 class VideoBase(BaseModel):
     title: Optional[str] = None
     title: Optional[str] = None
     progress_state: Optional[str] = None
     progress_state: Optional[str] = None
-    stored_file_name: Optional[str] = None
+    stored_filename: Optional[str] = None
 
 
 # Properties to receive on video creation
 # Properties to receive on video creation
 class VideoCreate(VideoBase):
 class VideoCreate(VideoBase):
     title: str
     title: str
     progress_state: str
     progress_state: str
-    stored_file_name: str
+    stored_filename: str
 
 
 # Properties to receive on video update
 # Properties to receive on video update
 class VideoUpdate(VideoBase):
 class VideoUpdate(VideoBase):