diff --git a/.gitignore b/.gitignore index 84b57dc..fe5de12 100644 --- a/.gitignore +++ b/.gitignore @@ -95,4 +95,8 @@ logs/ # Temporary files *.tmp -*.temp \ No newline at end of file +*.temp + +# UV package manager +uv.lock +.uv/ diff --git a/api-server/.devcontainer/Dockerfile b/api-server/.devcontainer/Dockerfile new file mode 100644 index 0000000..6f79798 --- /dev/null +++ b/api-server/.devcontainer/Dockerfile @@ -0,0 +1,36 @@ +FROM python:3.13-alpine + +# Install system dependencies +RUN apk add --no-cache \ + git \ + curl \ + wget \ + bash \ + build-base \ + linux-headers \ + fish + +# Install uv from official image +COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv +RUN uv --version + +# Create non-root user with fish shell +RUN adduser -D -s /usr/bin/fish vscode + +# Create workspace and set permissions +RUN mkdir -p /workspace && chown vscode:vscode /workspace + +USER vscode +WORKDIR /workspace + +# Set fish as default shell +SHELL ["/usr/bin/fish", "-c"] + +# Install packages directly to system Python (container-isolated) +ENV UV_PROJECT_ENVIRONMENT="" +ENV UV_PYTHON=python + +# Verify everything works +RUN echo "Python: $(python --version)" && \ + echo "UV: $(uv --version)" && \ + echo "Fish: $(fish --version)" diff --git a/api-server/.devcontainer/devcontainer.json b/api-server/.devcontainer/devcontainer.json new file mode 100644 index 0000000..e0fd553 --- /dev/null +++ b/api-server/.devcontainer/devcontainer.json @@ -0,0 +1,44 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/python +{ + "name": "API-Server", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "build": { + "dockerfile": "Dockerfile" + }, + // "image": "mcr.microsoft.com/devcontainers/python:1-3.12", + // "image": "python:latest", + //"image": "python:3.13-slim", + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + "settings": {}, + "extensions": [ + "ms-python.python", + "frhtylcn.pythonsnippets", + "kevinrose.vsc-python-indent", + "wayou.vscode-todo-highlight", + "charliermarsh.ruff", + "tamasfe.even-better-toml" + ] + } + }, + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [9000], + // Use 'portsAttributes' to set default properties for specific forwarded ports. + // More info: https://containers.dev/implementors/json_reference/#port-attributes + "portsAttributes": { + "9000": { + "label": "API-Server Application", + "onAutoForward": "notify" + } + }, + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": "uv sync" + // "postCreateCommand": "apt-get update && apt-get install -y git && pip3 install -r requirements.txt" + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} \ No newline at end of file diff --git a/api-server/app/main.py b/api-server/app/main.py new file mode 100644 index 0000000..e4d22bd --- /dev/null +++ b/api-server/app/main.py @@ -0,0 +1,75 @@ +from fastapi import FastAPI, HTTPException +from fastapi.middleware.cors import CORSMiddleware +from pydantic import BaseModel +from typing import List, Optional + +app = FastAPI(title="FastAPI Server", version="1.0.0") + +# CORS middleware to allow client requests +app.add_middleware( + CORSMiddleware, + allow_origins=["http://localhost:3000", "http://client:3000"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +class Item(BaseModel): + id: int + name: str + description: Optional[str] = None + price: float + +# In-memory database +items_db = [ + Item(id=1, name="Laptop", description="High-performance laptop", price=999.99), + Item(id=2, name="Mouse", description="Wireless mouse", price=29.99), + Item(id=3, name="Keyboard", description="Mechanical keyboard", price=79.99), +] + +@app.get("/") +async def root(): + return {"message": "FastAPI Server is running!"} + +@app.get("/items", response_model=List[Item]) +async def get_items(): + return items_db + +@app.get("/items/{item_id}", response_model=Item) +async def get_item(item_id: int): + item = next((item for item in items_db if item.id == item_id), None) + if item is None: + raise HTTPException(status_code=404, detail="Item not found") + return item + +@app.post("/items", response_model=Item) +async def create_item(item: Item): + if any(existing_item.id == item.id for existing_item in items_db): + raise HTTPException(status_code=400, detail="Item ID already exists") + items_db.append(item) + return item + +@app.put("/items/{item_id}", response_model=Item) +async def update_item(item_id: int, item: Item): + if item.id != item_id: + raise HTTPException(status_code=400, detail="Item ID mismatch") + + for idx, existing_item in enumerate(items_db): + if existing_item.id == item_id: + items_db[idx] = item + return item + + raise HTTPException(status_code=404, detail="Item not found") + +@app.delete("/items/{item_id}") +async def delete_item(item_id: int): + for idx, item in enumerate(items_db): + if item.id == item_id: + deleted_item = items_db.pop(idx) + return {"message": f"Item {deleted_item.name} deleted successfully"} + + raise HTTPException(status_code=404, detail="Item not found") + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) \ No newline at end of file diff --git a/api-server/pyproject.toml b/api-server/pyproject.toml new file mode 100644 index 0000000..751bc97 --- /dev/null +++ b/api-server/pyproject.toml @@ -0,0 +1,12 @@ +[project] +name = "api-server" +version = "0.1.0" +requires-python = ">=3.11" +dependencies = [ + "fastapi>=0.104.0", + "uvicorn[standard]>=0.24.0", +] + +# [build-system] +# requires = ["hatchling"] +# build-backend = "hatchling.build" diff --git a/pico-client/.devcontainer/Dockerfile b/pico-client/.devcontainer/Dockerfile index ccb9000..7022972 100644 --- a/pico-client/.devcontainer/Dockerfile +++ b/pico-client/.devcontainer/Dockerfile @@ -1,13 +1,3 @@ -#Moin from VSCode - -# FROM python:3.13-slim -# RUN apt-get update && apt-get install -y \ -# git \ -# curl \ -# wget \ -# && rm -rf /var/lib/apt/lists/* - - FROM python:3.13-alpine RUN apk add --no-cache \ git \ diff --git a/pico-client/.devcontainer/devcontainer.json b/pico-client/.devcontainer/devcontainer.json index ae396de..b6d3eae 100644 --- a/pico-client/.devcontainer/devcontainer.json +++ b/pico-client/.devcontainer/devcontainer.json @@ -1,7 +1,7 @@ // For format details, see https://aka.ms/devcontainer.json. For config options, see the // README at: https://github.com/devcontainers/templates/tree/main/src/python { - "name": "Python 3", + "name": "Pico-Client", // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile "build": { "dockerfile": "Dockerfile" @@ -23,7 +23,8 @@ "wayou.vscode-todo-highlight", "charliermarsh.ruff", "wokwi.wokwi-vscode", - "humao.rest-client" + "humao.rest-client", + "tamasfe.even-better-toml" ] } }, @@ -33,7 +34,7 @@ // More info: https://containers.dev/implementors/json_reference/#port-attributes "portsAttributes": { "9000": { - "label": "Flask Application", + "label": "Pico-W Application", "onAutoForward": "notify" } }, diff --git a/pico-client/main.py b/pico-client/main.py index 4bf4d0c..42b49d3 100644 --- a/pico-client/main.py +++ b/pico-client/main.py @@ -1,6 +1,6 @@ from display import NeoPixel_64x64 from display.fonts import font_5x7 -from tryout import Font_Checker, Weather_Checker, Emoji_Checker +from tryout import Font_Checker, Weather_Checker, Emoji_Checker, API_Server_Checker from utils import show_system_load from utils import ( sync_ntp_time, @@ -12,12 +12,12 @@ from utils.digital_clock import DigitalClock import uasyncio as asyncio # type: ignore from web import Wlan -CITY_LIST: list[str] = sorted( - ["Großhansdorf", "Columbus", "London", "Ebeltoft", "Tokio"] -) - async def weather_check_task(weather_checker: Weather_Checker): + CITY_LIST: list[str] = sorted( + ["Großhansdorf", "Columbus", "London", "Ebeltoft", "Tokio"] + ) + while True: for city in CITY_LIST: weather_checker.check(city=city, lang="de", test_mode=True) @@ -63,7 +63,9 @@ if __name__ == "__main__": # emoji_checker : Emoji_Checker = Emoji_Checker(display) # emoji_checker.check() - # tryout.weather_check(display, test_mode=False) + api_server_check_task: API_Server_Checker = API_Server_Checker() + api_server_check_task.check() + display.set_font(font_5x7) weather_checker: Weather_Checker = Weather_Checker(display=display) diff --git a/pico-client/restapi/server-api.http b/pico-client/restapi/server-api.http new file mode 100644 index 0000000..6befb3d --- /dev/null +++ b/pico-client/restapi/server-api.http @@ -0,0 +1,7 @@ +### all Items +### FIXME: wie erreiche ich den Server aus dem Container +GET http://api-server-admin-:8000/items +Accept: application/json + +### +GET http://0.0.0.0:8000/items \ No newline at end of file diff --git a/pico-client/tryout/__init__.py b/pico-client/tryout/__init__.py index 3c8b71d..939edee 100644 --- a/pico-client/tryout/__init__.py +++ b/pico-client/tryout/__init__.py @@ -1,5 +1,6 @@ from .font_checker import Font_Checker from .weather_checker import Weather_Checker from .emoji_checker import Emoji_Checker +from .api_server_checker import API_Server_Checker -__all__ = [ 'Font_Checker', 'Weather_Checker', 'Emoji_Checker'] +__all__ = ["Font_Checker", "Weather_Checker", "Emoji_Checker", "API_Server_Checker"] diff --git a/pico-client/tryout/api_server_checker.py b/pico-client/tryout/api_server_checker.py new file mode 100644 index 0000000..e1ec652 --- /dev/null +++ b/pico-client/tryout/api_server_checker.py @@ -0,0 +1,20 @@ +import urequests # type: ignore +import json + +BASE_API_URL: str = "http://0.0.0.0:8000" + + +class API_Server_Checker: + def __init__(self): + pass + + def _get_items(self): + items_url: str = f"{BASE_API_URL}/items" + print(f"query: {items_url}") + r = urequests.get(items_url) + print("Status-Code:", r.status_code) + json_resp = r.json() + print("json_resp:", json_resp) + + def check(self): + self._get_items()