Skip to content

Commit

Permalink
[Feat] create market category posts (#2)
Browse files Browse the repository at this point in the history
## Description

- [x]  posts create
- [x]  post data store in mysql
- [x]  img data store in s3 bucket

~~Post also needs user_id, but there is not code about it(we have to get
user_id using jwt_parse). So I will make it later and s3 code too.
Because something has changed(docker-compose, api prefix)~~

## Related Issue

Closes #1
  • Loading branch information
Eeap authored Oct 17, 2023
2 parents 7838ba3 + e04aaf5 commit c570e8a
Show file tree
Hide file tree
Showing 16 changed files with 219 additions and 4 deletions.
2 changes: 2 additions & 0 deletions app/api/routes/api.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from fastapi import APIRouter

from app.api.routes import users
from app.api.routes import markets


router = APIRouter()
router.include_router(users.router, tags=["users"], prefix="/user")
router.include_router(markets.router, tags=["markets"], prefix="/market")
32 changes: 32 additions & 0 deletions app/api/routes/markets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import logging
from typing import Any, List
from fastapi import APIRouter, Depends, HTTPException, UploadFile
from sqlalchemy.orm import Session
from app.api.dependencies import database
from app.models.domain import markets
from app.crud import crud_markets, crud_posts

router = APIRouter()


@router.post("/create")
def create_market_posts(
*,
db: Session = Depends(database.get_db),
files: List[UploadFile],
market_in: markets.MarketCreate = Depends()
) -> Any:
"""
Create new user.
"""
# post data create
post_data = crud_posts.create(db=db, obj_in=market_in, files=files)
# room data create
market_data = crud_markets.create(db=db, obj_in=market_in, post_id=post_data.id)
if market_data and post_data:
return {"message": "create success"}
else:
raise HTTPException(
status_code=500,
detail="create failed",
)
7 changes: 6 additions & 1 deletion app/core/config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import logging
import sys
from typing import List, Any

import boto3
from pydantic_settings import BaseSettings
from loguru import logger
from starlette.config import Config
Expand All @@ -11,7 +13,7 @@

class Settings(BaseSettings):
config: Config = Config(".env")
API_PREFIX: str = "/api"
API_PREFIX: str = "/api/v1"
JWT_TOKEN_PREFIX: str = "Token"
VERSION: str = "1.0.0"

Expand All @@ -29,6 +31,9 @@ class Settings(BaseSettings):
ALLOWED_HOSTS: List[str] = config(
"ALLOWED_HOSTS", cast=CommaSeparatedStrings, default=""
)
BUCKET_NAME: str = config("BUCKET_NAME", cast=str)
AWS_ACCESS_KEY: str = config("AWS_ACCESS_KEY", cast=str)
AWS_SECRET_KEY: str = config("AWS_SECRET_KEY", cast=str)


# logging configuration
Expand Down
Empty file added app/crud/__init__.py
Empty file.
26 changes: 26 additions & 0 deletions app/crud/crud_markets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from sqlalchemy.orm import Session
from app.models.domain import markets
from app.models.schemas.markets import Market


def create(db: Session, *, obj_in: markets.MarketCreate, post_id: int) -> Market:
db_obj = None
if obj_in.starting_price:
db_obj = Market(
starting_price=obj_in.starting_price,
price=obj_in.price,
auction=obj_in.auction,
deadline=obj_in.deadline,
post_id=post_id,
)
else:
db_obj = Market(
price=obj_in.price,
auction=obj_in.auction,
deadline=obj_in.deadline,
post_id=post_id,
)
db.add(db_obj)
db.commit()
db.refresh(db_obj)
return db_obj
34 changes: 34 additions & 0 deletions app/crud/crud_posts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from sqlalchemy.orm import Session
from app.models.domain import posts
from app.models.schemas.posts import Post
from app.services.aws import s3_upload
from fastapi import HTTPException
from fastapi import UploadFile
from typing import List
import datetime


def create(db: Session, *, obj_in: posts.PostCreate, files: List[UploadFile]) -> Post:
db_obj = Post(
title=obj_in.title,
content=obj_in.content,
status=obj_in.status,
category=obj_in.category,
created_at=datetime.datetime.today(),
)
# image bool value insert
db_obj.image = True if files else False
db.add(db_obj)
db.commit()
db.refresh(db_obj)
# if image exits, do uploading in s3
if files:
s3_result = s3_upload(
files=files, post_id=db_obj.id, user_email="[email protected]"
)
if not s3_result:
raise HTTPException(
status_code=500,
detail="s3 upload failed",
)
return db_obj
2 changes: 2 additions & 0 deletions app/db/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
# imported by Alembic
from app.db.base_class import Base # noqa
from app.models.schemas.users import User # noqa
from app.models.schemas.posts import Post # noqa
from app.models.schemas.markets import Market # noqa
16 changes: 16 additions & 0 deletions app/models/domain/markets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from typing import Optional
from pydantic import BaseModel
import datetime
from app.resources.status import Status


class MarketCreate(BaseModel):
post_id: Optional[int] = None
starting_price: Optional[int] = None
price: int
auction: bool
deadline: datetime.datetime
title: str
content: str
status: Status
category: str
11 changes: 11 additions & 0 deletions app/models/domain/posts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from pydantic import BaseModel
from typing import Optional
from app.resources.status import Status


class PostCreate(BaseModel):
title: str
content: str
status: Status
category: str
user_id: Optional[int] = None
17 changes: 17 additions & 0 deletions app/models/schemas/markets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from sqlalchemy import Boolean, Column, Integer, DateTime, ForeignKey
from sqlalchemy.orm import relationship
from typing import TYPE_CHECKING
from app.db.base_class import Base

if TYPE_CHECKING:
from .posts import Post # noqa: F401


class Market(Base):
id = Column(Integer, primary_key=True, autoincrement=True, index=True)
starting_price = Column(Integer)
price = Column(Integer, nullable=False)
auction = Column(Boolean(), default=False)
deadline = Column(DateTime, nullable=False)
post_id = Column(Integer, ForeignKey("post.id"))
post = relationship("Post", back_populates="market")
23 changes: 23 additions & 0 deletions app/models/schemas/posts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from sqlalchemy import Boolean, Column, Integer, String, DateTime, ForeignKey, Enum
from sqlalchemy.orm import relationship
from app.db.base_class import Base
from typing import TYPE_CHECKING
from app.resources.status import Status

if TYPE_CHECKING:
from .users import User # noqa: F401
from .markets import Market # noqa: F401


class Post(Base):
id = Column(Integer, primary_key=True, autoincrement=True, index=True)
title = Column(String(255), index=True)
content = Column(String(255), index=True)
user_id = Column(Integer)
user_id = Column(Integer, ForeignKey("user.id"))
user = relationship("User", back_populates="posts")
status = Column(Enum(Status), nullable=False, default=Status.PROCESSING)
created_at = Column(DateTime, nullable=False)
category = Column(String(255), index=True)
image = Column(Boolean(), default=False)
market = relationship("Market", back_populates="post")
9 changes: 7 additions & 2 deletions app/models/schemas/users.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
from sqlalchemy import Boolean, Column, Integer, String

from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import relationship
from app.db.base_class import Base
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from .posts import Post # noqa: F401


class User(Base):
id = Column(Integer, primary_key=True, autoincrement=True, index=True)
username = Column(String(255), index=True)
email = Column(String(255), unique=True, index=True, nullable=False)
posts = relationship("Post", back_populates="user")
6 changes: 6 additions & 0 deletions app/resources/status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import enum


class Status(enum.Enum):
COMPLETED = "COMPLETED"
PROCESSING = "PROCESSING"
26 changes: 26 additions & 0 deletions app/services/aws.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import boto3
from botocore.exceptions import ClientError
from fastapi import UploadFile
from app.core.config import settings
from typing import List


def s3_upload(files: List[UploadFile], post_id: int, user_email: str) -> bool:
client = boto3.client(
"s3",
region_name="us-east-1",
aws_access_key_id=settings.AWS_ACCESS_KEY,
aws_secret_access_key=settings.AWS_SECRET_KEY,
)
try:
for file in files:
new_filename = f"/{user_email}/{post_id}/{file.filename}"
client.upload_fileobj(
file.file,
settings.BUCKET_NAME,
new_filename,
ExtraArgs={"ContentType": file.content_type},
)
except ClientError as e:
return False
return True
4 changes: 3 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
services:
app:
build: .
build:
no_cache: true
context: .
ports:
- "8000:8000"
env_file:
Expand Down
8 changes: 8 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ anyio==3.7.1
astroid==3.0.1
bcrypt==4.0.1
black==23.9.1
boto3==1.28.64
botocore==1.31.64
cffi==1.16.0
click==8.1.7
cryptography==41.0.4
Expand All @@ -15,6 +17,7 @@ fastapi==0.103.2
h11==0.14.0
idna==3.4
isort==5.12.0
jmespath==1.0.1
jwt==1.3.1
lists==1.3.0
loguru==0.7.2
Expand All @@ -30,10 +33,15 @@ pydantic-settings==2.0.3
pydantic_core==2.10.1
pylint==3.0.1
PyMySQL==1.1.0
python-dateutil==2.8.2
python-dotenv==1.0.0
python-multipart==0.0.6
s3transfer==0.7.0
six==1.16.0
sniffio==1.3.0
SQLAlchemy==1.4.49
starlette==0.27.0
tomlkit==0.12.1
typing_extensions==4.8.0
urllib3==2.0.6
uvicorn==0.23.2

0 comments on commit c570e8a

Please sign in to comment.