From 721ed4fa4f91a5fd46e48f06f4674f2aef30fe04 Mon Sep 17 00:00:00 2001
From: Mike Smith <89040888+smiggiddy@users.noreply.github.com>
Date: Wed, 17 Jul 2024 22:23:47 -0400
Subject: [PATCH] feat: basic api complete
basic grid layout
---
.gitignore | 164 ++++++++++++++++++
memory-game/mg-backend/__init__.py | 0
memory-game/mg-backend/ai.py | 61 +++++++
memory-game/mg-backend/main.py | 53 ++++++
memory-game/mg-backend/models.py | 0
memory-game/mg-backend/photos.py | 20 +++
memory-game/mg-backend/requirements.txt | 35 ++++
memory-game/mg-backend/test_data.py | 74 ++++++++
memory-game/mg-frontend/src/App.jsx | 7 +-
.../mg-frontend/src/components/card.jsx | 7 +-
.../mg-frontend/src/components/gameboard.jsx | 13 +-
.../mg-frontend/src/components/scoreboard.jsx | 10 ++
memory-game/mg-frontend/src/index.css | 1 +
memory-game/mg-frontend/src/styles/card.css | 3 +-
.../mg-frontend/src/styles/gameboard.css | 8 +-
.../mg-frontend/src/styles/scoreboard.css | 3 +
16 files changed, 444 insertions(+), 15 deletions(-)
create mode 100644 memory-game/mg-backend/__init__.py
create mode 100644 memory-game/mg-backend/ai.py
create mode 100644 memory-game/mg-backend/main.py
create mode 100644 memory-game/mg-backend/models.py
create mode 100644 memory-game/mg-backend/photos.py
create mode 100644 memory-game/mg-backend/requirements.txt
create mode 100644 memory-game/mg-backend/test_data.py
create mode 100644 memory-game/mg-frontend/src/components/scoreboard.jsx
create mode 100644 memory-game/mg-frontend/src/styles/scoreboard.css
diff --git a/.gitignore b/.gitignore
index 46e2e6a..491656d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -130,3 +130,167 @@ dist
.pnp.*
.DS_STORE
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+# in version control.
+# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
+.pdm.toml
+.pdm-python
+.pdm-build/
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
+
diff --git a/memory-game/mg-backend/__init__.py b/memory-game/mg-backend/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/memory-game/mg-backend/ai.py b/memory-game/mg-backend/ai.py
new file mode 100644
index 0000000..6911f39
--- /dev/null
+++ b/memory-game/mg-backend/ai.py
@@ -0,0 +1,61 @@
+from dotenv import load_dotenv
+import google.generativeai as genai
+import json
+import os
+from .photos import Pictures
+from pydantic import BaseModel, HttpUrl
+
+
+load_dotenv()
+GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
+
+
+class Topics(BaseModel):
+ topic: str
+
+
+class Results(BaseModel):
+ topic: str
+ medium_url: str
+ photo_id: int
+ photo_url: str
+ bad_match: bool
+
+
+class AI:
+ genai = genai.configure(api_key=GEMINI_API_KEY)
+
+ def generate_topics(self) -> list:
+ model = genai.GenerativeModel(
+ "gemini-1.5-flash",
+ generation_config={
+ "response_mime_type": "application/json",
+ "response_schema": list[Topics],
+ },
+ )
+
+ prompt = """
+ Return 12 items for a toddler aged 2 to 3 to choose from in a memory game.
+ """
+ response = model.generate_content(prompt).text
+
+ return json.loads(response)
+
+ 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
+ {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(
+ "gemini-1.5-flash",
+ generation_config={
+ "response_mime_type": "application/json",
+ "response_schema": Results,
+ },
+ )
+
+ r = model.generate_content(prompt).text
+
+ return json.loads(r)
diff --git a/memory-game/mg-backend/main.py b/memory-game/mg-backend/main.py
new file mode 100644
index 0000000..697b270
--- /dev/null
+++ b/memory-game/mg-backend/main.py
@@ -0,0 +1,53 @@
+from .ai import AI
+from .test_data import data
+from dotenv import load_dotenv
+from fastapi import FastAPI
+from fastapi.middleware.cors import CORSMiddleware
+import logging
+import os
+from .photos import Pictures
+
+logger = logging.getLogger("uvicorn.error")
+logger.setLevel(logging.DEBUG)
+load_dotenv()
+
+app = FastAPI()
+ai = AI()
+
+# IN HERE FOR LOCAL DEV
+origins = ["http://localhost:5173"]
+app.add_middleware(
+ CORSMiddleware,
+ allow_origins=origins,
+ allow_credentials=True,
+ allow_methods=["*"],
+ allow_headers=["*"],
+)
+
+
+PEXELS_API_KEY = os.getenv("PEXELS_API_KEY")
+photos = Pictures(PEXELS_API_KEY)
+
+
+@app.get("/")
+async def read_main():
+ return data
+ # data = []
+ #
+ # topics = ai.generate_topics()
+ #
+ # try:
+ # for item in topics:
+ #
+ # logger.info(item)
+ # picture_data = photos.search(item["topic"])
+ #
+ # card_json = ai.generate_card_json(picture_data, item)
+ # logger.info(card_json)
+ #
+ # data.append(card_json)
+ #
+ # return data
+ # 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
new file mode 100644
index 0000000..e69de29
diff --git a/memory-game/mg-backend/photos.py b/memory-game/mg-backend/photos.py
new file mode 100644
index 0000000..c17cc57
--- /dev/null
+++ b/memory-game/mg-backend/photos.py
@@ -0,0 +1,20 @@
+import requests
+
+
+class Pictures:
+ def __init__(self, token) -> None:
+ self.url = "https://api.pexels.com"
+
+ self.parameters = {"orientation": "square", "size": "small"}
+ self.headers = {"Authorization": f"{token}"}
+
+ def search(self, topic):
+
+ search_path = "/v1/search"
+ search_params = {**self.parameters, "query": topic}
+
+ r = requests.get(
+ url=self.url + search_path, params=search_params, headers=self.headers
+ )
+
+ return r.json()
diff --git a/memory-game/mg-backend/requirements.txt b/memory-game/mg-backend/requirements.txt
new file mode 100644
index 0000000..ef7e9da
--- /dev/null
+++ b/memory-game/mg-backend/requirements.txt
@@ -0,0 +1,35 @@
+annotated-types==0.7.0
+anyio==4.4.0
+certifi==2024.6.2
+click==8.1.7
+dnspython==2.6.1
+email_validator==2.2.0
+fastapi==0.111.0
+fastapi-cli==0.0.4
+h11==0.14.0
+httpcore==1.0.5
+httptools==0.6.1
+httpx==0.27.0
+idna==3.7
+Jinja2==3.1.4
+markdown-it-py==3.0.0
+MarkupSafe==2.1.5
+mdurl==0.1.2
+orjson==3.10.5
+pydantic==2.7.4
+pydantic_core==2.18.4
+Pygments==2.18.0
+python-dotenv==1.0.1
+python-multipart==0.0.9
+PyYAML==6.0.1
+rich==13.7.1
+shellingham==1.5.4
+sniffio==1.3.1
+starlette==0.37.2
+typer==0.12.3
+typing_extensions==4.12.2
+ujson==5.10.0
+uvicorn==0.30.1
+uvloop==0.19.0
+watchfiles==0.22.0
+websockets==12.0
diff --git a/memory-game/mg-backend/test_data.py b/memory-game/mg-backend/test_data.py
new file mode 100644
index 0000000..d517fbd
--- /dev/null
+++ b/memory-game/mg-backend/test_data.py
@@ -0,0 +1,74 @@
+data = [
+ {
+ "medium_url": "https://images.pexels.com/photos/19314176/pexels-photo-19314176.jpeg?auto=compress&cs=tinysrgb&h=350",
+ "photo_id": 19314176,
+ "photo_url": "https://www.pexels.com/photo/ball-in-golden-paper-19314176/",
+ "topic": "ball",
+ },
+ {
+ "medium_url": "https://images.pexels.com/photos/1592384/pexels-photo-1592384.jpeg?auto=compress&cs=tinysrgb&h=350",
+ "photo_id": 1592384,
+ "photo_url": "https://www.pexels.com/photo/shallow-focus-photography-of-blue-alpine-car-1592384/",
+ "topic": "car",
+ },
+ {
+ "medium_url": "https://images.pexels.com/photos/444492/pexels-photo-444492.jpeg?auto=compress&cs=tinysrgb&h=350",
+ "photo_id": 444492,
+ "photo_url": "https://www.pexels.com/photo/golden-retriever-puppy-mix-444492/",
+ "topic": "dog",
+ },
+ {
+ "medium_url": "https://images.pexels.com/photos/160722/cat-tiger-getiegert-feel-at-home-160722.jpeg?auto=compress&cs=tinysrgb&h=350",
+ "photo_id": 160722,
+ "photo_url": "https://www.pexels.com/photo/silver-tabby-cat-lying-on-green-grass-160722/",
+ "topic": "cat",
+ },
+ {
+ "medium_url": "https://images.pexels.com/photos/694587/pexels-photo-694587.jpeg?auto=compress&cs=tinysrgb&h=350",
+ "photo_id": 694587,
+ "photo_url": "https://www.pexels.com/photo/silhouette-of-boy-running-in-body-of-water-during-sunset-694587/",
+ "topic": "sun",
+ },
+ {
+ "medium_url": "https://images.pexels.com/photos/821718/pexels-photo-821718.jpeg?auto=compress&cs=tinysrgb&h=350",
+ "photo_id": 821718,
+ "photo_url": "https://www.pexels.com/photo/full-moon-821718/",
+ "topic": "moon",
+ },
+ {
+ "medium_url": "https://images.pexels.com/photos/102104/pexels-photo-102104.jpeg?auto=compress&cs=tinysrgb&h=350",
+ "photo_id": 102104,
+ "photo_url": "https://www.pexels.com/photo/red-and-orange-apple-fruit-102104/",
+ "topic": "apple",
+ },
+ {
+ "medium_url": "https://images.pexels.com/photos/2872767/pexels-photo-2872767.jpeg?auto=compress&cs=tinysrgb&h=350",
+ "photo_id": 2872767,
+ "photo_url": "https://www.pexels.com/photo/photo-of-peeled-banana-on-yellow-plate-and-background-2872767/",
+ "topic": "banana",
+ },
+ {
+ "medium_url": "https://images.pexels.com/photos/207732/pexels-photo-207732.jpeg?auto=compress&cs=tinysrgb&h=350",
+ "photo_id": 207732,
+ "photo_url": "https://www.pexels.com/photo/grayscale-photo-of-books-207732/",
+ "topic": "book",
+ },
+ {
+ "medium_url": "https://images.pexels.com/photos/1464625/pexels-photo-1464625.jpeg?auto=compress&cs=tinysrgb&h=350",
+ "photo_id": 1464625,
+ "photo_url": "https://www.pexels.com/photo/grey-sneakers-with-dense-surface-of-texture-for-comfortable-everyday-wearing-1464625/",
+ "topic": "shoe",
+ },
+ {
+ "medium_url": "https://images.pexels.com/photos/2098848/pexels-photo-2098848.jpeg?auto=compress&cs=tinysrgb&h=350",
+ "photo_id": 2098848,
+ "photo_url": "https://www.pexels.com/photo/brown-sun-hat-and-sandals-on-sand-2098848/",
+ "topic": "hat",
+ },
+ {
+ "medium_url": "https://images.pexels.com/photos/698275/pexels-photo-698275.jpeg?auto=compress&cs=tinysrgb&h=350",
+ "photo_id": 698275,
+ "photo_url": "https://www.pexels.com/photo/tree-surrounded-by-snow-698275/",
+ "topic": "tree",
+ },
+]
diff --git a/memory-game/mg-frontend/src/App.jsx b/memory-game/mg-frontend/src/App.jsx
index ebfd09a..cb82c39 100644
--- a/memory-game/mg-frontend/src/App.jsx
+++ b/memory-game/mg-frontend/src/App.jsx
@@ -1,11 +1,16 @@
import { useState } from "react";
import "./App.css";
import GameBoard from "./components/gameboard";
+import Scoreboard from "./components/scoreboard";
function App() {
+ const [score, setScore] = useState(0);
+ const [highScore, setHighScore] = useState(0);
+
return (
<>
-
SCORE: {props.score}
+HIGH SCORE: {props.highscore}
+