diff --git a/learn_sql_model/config.py b/learn_sql_model/config.py index 500642b..c764122 100644 --- a/learn_sql_model/config.py +++ b/learn_sql_model/config.py @@ -81,7 +81,8 @@ def get_config(overrides: dict = {}) -> Config: return config -def get_session(config: Config = None) -> "Session": +def get_session() -> "Session": + config = get_config() with Session(config.database.engine) as session: yield session diff --git a/learn_sql_model/game/game.py b/learn_sql_model/game/game.py new file mode 100644 index 0000000..bcb63c9 --- /dev/null +++ b/learn_sql_model/game/game.py @@ -0,0 +1,165 @@ +# using pygame make a game using Hero +# it should be gamepad and mouse compatible +# it should have a server that keeps track of the game logic +# it should have a renderer that renders the game +# it should have a client that sends commands to the server +# + + +import atexit + +import pygame +from rich.console import Console +import typer +from typer import Typer + +from learn_sql_model.models.hero import ( + Hero, + HeroCreate, + HeroDelete, + HeroRead, + HeroUpdate, +) + +speed = 10 + +pygame.font.init() # you have to call this at the start, +# if you want to use this module. +my_font = pygame.font.SysFont("Comic Sans MS", 30) + + +class Client: + def __init__(self, name, secret_name): + self.hero = Hero(name=name, secret_name=secret_name, x=400, y=300, size=50) + self.hero = HeroCreate(**self.hero.dict()).post() + + self.screen = pygame.display.set_mode((800, 600)) + self.clock = pygame.time.Clock() + self.running = True + self.screen.fill((0, 0, 0)) + + self.moving_up = False + self.moving_down = False + self.moving_left = False + self.moving_right = False + + self.others = HeroRead.list() + + atexit.register(self.quit) + + def run(self): + while self.running: + self.handle_events() + self.update() + self.render() + self.clock.tick(60) + self.quit() + + def quit(self): + HeroDelete(id=self.hero.id).delete() + + def update(self): + if self.moving_up: + self.hero.y -= speed + if self.moving_down: + self.hero.y += speed + if self.moving_left: + self.hero.x -= speed + if self.moving_right: + self.hero.x += speed + + HeroUpdate( + **{k: v for k, v in self.hero.dict().items() if v is not None} + ).update() + + self.others = HeroRead.list() + + def render(self): + Console().print(self.hero) + self.screen.fill((0, 0, 0)) + + for other in self.others: + pygame.draw.circle(self.screen, (255, 0, 0), (other.x, other.y), other.size) + self.screen.blit( + my_font.render(other.name, False, (255, 255, 255), 1), + (other.x, other.y), + ) + + pygame.draw.circle( + self.screen, (0, 0, 255), (self.hero.x, self.hero.y), self.hero.size + ) + self.screen.blit( + my_font.render(self.hero.name, False, (255, 255, 255)), + (self.hero.x, self.hero.y), + ) + + # update the screen + pygame.display.flip() + + def handle_events(self): + self.events = pygame.event.get() + for event in self.events: + if event.type == pygame.QUIT: + self.running = False + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_ESCAPE: + self.running = False + if event.key == pygame.K_LEFT: + self.moving_left = True + if event.key == pygame.K_RIGHT: + self.moving_right = True + if event.key == pygame.K_UP: + self.moving_up = True + if event.key == pygame.K_DOWN: + self.moving_down = True + # wasd + if event.key == pygame.K_w: + self.moving_up = True + if event.key == pygame.K_s: + self.moving_down = True + if event.key == pygame.K_a: + self.moving_left = True + if event.key == pygame.K_d: + self.moving_right = True + # controller left joystick + + if event.type == pygame.KEYUP: + if event.key == pygame.K_LEFT: + self.moving_left = False + if event.key == pygame.K_RIGHT: + self.moving_right = False + if event.key == pygame.K_UP: + self.moving_up = False + if event.key == pygame.K_DOWN: + self.moving_down = False + # wasd + if event.key == pygame.K_w: + self.moving_up = False + if event.key == pygame.K_s: + self.moving_down = False + if event.key == pygame.K_a: + self.moving_left = False + if event.key == pygame.K_d: + self.moving_right = False + + def check_events(self): + pass + + def check_collisions(self): + pass + + +app = Typer() + + +@app.command() +def run( + name: str = typer.Option(...), + secret_name: str = typer.Option(...), +): + client = Client(name, secret_name) + client.run() + + +if __name__ == "__main__": + app() diff --git a/learn_sql_model/models/hero.py b/learn_sql_model/models/hero.py index 7bbef32..d4b1991 100644 --- a/learn_sql_model/models/hero.py +++ b/learn_sql_model/models/hero.py @@ -12,6 +12,9 @@ from learn_sql_model.models.pet import Pet class HeroBase(SQLModel, table=False): name: str secret_name: str + x: int + y: int + size: int age: Optional[int] = None shoe_size: Optional[int] = None @@ -34,6 +37,8 @@ class HeroCreate(HeroBase): if r.status_code != 200: raise RuntimeError(f"{r.status_code}:\n {r.text}") + return Hero.parse_obj(r.json()) + class HeroRead(HeroBase): id: int @@ -80,6 +85,8 @@ class HeroUpdate(SQLModel): secret_name: Optional[str] = None age: Optional[int] = None shoe_size: Optional[int] = None + x: int + y: int pet_id: Optional[int] = Field(default=None, foreign_key="pet.id") pet: Optional[Pet] = Relationship(back_populates="hero") diff --git a/tests/test_hero.py b/tests/test_hero.py index 139a3af..831a563 100644 --- a/tests/test_hero.py +++ b/tests/test_hero.py @@ -9,7 +9,7 @@ from learn_sql_model.api.app import app from learn_sql_model.cli.hero import hero_app from learn_sql_model.config import get_config, get_session from learn_sql_model.factories.hero import HeroFactory -from learn_sql_model.models.hero import Hero +from learn_sql_model.models.hero import Hero, HeroCreate, HeroRead runner = CliRunner() client = TestClient(app) @@ -76,15 +76,6 @@ def test_api_read_hero(session: Session, client: TestClient): session.add(hero_1) session.commit() - response = client.get(f"/hero/999") - assert response.status_code == 404 - - -def test_api_read_hero_404(session: Session, client: TestClient): - hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") - session.add(hero_1) - session.commit() - response = client.get(f"/hero/{hero_1.id}") data = response.json() @@ -95,6 +86,15 @@ def test_api_read_hero_404(session: Session, client: TestClient): assert data["id"] == hero_1.id +def test_api_read_hero_404(session: Session, client: TestClient): + hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") + session.add(hero_1) + session.commit() + + response = client.get(f"/hero/999") + assert response.status_code == 404 + + def test_api_update_hero(session: Session, client: TestClient): hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") session.add(hero_1) @@ -233,3 +233,46 @@ def test_cli_list(mocker): assert f"secret_name='{hero_1.secret_name}'" in result.stdout assert f"name='{hero_2.name}'" in result.stdout assert f"secret_name='{hero_2.secret_name}'" in result.stdout + + +def test_model_post(mocker): + patch_httpx_post = mocker.patch( + "httpx.post", return_value=mocker.Mock(status_code=200) + ) + hero = HeroFactory().build(name="Steelman", age=25) + hero_create = HeroCreate(**hero.dict()) + hero_create.post() + assert patch_httpx_post.call_count == 1 + + +def test_model_post_500(mocker): + patch_httpx_post = mocker.patch( + "httpx.post", return_value=mocker.Mock(status_code=500) + ) + hero = HeroFactory().build(name="Steelman", age=25) + hero_create = HeroCreate(**hero.dict()) + with pytest.raises(RuntimeError): + hero_create.post() + assert patch_httpx_post.call_count == 1 + + +def test_model_read_hero(mocker, session: Session, client: TestClient): + mocker.patch( + "learn_sql_model.config.Database.engine", + new_callable=lambda: create_engine( + "sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool + ), + ) + + config = get_config() + SQLModel.metadata.create_all(config.database.engine) + + hero = Hero(name="Deadpond", secret_name="Dive Wilson") + session = config.database.session + session.add(hero) + session.commit() + session.refresh(hero) + + hero_read = HeroRead.get(id=hero.id) + assert hero_read.name == "Deadpond" + assert hero_read.secret_name == "Dive Wilson"