diff --git a/memory-game/mg-backend/ai.py b/memory-game/mg-backend/ai.py index 6911f39..6df7e84 100644 --- a/memory-game/mg-backend/ai.py +++ b/memory-game/mg-backend/ai.py @@ -16,6 +16,7 @@ class Topics(BaseModel): class Results(BaseModel): topic: str + alt: str medium_url: str photo_id: int photo_url: str @@ -35,7 +36,7 @@ class AI: ) prompt = """ - Return 12 items for a toddler aged 2 to 3 to choose from in a memory game. + Return 12 items for a toddler aged 2 to 3 to choose from in a memory game. e.g. "ball", "car" . Don't use my examples words """ response = model.generate_content(prompt).text @@ -43,9 +44,11 @@ class AI: def generate_card_json(self, photo_payload, topic): prompt = f""" - This JSON payload will need to be analayzed. Your job is to pick the alt field with the best match for the toddler matching game. return the properties of the object + This JSON payload will be analyzed for a toddler matching game. Your job is to identify which object within the payload that best matches the topic "{topic}". + Define a "best match" as containing the keywords from the topic within the "alt" field of the image object that would be appropriate for a toddler's matching game. a "best match" should also be the primary subject in the photo, if there are other items in the alt text then that's determined to be a bad match. + + if its determine the alt text is not a great match for the toddler game set the `bad_match` to True and for best matches set to False {photo_payload} - and the original topic is {topic}. If the alt text and topic are a bad match set the bad_match bool. """ model = genai.GenerativeModel( diff --git a/memory-game/mg-backend/crud.py b/memory-game/mg-backend/crud.py new file mode 100644 index 0000000..a229555 --- /dev/null +++ b/memory-game/mg-backend/crud.py @@ -0,0 +1,20 @@ +from sqlalchemy.orm import Session + +from . import models, schemas + + +def get_card(db: Session, card_id: int): + return db.query(models.Card).filter(models.Card.id == card_id).first() + + +def get_cards(db: Session, skip: int = 0, limit: int = 100): + return db.query(models.Card).offset(skip).limit(limit).all() + + +def create_card(db: Session, card: schemas.CardCreate): + db_card = models.Card(**card.model_dump()) + db.add(db_card) + db.commit() + db.refresh(db_card) + + return db_card diff --git a/memory-game/mg-backend/database.py b/memory-game/mg-backend/database.py new file mode 100644 index 0000000..e03c275 --- /dev/null +++ b/memory-game/mg-backend/database.py @@ -0,0 +1,13 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker + +SQLALCHEMY_DATABASE_URL = "sqlite:///./memory_game.db" + +engine = create_engine( + SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} +) + +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +Base = declarative_base() diff --git a/memory-game/mg-backend/main.py b/memory-game/mg-backend/main.py index 697b270..17f8abb 100644 --- a/memory-game/mg-backend/main.py +++ b/memory-game/mg-backend/main.py @@ -1,12 +1,18 @@ from .ai import AI from .test_data import data from dotenv import load_dotenv -from fastapi import FastAPI +import json +from fastapi import Depends, FastAPI from fastapi.middleware.cors import CORSMiddleware import logging +import requests import os from .photos import Pictures +from . import crud, models, schemas +from .database import SessionLocal, engine +from sqlalchemy.orm import Session + logger = logging.getLogger("uvicorn.error") logger.setLevel(logging.DEBUG) load_dotenv() @@ -24,6 +30,17 @@ app.add_middleware( allow_headers=["*"], ) +models.Base.metadata.create_all(bind=engine) + + +## DB Deps +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + PEXELS_API_KEY = os.getenv("PEXELS_API_KEY") photos = Pictures(PEXELS_API_KEY) @@ -51,3 +68,43 @@ async def read_main(): # except Exception as e: # logger.error(e) # return {"error": "uname to handle request"} + + +@app.post("/ai-cards", response_model=schemas.Card) +def ai_cards(card: schemas.CardCreate, db: Session = Depends(get_db)): + return crud.create_card(db=db, card=card) + + +@app.get("/cards") +def read_cards(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): + cards = crud.get_cards(db, skip=skip, limit=limit) + return cards + + +@app.get("/load-data") +def load_data(): + topics = ai.generate_topics() + + try: + for item in topics: + + logger.info(item) + picture_data = photos.search(item["topic"]) + logger.info(picture_data) + break + + card_json = ai.generate_card_json(picture_data, item) + logger.info(card_json) + + r = requests.post( + "http://127.0.0.1:8000/ai-cards", + json=card_json, + headers={"Content-Type": "application/json"}, + ) + logger.info(r.json()) + + return {"success": "entered into the db"} + + except Exception as e: + logger.error(e) + return {"error": "uname to handle request"} diff --git a/memory-game/mg-backend/models.py b/memory-game/mg-backend/models.py index e69de29..8b795da 100644 --- a/memory-game/mg-backend/models.py +++ b/memory-game/mg-backend/models.py @@ -0,0 +1,15 @@ +from sqlalchemy import Boolean, Column, Integer, String + +from .database import Base + + +class Card(Base): + __tablename__ = "card" + + id = Column(Integer, primary_key=True) + photo_id = Column(Integer) + medium_url = Column(String) + photo_url = Column(String) + topic = Column(String, index=True) + bad_match = Column(Boolean) + alt = Column(String) diff --git a/memory-game/mg-backend/schemas.py b/memory-game/mg-backend/schemas.py new file mode 100644 index 0000000..d76190e --- /dev/null +++ b/memory-game/mg-backend/schemas.py @@ -0,0 +1,21 @@ +from pydantic import BaseModel + + +class CardBase(BaseModel): + photo_id: int + medium_url: str + photo_url: str + topic: str + alt: str + bad_match: bool + + +class CardCreate(CardBase): + pass + + +class Card(CardBase): + id: int + + class Config: + orm_mode = True diff --git a/memory-game/mg-frontend/src/App.jsx b/memory-game/mg-frontend/src/App.jsx index cb82c39..fc29d8d 100644 --- a/memory-game/mg-frontend/src/App.jsx +++ b/memory-game/mg-frontend/src/App.jsx @@ -1,16 +1,34 @@ import { useState } from "react"; import "./App.css"; +import Button from "./components/button"; import GameBoard from "./components/gameboard"; import Scoreboard from "./components/scoreboard"; function App() { + const [gameStarted, setGameStarted] = useState(false); const [score, setScore] = useState(0); const [highScore, setHighScore] = useState(0); return ( <> - - + {!gameStarted ? ( + + ); +} diff --git a/memory-game/mg-frontend/src/components/card.jsx b/memory-game/mg-frontend/src/components/card.jsx index 1a1eca9..bff538b 100644 --- a/memory-game/mg-frontend/src/components/card.jsx +++ b/memory-game/mg-frontend/src/components/card.jsx @@ -4,7 +4,9 @@ export default function Card(props) { return (
onClick(event, props.setScore, props.score)} + onClick={(event) => + onClick(event, props.clicked, props.setScore, props.score) + } data-cardname={props.title} >
@@ -19,8 +21,9 @@ function Image(props) { return {props.alt}; } -function onClick(event, setScore, score) { +function onClick(event, clicked, setScore, score) { // implement something to handle the things ID setScore(score + 1); + clicked = true; console.log(event.currentTarget.dataset.cardname); } diff --git a/memory-game/mg-frontend/src/components/footer.jsx b/memory-game/mg-frontend/src/components/footer.jsx new file mode 100644 index 0000000..1334832 --- /dev/null +++ b/memory-game/mg-frontend/src/components/footer.jsx @@ -0,0 +1,7 @@ +export default function Footer() { + return ( + + ); +} diff --git a/memory-game/mg-frontend/src/components/gameboard.jsx b/memory-game/mg-frontend/src/components/gameboard.jsx index 8b9403b..03f1b2d 100644 --- a/memory-game/mg-frontend/src/components/gameboard.jsx +++ b/memory-game/mg-frontend/src/components/gameboard.jsx @@ -4,29 +4,50 @@ import "../styles/gameboard.css"; export default function GameBoard(props) { const [cards, setCards] = useState([]); + useEffect(() => { fetchCards({ setCards }); }, []); return ( -
- {cards.map((item) => { - return ( - - ); - })} -
+ <> + {props.gameStarted ? ( +
+ {cards.map((item) => { + console.log(item.topic, item.clicked); + return ( + + ); + })} +
+ ) : null} + ); } async function fetchCards({ setCards }) { - const cards = await fetch("http://localhost:8000/"); - setCards(await cards.json()); + const cards = await fetch("http://localhost:8000/cards"); + const jsonCards = await cards.json(); + + let gameCards = await jsonCards.map((c) => { + return { ...c, clicked: false, key: crypto.randomUUID() }; + }); + + setCards(gameCards); +} + +function shuffleCards(arr) { + for (let i = arr.length - 1; i > 0; i--) { + let j = Math.floor(Math.random() * (i + 1)); + [arr[i], arr[j]] = [arr[j], arr[i]]; + return arr; + } } diff --git a/memory-game/mg-frontend/src/components/scoreboard.jsx b/memory-game/mg-frontend/src/components/scoreboard.jsx index fa14822..3203b78 100644 --- a/memory-game/mg-frontend/src/components/scoreboard.jsx +++ b/memory-game/mg-frontend/src/components/scoreboard.jsx @@ -2,9 +2,13 @@ import "../styles/scoreboard.css"; export default function Scoreboard(props) { return ( -
-

SCORE: {props.score}

-

HIGH SCORE: {props.highscore}

-
+ <> + {props.gameStarted ? ( +
+

SCORE: {props.score}

+

HIGH SCORE: {props.highscore}

+
+ ) : null} + ); } diff --git a/memory-game/mg-frontend/src/styles/button.css b/memory-game/mg-frontend/src/styles/button.css new file mode 100644 index 0000000..3143038 --- /dev/null +++ b/memory-game/mg-frontend/src/styles/button.css @@ -0,0 +1,59 @@ +:root { + /* Grays */ + --lightest-gray: 208, 21%, 93%; + --lighter-gray: 210, 16%, 76%; + --light-gray: 208, 12%, 58%; + --dark-gray: 207, 12%, 43%; + --darker-gray: 209, 15%, 28%; + + --dark-blue: 246, 87%, 30%; + --blue: 219, 100%, 57%; + --light-blue: 219, 100%, 69%; + + --green: 158, 95%, 34%; + --green-hover: 158, 95%, 28%; +} + +.btn { + border-radius: 0.4rem; + padding: 1rem; + cursor: pointer; + text-decoration: none; + display: inline-block; + border: none; + font-size: 1.2rem; + flex: 1; +} + +.btn:hover { + /* background-color: hsl(var(--dark-gray)); */ +} + +.normal-btn { + background-color: hsl(var(--light-gray)); + transition: + background-color 500ms ease-in-out, + color 500ms ease-in-out; +} + +.normal-btn:hover { + background-color: hsl(var(--dark-gray)); + color: hsl(var(--lighter-gray)); +} + +.submit-btn { + background-color: hsl(var(--green)); + transition: background-color 600ms; +} + +.submit-btn:hover { + background-color: hsl(var(--green-hover)); +} + +.clear-btn { + background-color: hsl(var(--lighter-gray)); + transition: background-color 600ms; +} +.clear-btn:hover { + background-color: hsl(var(--light-gray)); +}