From 03d1c85b1acb2184c73651d679ff453afc0eef2f Mon Sep 17 00:00:00 2001 From: Alejandro Jaramillo Date: Sat, 5 Apr 2025 17:33:11 +0200 Subject: [PATCH 1/5] Add backend --- .gitignore | 2 + api/__init__.py | 0 api/__pycache__/database.cpython-312.pyc | Bin 0 -> 2956 bytes api/__pycache__/index.cpython-312.pyc | Bin 0 -> 6944 bytes api/__pycache__/models.cpython-312.pyc | Bin 0 -> 3108 bytes api/create_tables.py | 11 ++ api/database.py | 47 +++++++ api/index.py | 165 +++++++++++++++++++++++ api/models.py | 68 ++++++++++ docker-compose.yml | 10 ++ next.config.mjs | 25 ++++ requirements.txt | 11 ++ 12 files changed, 339 insertions(+) create mode 100644 api/__init__.py create mode 100644 api/__pycache__/database.cpython-312.pyc create mode 100644 api/__pycache__/index.cpython-312.pyc create mode 100644 api/__pycache__/models.cpython-312.pyc create mode 100644 api/create_tables.py create mode 100644 api/database.py create mode 100644 api/index.py create mode 100644 api/models.py create mode 100644 docker-compose.yml create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore index f650315..3465319 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ /.next/ /out/ +/api/.env + # production /build diff --git a/api/__init__.py b/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/__pycache__/database.cpython-312.pyc b/api/__pycache__/database.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..12c4c3f70353abd1717eb216356e528c9221e853 GIT binary patch literal 2956 zcmbVOU2Ggz6~1?V_kZo(bzCDmtbd5TD)u(Uc2w618h0CLiH*P}Qb&=7@y@k9&dls) zW*lctfK?MAq97${MeQm^lJ5jo=9IU_1ZVIFV9%$OYWb%GH$o8%^p z7(|-x)KdCVbfK#z?t5wC9b6UV1W7oI7LXG#6O)7%+z8!aYPKZkIFclc7T$>5VQbnH zX!&{$xrIUBKw9)N(qg2g;d4lfUq*7P)&%s05|?RyOebd;FPXQA>JmkDN>*MW)|_q; zFCtNCia3s5u)rzeR9RC!OKh)6BY8u$RaZYxlo{0_8Nm}?Ef|ZY<%zFbE|~)-Vam02 zYtG}Q$pzO7k{J!ltiQXw#%Rn2+t@jQYT*$zu6{-uzaccvB+#%o&hUUe z=tm7f%;$5$zAkdWFY4=JB)*W|zuYRn5oqLV)`D<`iBc#N_M)eCjT9tHHA;@|c(LvxM=I9YL}F=q~!6srI8Dd{|PZq(i5;24MUwV$YTvOpb#}Ot1cRDzJ^6V zH{CCawB0cvP%ro*TJQ}A$xWymu+)&c-v$=;7yJ~;aFfgNu>&hkFEgWQ51)XAGP>pk zW_5#Dst*ayA;zpj7m|K<^wrTb^J@OA>dqG|$7cuo)nd`-9CvYc_KaH8&j6rQ3dezB z$qOioO^UXnEFU_MaiFgMGDLPLDWk3C^jhk})(=|Og6oO(+}(-W6YF39RpVOH&jtq$p-VTQn#iQIvCws!_8<6$O@1{RT)R!c;pz$ze)@l+cu3Bwv6s zv0TM16^TuIl_wPKf?0I!2o<8$Bh($mh3c`xbYj%^T6FVPVaW?st5#D~*QV{{HnO{^ zQzs>*CtZS6;c5~}$V7>2d_I7r&d9?h- zE{FN!)$}y;fnuXO0A+=C5uD_=fUF?50j_$>8bX(_hJOGcdzV>Zf5P+VhbV|HqK~+D z*%gd|Tc7~^!l=)_1d#OSvemiJej%EM>jiTON_&57j7sbNQ zc=&=Pbb);nEzuag-RLOt<5M3$KlO3&5?my2f-lWg_5O%m!Wm|{H%E#DiqOiJ(z69S zJx5#@x@6k#zUka}y4GkXp(3KymR!?#eBLNPtA#49X{yEHAGs$$AVr(82Vs*a5FA>f ze?@y`^p|-1)gM1a9N)D=$y)CY!v?#y5^Yx}K25Z(^xx=zC;Kqcc|X$mGzfOz^9l9b z+X3p_Bo%VdM=dPbSE~I-aE}g(Pu*3^*v%vivOBr^Bmx9c#Bye}J zBntO9;C>kf>u<2s%T7P=%rr3B(6&5)_abk8V0>}p@mudAt>sQk7Do@*nqFX<9r`T4TCM$E?&8K^+!(; z=0|oYSxarj64%Tt=B?cO6YottY#;hV`_O~f@Wa6H{lM^37F>MiC)9awD=@sbLv?_> z1D4O7(~#t?Ht9INiKQ;#77N__{Zb!WiD0Qms6;u+H-|x8X~)tLuF@e;z6VqN5ls0$ zK^he+16Vp5sbodUkBFepw0Vc%mqRu3^TaF-KqDUTD^f4mEF`s|=8+fM=TWdtn_|YM zAn`)aE&DD4{|=xqsWx@L+cO2H*4?XrKz5AIrzH))Lw1}x4^fh(ho`mfZ?@e} zxo%2m|9=)3t6onh>~RojyLHY1fepm?6O{V|y$tjr%KZriKSmuNqr;C-_;U2q_zOR^ zua&NpRu4aj_mv~tVsa}vSU&YcK%tJ+Gxxa7;BZ;kZfajWy?*Mq!<$XtDMufPtq;Vu bGWVGf#>cPcK11;OTf2a}b`vZf@t5>3(lN$W literal 0 HcmV?d00001 diff --git a/api/__pycache__/index.cpython-312.pyc b/api/__pycache__/index.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4739f6c641af709d7d824d5cc9dfbef71f3f94be GIT binary patch literal 6944 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 oddDZRM1wc73p;N*2xw7?B1PQ z9JNxU1j%_bPw+2P{3(4Qavt23P$TuJZ-uz3L|;0y=Ongkq@J{Md*9CP&U`aFd%q+S zQ3B7^TQ64rQ3&}RKlUH^7SG;Gcs(W-v7{0yOLrOmrHV)=4n~UD-sEj z8^ofU#8Q~_R1TKNs{&O44Kwus4GUBQG}41=0*wM1>scWp&^Vxp9yBV@B%rAtG$zmy zK+`>FT%eOb>DGcGb5l zj#=XLmhJh4z}Y}{JtupY;QKLQBrjPcANmSd%*%{2#R^@95z&*eXVJXM!i-KLs#pqQ zb*ig8{k3he$~D-g?%5vNvUU<>wu~laixoHfJ!)jlu1Xe6G`Uu^OUyCLjK>QV$7haj z`1LBwakc7J%2i+2xMmorU>H1V7_eHcg#EZ-d{;9|-I1_iSd{|6vgf-zWf-R8RD2Wt z_6);~ft4<1%FkpEBzbji;nMP|S-5ZdtDqse@6Mams_1!styo+(tM;<6-8);YyBe;5 zLWSm?0P&FgO3&;j(;G|e(eV}?Z)dXWpEhnb?`|#a(5ySQKVdlE85n*&-*s81e5{Yr z1(!ic*XBfo1)T*%Y-c5F4lv6T`y3lSd*J7E)s3Sy2^2{bDHJ0p(kMnz2!oGcZ(?7d zu7}*Auk*WO6FYPw&_k6z$0z{!>|F=(ddHXwNh1;ol1Q;ArjJN%1yWmm8p0(A$!qm6 z1b)1afIG~qLaf-@sY41}=GRF1~ButNF|upp~n^RCgNon}X(HQQIWD@sSrYi5v@v zodGPTM+Vjw0pqgm_(8x#=#X+3^#?X`@oWl#(e$z)Fn(uuwb;gu_Si&=PP9jkwdk?G zsjNg_5)i@OIIl0LG64AOodI!3(P4EERY2|52&kDsXrUe`1N8t4&)S*cfh)mSwo>d% zV&-wh_E{NTg-XqV9M*r7&=JDNyJdhLh~?lk&g4bvDCAXH@0J^Tl920 zmfZX-AORKs9|;T!q$ObnGX;7Q8=xlp*qUk--cj#2$DXI_m6btpBr@|j4h#a1D9>2RpqYVzRw%DP(d-KXU~twB zFi}JS(|Ry*FRze%ghdbZR7_QHILyP>&H5699|GAj>p32|2RF(6qUrfuDb<|9DpxJr zahS_vu86eZvZANQULQ^LTHLShdF*|F0+TEV^T74;9uA~9Ad8Ao*G`T}(f+WBdhqH@*4`&w+ zZeO|sV{k1o?LR56AzfdTOou1DZn79ydU!Zs$m0~K^oCS!kr`>QAmBoz_Z9pUGz?IK ze_<|)YtXutyFE68{wLDS(_70eI@vy+S-;*m)%am+euriP-*HM#PjRhUx3IhvTp3rw z8-lCwKZR!$e}4iS#J`NVFBfLZ+8g1JMpL#eU_Q;5O{ZvQz?a7@ItL* sa}t%3owzJz8sA_)(}_z`rg^bL;MLI-Dci7d)hy~yH|l?Z&a|N5U%T#;pa1{> literal 0 HcmV?d00001 diff --git a/api/create_tables.py b/api/create_tables.py new file mode 100644 index 0000000..9efc5b4 --- /dev/null +++ b/api/create_tables.py @@ -0,0 +1,11 @@ +import asyncio +from database import init_db + +async def create_tables(): + """Initialize the database tables.""" + print("Creating database tables...") + await init_db() + print("Database tables created successfully!") + +if __name__ == "__main__": + asyncio.run(create_tables()) \ No newline at end of file diff --git a/api/database.py b/api/database.py new file mode 100644 index 0000000..9423f84 --- /dev/null +++ b/api/database.py @@ -0,0 +1,47 @@ +from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession +from sqlalchemy.orm import sessionmaker, declarative_base +from sqlalchemy import Column, Integer, String, Text, DateTime, JSON +import os +from datetime import datetime +from dotenv import load_dotenv + +load_dotenv() + +DATABASE_URL = os.getenv("DATABASE_URL") +if not DATABASE_URL: + DATABASE_URL = "sqlite+aiosqlite:///./test.db" # Fallback for development + +# Create async engine +engine = create_async_engine(DATABASE_URL, echo=True) +async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False) + +Base = declarative_base() + + +class VideoAnalysis(Base): + __tablename__ = "video_analyses" + + id = Column(Integer, primary_key=True, index=True) + filename = Column(String(255), nullable=False) + content_type = Column(String(100), nullable=False) + prompt = Column(Text, nullable=True) + analysis_text = Column(JSON, nullable=False) + created_at = Column(DateTime, default=datetime.utcnow) + + def __repr__(self): + return f"" + + +async def get_db(): + """Dependency for getting async DB session""" + async with async_session() as session: + try: + yield session + finally: + await session.close() + + +async def init_db(): + """Initialize the database with tables""" + async with engine.begin() as conn: + await conn.run_sync(Base.metadata.create_all) diff --git a/api/index.py b/api/index.py new file mode 100644 index 0000000..6cd4f5e --- /dev/null +++ b/api/index.py @@ -0,0 +1,165 @@ +from fastapi import FastAPI, HTTPException, UploadFile, File, Form, Depends +from fastapi.middleware.cors import CORSMiddleware +import os +import tempfile +import base64 +from google import genai +from google.genai import types +from dotenv import load_dotenv +from typing import Optional +import shutil +import json +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.future import select +from sqlalchemy import desc + +# Import database models +from database import get_db, VideoAnalysis, init_db + +# Import Pydantic models +from models import MealPlan + +GEMINI_API_KEY = os.getenv("GEMINI_API_KEY", None) + +# Load environment variables +load_dotenv() + +# Configure Gemini API + +app = FastAPI(title="Vercel FastAPI") + +# Add CORS middleware +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# Initialize database tables on startup +@app.on_event("startup") +async def startup_db_client(): + await init_db() + +@app.get("/") +async def root(): + return {"message": "Welcome to FastAPI on Vercel!"} + + +@app.get("/hello") +async def hello(): + return {"message": "Hello World"} + + +@app.post("/analyze-video", response_model=MealPlan) +async def analyze_video( + video: UploadFile = File(...), + db: AsyncSession = Depends(get_db), +): + if not GEMINI_API_KEY: + raise HTTPException(status_code=500, detail="Gemini API key not configured") + + if not video.content_type or not video.content_type.startswith("video/"): + raise HTTPException(status_code=400, detail="File must be a video") + + # Save uploaded file temporarily + with tempfile.NamedTemporaryFile( + delete=False, suffix=os.path.splitext(video.filename)[1] + ) as temp_video: + shutil.copyfileobj(video.file, temp_video) + temp_video_path = temp_video.name + + + + # Initialize Gemini client + client = genai.Client(api_key=GEMINI_API_KEY) + + # Read the video file and encode it + with open(temp_video_path, "rb") as f: + video_bytes = f.read() + video_base64 = base64.b64encode(video_bytes).decode("utf-8") + + # Define the output structure - now in the prompt + prompt = """ + Analyze this video showing food items and create a meal plan. + + Based on the food items visible in the video, generate: + 1. Recipes with detailed ingredients and instructions for the meal plans. Meal plans should be for the whole week. Monday to Sunday. + 2. A complete shopping list with all required ingredients for the meal plans if you don't have the ingredients in your kitchen. Which are not in the video. + + """ + + # Create content with video data + contents = [ + types.Content( + role="user", + parts=[ + types.Part.from_text(text=prompt), + types.Part( + inline_data=types.Blob( + mime_type=video.content_type, + data=video_bytes + ) + ), + ], + ), + ] + + # Generate content with temperature=0 and JSON response type + response = client.models.generate_content( + model="gemini-2.0-flash", + contents=contents, + config=types.GenerateContentConfig( + response_mime_type="application/json", + temperature=0, + response_schema=MealPlan + ) + ) + + + # Check if analysis already exists + result = await db.execute( + select(VideoAnalysis).order_by(desc(VideoAnalysis.created_at)).limit(1) + ) + existing_analysis = result.scalars().first() + + # If analysis exists, delete it + if existing_analysis: + await db.delete(existing_analysis) + await db.commit() + + # Create new analysis + analysis = VideoAnalysis( + filename=video.filename, + content_type=video.content_type, + prompt=prompt, + analysis_text=response.parsed.model_dump_json(), + ) + + db.add(analysis) + await db.commit() + await db.refresh(analysis) + + if os.path.exists(temp_video_path): + os.unlink(temp_video_path) + + return response.parsed + + + + + +@app.get("/analysis", response_model=MealPlan) +async def get_latest_analysis(db: AsyncSession = Depends(get_db)): + """Get the most recent video analysis""" + result = await db.execute( + select(VideoAnalysis).order_by(desc(VideoAnalysis.created_at)).limit(1) + ) + analysis = result.scalars().first() + + if not analysis: + raise HTTPException(status_code=404, detail="No analysis found") + + # Return the analysis_text directly which contains the meal plan + return analysis.analysis_text diff --git a/api/models.py b/api/models.py new file mode 100644 index 0000000..065a74f --- /dev/null +++ b/api/models.py @@ -0,0 +1,68 @@ +from pydantic import BaseModel +from datetime import datetime +from typing import Optional, List + + +class VideoAnalysisBase(BaseModel): + """Base model for video analysis data""" + + filename: str + content_type: str + prompt: Optional[str] = None + + +class VideoAnalysisCreate(VideoAnalysisBase): + """Model for creating a video analysis""" + + analysis_text: str + + +class VideoAnalysisResponse(VideoAnalysisBase): + """Model for returning a video analysis""" + + id: int + analysis_text: str + created_at: datetime + + class Config: + from_attributes = ( + True # Enables ORM mode (renamed from orm_mode in Pydantic v2) + ) + + +class VideoAnalysisList(BaseModel): + """Model for returning a list of video analyses""" + + items: list[VideoAnalysisResponse] + count: int + + +# Meal planning models +class Ingredient(BaseModel): + name: str + quantity: float + unit: str + + +class Recipe(BaseModel): + name: str + ingredients: List[Ingredient] + instructions: List[str] + + +class ShoppingList(BaseModel): + items: List[Ingredient] + + +class DayMeal(BaseModel): + day: str # Monday, Tuesday, etc. + breakfast: Optional[str] = None + lunch: Optional[str] = None + dinner: Optional[str] = None + recipe_refs: List[str] = [] # References to recipes by name + + +class MealPlan(BaseModel): + shopping_list: ShoppingList + recipes: List[Recipe] + days: List[DayMeal] = [] # Weekly meal plan organized by days diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..b6f344e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,10 @@ + +services: + postgres: + image: postgres:15 + environment: + POSTGRES_USER: user + POSTGRES_PASSWORD: password + POSTGRES_DB: dbname + ports: + - "5432:5432" \ No newline at end of file diff --git a/next.config.mjs b/next.config.mjs index db42585..86c7a23 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -27,6 +27,31 @@ const nextConfig = { parallelServerBuildTraces: true, parallelServerCompiles: true, }, + rewrites: async () => { + return [ + { + source: "/api/py/:path*", + destination: + process.env.NODE_ENV === "development" + ? "http://127.0.0.1:8000/api/py/:path*" + : "/api/", + }, + { + source: "/docs", + destination: + process.env.NODE_ENV === "development" + ? "http://127.0.0.1:8000/api/py/docs" + : "/api/py/docs", + }, + { + source: "/openapi.json", + destination: + process.env.NODE_ENV === "development" + ? "http://127.0.0.1:8000/api/py/openapi.json" + : "/api/py/openapi.json", + }, + ]; + }, } if (userConfig) { diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f4c5028 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,11 @@ +fastapi==0.104.1 +uvicorn==0.23.2 +mangum==0.17.0 +pydantic==2.4.2 +python-multipart==0.0.6 +google-generativeai==0.3.1 +python-dotenv==1.0.0 +sqlalchemy==2.0.23 +psycopg2-binary==2.9.9 +asyncpg==0.28.0 +aiosqlite==0.19.0 \ No newline at end of file From 6f7ef6c2925af17b58b54b262064f692b065237a Mon Sep 17 00:00:00 2001 From: Alejandro Jaramillo Date: Sat, 5 Apr 2025 17:45:30 +0200 Subject: [PATCH 2/5] Change config --- next.config.mjs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/next.config.mjs b/next.config.mjs index 86c7a23..b44df68 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -30,25 +30,25 @@ const nextConfig = { rewrites: async () => { return [ { - source: "/api/py/:path*", + source: "/api/:path*", destination: process.env.NODE_ENV === "development" - ? "http://127.0.0.1:8000/api/py/:path*" + ? "http://127.0.0.1:8000/:path*" : "/api/", }, { source: "/docs", destination: process.env.NODE_ENV === "development" - ? "http://127.0.0.1:8000/api/py/docs" - : "/api/py/docs", + ? "http://127.0.0.1:8000/docs" + : "/api/docs", }, { source: "/openapi.json", destination: process.env.NODE_ENV === "development" - ? "http://127.0.0.1:8000/api/py/openapi.json" - : "/api/py/openapi.json", + ? "http://127.0.0.1:8000/openapi.json" + : "/api/openapi.json", }, ]; }, From de3ba7e7122f0861e0fdd9badb7e581a71ce4dba Mon Sep 17 00:00:00 2001 From: Alejandro Jaramillo Date: Sat, 5 Apr 2025 17:51:38 +0200 Subject: [PATCH 3/5] Add genai requirement --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f4c5028..2deae18 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,4 +8,5 @@ python-dotenv==1.0.0 sqlalchemy==2.0.23 psycopg2-binary==2.9.9 asyncpg==0.28.0 -aiosqlite==0.19.0 \ No newline at end of file +aiosqlite==0.19.0 +google-genai \ No newline at end of file From 19a8b04d48212dfa6f98c3aef391cd05c22f8d6a Mon Sep 17 00:00:00 2001 From: Alejandro Jaramillo Date: Sat, 5 Apr 2025 18:09:37 +0200 Subject: [PATCH 4/5] Save --- api/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 146 bytes api/__pycache__/index.cpython-312.pyc | Bin 6944 -> 6959 bytes api/index.py | 4 ++-- api/logic/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 152 bytes .../__pycache__/database.cpython-312.pyc | Bin 0 -> 2962 bytes api/logic/__pycache__/models.cpython-312.pyc | Bin 0 -> 3114 bytes api/{ => logic}/create_tables.py | 2 +- api/{ => logic}/database.py | 0 api/{ => logic}/models.py | 0 10 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 api/__pycache__/__init__.cpython-312.pyc create mode 100644 api/logic/__init__.py create mode 100644 api/logic/__pycache__/__init__.cpython-312.pyc create mode 100644 api/logic/__pycache__/database.cpython-312.pyc create mode 100644 api/logic/__pycache__/models.cpython-312.pyc rename api/{ => logic}/create_tables.py (86%) rename api/{ => logic}/database.py (100%) rename api/{ => logic}/models.py (100%) diff --git a/api/__pycache__/__init__.cpython-312.pyc b/api/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..49c3e65d147a011baacf59b6c60365b1cc87a1db GIT binary patch literal 146 zcmX@j%ge<81fu>Q(?RrO5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!GIX|z$w*AjPAtjD z&npHqjdT+W3c#%5lG3!an8bq2nE3e2yv&mLc)fzkUmP~M`6;D2sdh!IKob~&xERFv N$jr#dSi}ru0RZ|rB8vb3 literal 0 HcmV?d00001 diff --git a/api/__pycache__/index.cpython-312.pyc b/api/__pycache__/index.cpython-312.pyc index 4739f6c641af709d7d824d5cc9dfbef71f3f94be..de7fa056be6fc7d8a23766df05dd53ab4e011c12 100644 GIT binary patch delta 92 zcmZ2rw%&~IG%qg~0}yzJeoW^V-pD5s8zp^nfDs7TdU<3e=&KZXQ delta 60 zcmZ2)w!n< TFamKgi1Cq`k&&^88OQxzw;qVH3(lCD_%FjoYM#sDWC_2x&U(9ANNV)-!|g zQny{jR#nTDY$s|Uva8UiVwuQ(=tCbWRr^*c4{>TF!&FgXwGZ_hE|E;FsO^7dFNUb9 z+Kx2;{I~P}XU_Tl|C|qFu`q(C`Sr*1A8`o%nF_TBWdGUQhtDcfk&1O>U4f3Sc8)|UuJaP5G26}N5Qh)Pl4&gwBK2IDwAZp5XyujBQE(I7Qo z6hNfmb~U9h#TGlO;(?DU+{P7AN|K}#P=ituMq-jwL!05-fvPP9I+mnJK#gohZ!=YG z6KJ_w4yieSzKzt_C8WklbKU2Nnz)3d7PSHBb451O_=pb91iVzxl$Lo&R_zijDChUp2fnl70KCw|Jc zHFMr$r^$KO3+5olv}O>GeQoOX$;k}%f-0z7Pyrv&Ek%`8%O&PHFPaml#c^@!75V#b zoLXsj&gz;=o>w%h!i{FL+5W6c9JgPcorb|I$uC$ZVU|41%5h|`ft)XBHjyn;&Rd3| zx%tXMRcL+xe+Tj^B1pn2k^(B07=Lv+HJ}DInfl7{R12@5vb8-@xlO*lC&9YCzK4hG zAwOyuVm=!a@paKdelcGcCyB-M!Rc1`&0sxWqZ)!eOcukLh!;Dpsl*aZMK3y<SsrZm!9iq=UbRy}eBQKB6EGYRiio0N$j?WJCDTRX1IquTj+>BDtW^`*_%g+F; z6bj1zLeUG#vP}xMEUz3nl()a8|0=|HC@G=s#`JpAuUmfAvL4zAOu^q2bc_PNMnx+}hMTXEt8hN(_{~ zvy*Cl@7Q|k#`AZ%t<-So)J`mQEw?_n73(dX+-dsC+Th05wwhijP3$zbtOYlE?__Uf zw;D%FukEpzJ6=goGao8Bx&lyKXb-`Deiz6pa_bPQHLgK)5v%xz0JV1mtISV14*dv) z&;|4X`!2JJF>s3%hF^MWqb~w5{jp4CEHq(=rs7)JT!aEY7#pJ^9zj3<2D~j@|4nQb z=Ec(3BZz}PSVrfWH_WvAzf>q4(g`#m_F8&6l;?IhGitkRgP8js8C7BpO_)vBgaBL0?pJOq-}S$hB$ zi2}i)h5A>tUqXLLv|ahh6U1_zyOgZ=>;{-n=XSF7%EV{M*44i2eeYx+L_6+9JD!BV z?t4C_p8LB&6zlx^wVt}qAEO;lcd669@Ab^}3&(|YZzaSN_&OFj{%r=hUtlrAta4%~ za5Dwm9V`m`T^6|CM8NucEcUQv!~*g&>$bh;7hOoUx(NE0gx&No3CED zeBsKEpCHT)?^3efv>i`gH7*-BbMH^QH}Rlt@Z+|@`|+U%!J&J>p(hNu_|A{1^Zs^l zXn&XL0C|TjUpS{B$y=@BaeM=do%~G(xcB?SUZxzyVmDuov6OF&fV$j<#ba!_ou_;^ zrut);^1Zw`DwO-N_+qr26(~O}fIide9f6C7qURTgQS66C-0xSUX4woRwV~#b7eC-( z*@jIqV^ffL;b)cu2Z4VEP?%Jjy5H>smQ(HSm3xpKr{if!!==bhQ0GBPvXqQa;&*sj z>;7WfeU$5>g!ccZ!LiEmbiy77k+xgsED%^gj6X%WPtiAlK0vuYq0lF&{S$QbA&Ojz zeU^CcXZF?N<>K1W`-$FCbVo>Srv^%=9`h*NzBY50-3kqr_??EfwbL7?{xG!F@V!#( hq0n+)Xf3gy^AUXfTJCcMpTD*7xN|Sb;9-AC{{jdp$g%(c literal 0 HcmV?d00001 diff --git a/api/logic/__pycache__/models.cpython-312.pyc b/api/logic/__pycache__/models.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b55b2c4141348226646703a85bf819b7b8a9daca GIT binary patch literal 3114 zcma)8-)|d55Z?3cozK7HxN)1tX`H4MPBcY;TH2}wN(ya&q^JcXtVpM;Z=D>q&*tt7 zbp%qRq>}UIKDGY>>Yu_(E9b$iR1t|M-e_ZlR9=|ba}wLQ5-084-nX;6GvCb4-Y0<9SPGLK%fS)_RiG-MVWu9SVS#FZM*2`qpiw|$eJex+8V5AdheidO1T@u$#soSB zXu1!L3v?XNOdpyM=n+6C`p{$}s~`OXjn^d}`N;IxQq^K*_|PoVXTDuwdWdT`Yrb7| z%rd8++n!$xoDF2pbFy~{z8?}s3X(+%p|60&g3KsWtk7i`5j`1u7A>eO%;+Sdilrdd zpt{P_U)vU|UWIKMp6#J6>nBm>D`--_RCV*;qekBBspP>#lWQfr%p9}Ac)VD3eCGIu z->9)=uGZXYrRM7z*9-#{41-4v16He-u^%^#@9Ji`Hxf1st6BtD_I#J848wGss&As- zo?*B#u+qg$`I+p2Brne`Tv}N(i+4?b4Kzgeoq4lX6Ftwbmr5&U&0Z;2SMB18@W4A; zYq%P&gF=Pood9v4{7TR4Cexcso$-k_o#^E98=o|9v~F)N?a;iN*`F|)^&AYpn)QaP zQ=S>%b-`s2*!4LPXF+Na5!+eKn?wBa#6Hu8&+hrly6VQ!ngog@iWG`56loOWD1^aB zu$SEzsOuqv=4OB z5z^(Cxskh{U8p)Gd-cm@oeq)8Qzgh-*u{74J9VFVL$q==nCec$epApqENxq4H$L`U zCXr(Su`_@z>ye?gMZmahJAM!_5jv#YMg5_TTs)ydU^Klv2#nv^T`jhGy_3ndX|^+V ztWA&oO=Ttef`AD2#(4ull>xwK?+l1TiVkaps0C`TQb6SlLJL(v8LA3ce%8;74qO?= z^3~En5;Ko0w$Cc?Dpum;T-Tp z6+zwiM`3dB`_L=(7Ax8{=Dq>T@EDxFo{Juj+YW@IUKG`LSq~4#?7%k{%c$^T*u8li zK=Bqd56k4fd+CYRw@=fvZ93Z-&$he=`VO56JVo`xAU=+oHLftffy+RQZ=88$O8&b{yvNm@6V}xUA&qu~$bEy%x8udmel5p}-^y!aQ`nyo&=UE<*D@00CS@;+r=* zGjkh@&3ljJ?Vs9ot}`{W@p1D)Yhn9Jo6hvzu5Fs{OwVk5);iKEKA7C0GeHvIilf;@ zzelqR2X`Rdfibw2nD(EP*O0C+%BI5;UN2b;EIm9NFywIxRC*&Sx5$h%SP*a_()$X2 z3L1u}!GAFq#Z_p%%H7G#p#O<<>-6?Yn@)C)=Qgf2Pc?top5LLlz;~Rotf#nEYgkxb zimr?+;SIr6_^ZM*ia$Yt4dP!$T=M9rD2>5U6vhdIg>k}~K?`4Mm*JlX{=?pPKZLn> zhk8GP_*0f7=@~ikj7 Date: Sat, 5 Apr 2025 18:19:39 +0200 Subject: [PATCH 5/5] Save --- api/__pycache__/__init__.cpython-312.pyc | Bin 146 -> 0 bytes api/__pycache__/database.cpython-312.pyc | Bin 2956 -> 0 bytes api/__pycache__/index.cpython-312.pyc | Bin 6959 -> 0 bytes api/__pycache__/models.cpython-312.pyc | Bin 3108 -> 0 bytes api/index.py | 4 ++-- 5 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 api/__pycache__/__init__.cpython-312.pyc delete mode 100644 api/__pycache__/database.cpython-312.pyc delete mode 100644 api/__pycache__/index.cpython-312.pyc delete mode 100644 api/__pycache__/models.cpython-312.pyc diff --git a/api/__pycache__/__init__.cpython-312.pyc b/api/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 49c3e65d147a011baacf59b6c60365b1cc87a1db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 146 zcmX@j%ge<81fu>Q(?RrO5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!GIX|z$w*AjPAtjD z&npHqjdT+W3c#%5lG3!an8bq2nE3e2yv&mLc)fzkUmP~M`6;D2sdh!IKob~&xERFv N$jr#dSi}ru0RZ|rB8vb3 diff --git a/api/__pycache__/database.cpython-312.pyc b/api/__pycache__/database.cpython-312.pyc deleted file mode 100644 index 12c4c3f70353abd1717eb216356e528c9221e853..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2956 zcmbVOU2Ggz6~1?V_kZo(bzCDmtbd5TD)u(Uc2w618h0CLiH*P}Qb&=7@y@k9&dls) zW*lctfK?MAq97${MeQm^lJ5jo=9IU_1ZVIFV9%$OYWb%GH$o8%^p z7(|-x)KdCVbfK#z?t5wC9b6UV1W7oI7LXG#6O)7%+z8!aYPKZkIFclc7T$>5VQbnH zX!&{$xrIUBKw9)N(qg2g;d4lfUq*7P)&%s05|?RyOebd;FPXQA>JmkDN>*MW)|_q; zFCtNCia3s5u)rzeR9RC!OKh)6BY8u$RaZYxlo{0_8Nm}?Ef|ZY<%zFbE|~)-Vam02 zYtG}Q$pzO7k{J!ltiQXw#%Rn2+t@jQYT*$zu6{-uzaccvB+#%o&hUUe z=tm7f%;$5$zAkdWFY4=JB)*W|zuYRn5oqLV)`D<`iBc#N_M)eCjT9tHHA;@|c(LvxM=I9YL}F=q~!6srI8Dd{|PZq(i5;24MUwV$YTvOpb#}Ot1cRDzJ^6V zH{CCawB0cvP%ro*TJQ}A$xWymu+)&c-v$=;7yJ~;aFfgNu>&hkFEgWQ51)XAGP>pk zW_5#Dst*ayA;zpj7m|K<^wrTb^J@OA>dqG|$7cuo)nd`-9CvYc_KaH8&j6rQ3dezB z$qOioO^UXnEFU_MaiFgMGDLPLDWk3C^jhk})(=|Og6oO(+}(-W6YF39RpVOH&jtq$p-VTQn#iQIvCws!_8<6$O@1{RT)R!c;pz$ze)@l+cu3Bwv6s zv0TM16^TuIl_wPKf?0I!2o<8$Bh($mh3c`xbYj%^T6FVPVaW?st5#D~*QV{{HnO{^ zQzs>*CtZS6;c5~}$V7>2d_I7r&d9?h- zE{FN!)$}y;fnuXO0A+=C5uD_=fUF?50j_$>8bX(_hJOGcdzV>Zf5P+VhbV|HqK~+D z*%gd|Tc7~^!l=)_1d#OSvemiJej%EM>jiTON_&57j7sbNQ zc=&=Pbb);nEzuag-RLOt<5M3$KlO3&5?my2f-lWg_5O%m!Wm|{H%E#DiqOiJ(z69S zJx5#@x@6k#zUka}y4GkXp(3KymR!?#eBLNPtA#49X{yEHAGs$$AVr(82Vs*a5FA>f ze?@y`^p|-1)gM1a9N)D=$y)CY!v?#y5^Yx}K25Z(^xx=zC;Kqcc|X$mGzfOz^9l9b z+X3p_Bo%VdM=dPbSE~I-aE}g(Pu*3^*v%vivOBr^Bmx9c#Bye}J zBntO9;C>kf>u<2s%T7P=%rr3B(6&5)_abk8V0>}p@mudAt>sQk7Do@*nqFX<9r`T4TCM$E?&8K^+!(; z=0|oYSxarj64%Tt=B?cO6YottY#;hV`_O~f@Wa6H{lM^37F>MiC)9awD=@sbLv?_> z1D4O7(~#t?Ht9INiKQ;#77N__{Zb!WiD0Qms6;u+H-|x8X~)tLuF@e;z6VqN5ls0$ zK^he+16Vp5sbodUkBFepw0Vc%mqRu3^TaF-KqDUTD^f4mEF`s|=8+fM=TWdtn_|YM zAn`)aE&DD4{|=xqsWx@L+cO2H*4?XrKz5AIrzH))Lw1}x4^fh(ho`mfZ?@e} zxo%2m|9=)3t6onh>~RojyLHY1fepm?6O{V|y$tjr%KZriKSmuNqr;C-_;U2q_zOR^ zua&NpRu4aj_mv~tVsa}vSU&YcK%tJ+Gxxa7;BZ;kZfajWy?*Mq!<$XtDMufPtq;Vu bGWVGf#>cPcK11;OTf2a}b`vZf@t5>3(lN$W diff --git a/api/__pycache__/index.cpython-312.pyc b/api/__pycache__/index.cpython-312.pyc deleted file mode 100644 index de7fa056be6fc7d8a23766df05dd53ab4e011c12..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6959 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 diff --git a/api/__pycache__/models.cpython-312.pyc b/api/__pycache__/models.cpython-312.pyc deleted file mode 100644 index 39aab2f7f9bc35f794f295b0a3657aa281df6900..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3108 zcma)8-)|E~5Z?3cozK7Hgakqy;t)!EDgm`sDIh4Np@p=>RM1wc73p;N*2xw7?B1PQ z9JNxU1j%_bPw+2P{3(4Qavt23P$TuJZ-uz3L|;0y=Ongkq@J{Md*9CP&U`aFd%q+S zQ3B7^TQ64rQ3&}RKlUH^7SG;Gcs(W-v7{0yOLrOmrHV)=4n~UD-sEj z8^ofU#8Q~_R1TKNs{&O44Kwus4GUBQG}41=0*wM1>scWp&^Vxp9yBV@B%rAtG$zmy zK+`>FT%eOb>DGcGb5l zj#=XLmhJh4z}Y}{JtupY;QKLQBrjPcANmSd%*%{2#R^@95z&*eXVJXM!i-KLs#pqQ zb*ig8{k3he$~D-g?%5vNvUU<>wu~laixoHfJ!)jlu1Xe6G`Uu^OUyCLjK>QV$7haj z`1LBwakc7J%2i+2xMmorU>H1V7_eHcg#EZ-d{;9|-I1_iSd{|6vgf-zWf-R8RD2Wt z_6);~ft4<1%FkpEBzbji;nMP|S-5ZdtDqse@6Mams_1!styo+(tM;<6-8);YyBe;5 zLWSm?0P&FgO3&;j(;G|e(eV}?Z)dXWpEhnb?`|#a(5ySQKVdlE85n*&-*s81e5{Yr z1(!ic*XBfo1)T*%Y-c5F4lv6T`y3lSd*J7E)s3Sy2^2{bDHJ0p(kMnz2!oGcZ(?7d zu7}*Auk*WO6FYPw&_k6z$0z{!>|F=(ddHXwNh1;ol1Q;ArjJN%1yWmm8p0(A$!qm6 z1b)1afIG~qLaf-@sY41}=GRF1~ButNF|upp~n^RCgNon}X(HQQIWD@sSrYi5v@v zodGPTM+Vjw0pqgm_(8x#=#X+3^#?X`@oWl#(e$z)Fn(uuwb;gu_Si&=PP9jkwdk?G zsjNg_5)i@OIIl0LG64AOodI!3(P4EERY2|52&kDsXrUe`1N8t4&)S*cfh)mSwo>d% zV&-wh_E{NTg-XqV9M*r7&=JDNyJdhLh~?lk&g4bvDCAXH@0J^Tl920 zmfZX-AORKs9|;T!q$ObnGX;7Q8=xlp*qUk--cj#2$DXI_m6btpBr@|j4h#a1D9>2RpqYVzRw%DP(d-KXU~twB zFi}JS(|Ry*FRze%ghdbZR7_QHILyP>&H5699|GAj>p32|2RF(6qUrfuDb<|9DpxJr zahS_vu86eZvZANQULQ^LTHLShdF*|F0+TEV^T74;9uA~9Ad8Ao*G`T}(f+WBdhqH@*4`&w+ zZeO|sV{k1o?LR56AzfdTOou1DZn79ydU!Zs$m0~K^oCS!kr`>QAmBoz_Z9pUGz?IK ze_<|)YtXutyFE68{wLDS(_70eI@vy+S-;*m)%am+euriP-*HM#PjRhUx3IhvTp3rw z8-lCwKZR!$e}4iS#J`NVFBfLZ+8g1JMpL#eU_Q;5O{ZvQz?a7@ItL* sa}t%3owzJz8sA_)(}_z`rg^bL;MLI-Dci7d)hy~yH|l?Z&a|N5U%T#;pa1{> diff --git a/api/index.py b/api/index.py index c24b6f4..e9aabc4 100644 --- a/api/index.py +++ b/api/index.py @@ -14,10 +14,10 @@ from sqlalchemy import desc # Import database models -from logic.database import get_db, VideoAnalysis, init_db +from .logic.database import get_db, VideoAnalysis, init_db # Import Pydantic models -from logic.models import MealPlan +from .logic.models import MealPlan GEMINI_API_KEY = os.getenv("GEMINI_API_KEY", None)