Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSVから商品エントリを生成する機能を追加 #82

Merged
merged 1 commit into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/routers/products.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,9 @@ async def get_product_editor(request: Request, product_id: int):
@router.get("/product-editor", response_class=HTMLResponse)
async def get_empty_product_editor(request: Request):
return HTMLResponse(templates.products.empty_editor(request))


# TODO: This path is defined temporally for convenience and should be removed in the future.
@router.put("/products/static/{csv_file}")
async def renew_table_from_products_list_csv(csv_file: str = "products-list.csv"):
await ProductTable.renew_from_static_csv(f"static/{csv_file}")
159 changes: 28 additions & 131 deletions app/store/product.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import Annotated
import csv
from typing import Annotated, Iterable
from uuid import UUID, uuid4

import pydantic
Expand All @@ -16,7 +17,7 @@ class Product(sqlmodel.SQLModel, table=True):
name: Annotated[str, sqlmodel.Field(max_length=40)]
filename: Annotated[str, sqlmodel.Field(max_length=100)]
price: int
no_stock: int | None # Column(..., nullable=True)
no_stock: int | None

def price_str(self) -> str:
return self.to_price_str(self.price)
Expand Down Expand Up @@ -74,133 +75,29 @@ def __init__(self, database: Database):
async def ainit(self) -> None:
if not await self._empty():
return
await self.renew_from_static_csv()

# TODO: This function is defined temporally for convenience and should be removed in the future.
async def renew_from_static_csv(self, csv_file: str = "static/product-list.csv"):
def decomment(csv_rows: Iterable[str]):
for row in csv_rows:
row_body = row.split("#")[0].strip()
if row_body != "":
yield row_body

products: list[Product] = []
with open(csv_file) as f:
reader = csv.DictReader(
decomment(f), dialect="unix", quoting=csv.QUOTE_STRINGS, strict=True
)
for csv_row in reader:
if csv_row["no_stock"] == "":
csv_row["no_stock"] = None
products.append(Product.model_validate(csv_row))

# TODO: read this data from CSV or something
products = [
# Coffee
{
"product_id": 1,
"name": "ブレンドコーヒー",
"filename": "coffee01_blend.png",
"price": 150,
"no_stock": 100,
},
{
"product_id": 2,
"name": "アメリカンコーヒー",
"filename": "coffee02_american.png",
"price": 150,
"no_stock": 100,
},
{
"product_id": 3,
"name": "カフェオレコーヒー",
"filename": "coffee03_cafeole.png",
"price": 150,
"no_stock": 100,
},
{
"product_id": 4,
"name": "ブレンドブラックコーヒー",
"filename": "coffee04_blend_black.png",
"price": 150,
"no_stock": 100,
},
{
"product_id": 5,
"name": "カプチーノコーヒー",
"filename": "coffee05_cappuccino.png",
"price": 150,
"no_stock": 100,
},
{
"product_id": 6,
"name": "カフェラテコーヒー",
"filename": "coffee06_cafelatte.png",
"price": 150,
"no_stock": 100,
},
{
"product_id": 7,
"name": "マキアートコーヒー",
"filename": "coffee07_cafe_macchiato.png",
"price": 150,
"no_stock": 100,
},
{
"product_id": 8,
"name": "モカコーヒー",
"filename": "coffee08_cafe_mocha.png",
"price": 150,
"no_stock": 100,
},
{
"product_id": 9,
"name": "カラメルコーヒー",
"filename": "coffee09_caramel_macchiato.png",
"price": 150,
"no_stock": 100,
},
{
"product_id": 10,
"name": "アイスコーヒー",
"filename": "coffee10_iced_coffee.png",
"price": 150,
"no_stock": 100,
},
{
"product_id": 11,
"name": "アイスミルクコーヒー",
"filename": "coffee11_iced_milk_coffee.png",
"price": 150,
"no_stock": 100,
},
{
"product_id": 12,
"name": "エスプレッソコーヒー",
"filename": "coffee12_espresso.png",
"price": 150,
"no_stock": 100,
},
# Tea
{
"product_id": 13,
"name": "レモンティー",
"filename": "tea_lemon.png",
"price": 100,
"no_stock": 100,
},
{
"product_id": 14,
"name": "ミルクティー",
"filename": "tea_milk.png",
"price": 100,
"no_stock": 100,
},
{
"product_id": 15,
"name": "ストレイトティー",
"filename": "tea_straight.png",
"price": 100,
"no_stock": 100,
},
# Others
{
"product_id": 16,
"name": "シュガー",
"filename": "cooking_sugar_stick.png",
"price": 0,
"no_stock": None,
},
{
"product_id": 17,
"name": "ミルクシロップ",
"filename": "sweets_milk_cream.png",
"price": 0,
"no_stock": None,
},
]
await self._insert_many([Product.model_validate(obj) for obj in products])
async with self._db.transaction():
await self._db.execute(sqlmodel.delete(Product))
await self._insert_many(products)

async def _empty(self) -> bool:
return await self._db.fetch_one(sqlmodel.select(Product)) is None
Expand All @@ -210,7 +107,7 @@ async def _insert_many(self, products: list[Product]) -> None:
await self._db.execute_many(query, [p.model_dump() for p in products])

async def select_all(self) -> list[Product]:
query = sqlmodel.select(Product)
query = sqlmodel.select(Product).order_by(col(Product.product_id).asc())
return [Product.model_validate(m) async for m in self._db.iterate(query)]

async def by_product_id(self, product_id: int) -> Product | None:
Expand All @@ -223,7 +120,7 @@ async def insert(self, product: Product) -> Product | None:
maybe_record = await self._db.fetch_one(query, product.model_dump())
if (record := maybe_record) is None:
return None
return Product.model_validate(record._mapping)
return Product.model_validate(dict(record._mapping))

async def update(self, product_id: int, new_product: Product) -> Product | None:
dump = new_product.model_dump()
Expand All @@ -246,4 +143,4 @@ async def update(self, product_id: int, new_product: Product) -> Product | None:
maybe_record = await self._db.fetch_one(query)
if (record := maybe_record) is None:
return None
return Product.model_validate(record._mapping)
return Product.model_validate(dict(record._mapping))
18 changes: 18 additions & 0 deletions static/product-list.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"product_id","name","filename","price","no_stock"
1,"ブレンドコーヒー","coffee01_blend.png",150,100
2,"アメリカンコーヒー","coffee02_american.png",150,100
3,"カフェオレコーヒー","coffee03_cafeole.png",150,100
4,"ブレンドブラックコーヒー","coffee04_blend_black.png",150,100
5,"カプチーノコーヒー","coffee05_cappuccino.png",150,100
6,"カフェラテコーヒー","coffee06_cafelatte.png",150,100
7,"マキアートコーヒー","coffee07_cafe_macchiato.png",150,100
8,"モカコーヒー","coffee08_cafe_mocha.png",150,100
9,"カラメルコーヒー","coffee09_caramel_macchiato.png",150,100
10,"アイスコーヒー","coffee10_iced_coffee.png",150,100
11,"アイスミルクコーヒー","coffee11_iced_milk_coffee.png",150,100
12,"エスプレッソコーヒー","coffee12_espresso.png",150,100
13,"レモンティー","tea_lemon.png",100,100
14,"ミルクティー","tea_milk.png",100,100
15,"ストレイトティー","tea_straight.png",100,100
16,"シュガー","cooking_sugar_stick.png",0,
17,"ミルクシロップ","sweets_milk_cream.png",0,