This commit is contained in:
Waylon Walker 2023-06-06 09:57:21 -05:00
parent 6b1c60a550
commit 839cbd0dc0
No known key found for this signature in database
GPG key ID: 66E2BF2B4190EFE4
17 changed files with 213 additions and 35 deletions

View file

@ -7,7 +7,13 @@ from learn_sql_model.console import console
# models # models
from learn_sql_model.models.fast_model import FastModel from learn_sql_model.models.fast_model import FastModel
from learn_sql_model.models.hero import Hero from learn_sql_model.models.hero import Hero
from learn_sql_model.models.hero import HeroCreate
from learn_sql_model.models.hero import HeroRead
from learn_sql_model.models.hero import HeroUpdate
from learn_sql_model.models.hero import HeroDelete
from learn_sql_model.factories.hero import HeroFactory from learn_sql_model.factories.hero import HeroFactory
from learn_sql_model.factories.pet import PetFactory from learn_sql_model.factories.pet import PetFactory
from learn_sql_model.models.pet import Pet from learn_sql_model.models.pet import Pet

View file

@ -1,8 +1,5 @@
from python:3.10 from python:3.10
# install python and psycopg2
# RUN apt-get update && apt-get install -y python3 python3-pip
# RUN pip3 install hatch
WORKDIR /app WORKDIR /app
Copy pyproject.toml /app Copy pyproject.toml /app
COPY learn_sql_model/__about__.py /app/learn_sql_model/__about__.py COPY learn_sql_model/__about__.py /app/learn_sql_model/__about__.py

View file

@ -1,4 +1,4 @@
FROM learn-sql-model FROM python:3.10
ENV DEBIAIN_FRONTEND=noninteractive ENV DEBIAIN_FRONTEND=noninteractive
ENV PATH="$PATH:/root/.local/bin:/root/.cargo/bin" ENV PATH="$PATH:/root/.local/bin:/root/.cargo/bin"
@ -43,11 +43,20 @@ RUN python3 -m pip install --upgrade pip && \
WORKDIR /app WORKDIR /app
## DUPLICATE from Dockerfile
## building FROM learn-sql-model will cause the cache to bust for every
## change, it needs to come after the dev installs.
COPY pyproject.toml /app
COPY learn_sql_model/__about__.py /app/learn_sql_model/__about__.py
COPY README.md /app
RUN pip3 install .
COPY . . COPY . .
RUN python3 -m hatch env create && \ RUN python3 -m hatch env create && \
python3 -m hatch shell python3 -m hatch shell
RUN stow bin -t /root/ RUN stow bin -t /root/
COPY .env.dev.docker /app/.env.dev
ENTRYPOINT /tmp/zellij/bootstrap/zellij ENTRYPOINT /tmp/zellij/bootstrap/zellij

View file

@ -9,6 +9,19 @@ pip install hatch
hatch shell hatch shell
``` ```
## Development with Docker
```console
docker compose build
docker compose up -d
```
To attach to the cli.
```console
docker attach learn-sql-model-cli-1
```
## Start the Server ## Start the Server
```console ```console

View file

@ -61,7 +61,7 @@ version_path_separator = os # Use os.pathsep. Default configuration used for ne
# are written from script.py.mako # are written from script.py.mako
# output_encoding = utf-8 # output_encoding = utf-8
sqlalchemy.url = sqlite:///database.db sqlalchemy.url = postgresql+psycopg2://postgres:postgres@localhost:5432
[post_write_hooks] [post_write_hooks]

View file

@ -35,6 +35,8 @@ services:
restart: always restart: always
tty: true tty: true
stdin_open: true stdin_open: true
volumes:
- ./:/app
volumes: volumes:
db: db:
driver: local driver: local

48
dockerfile-dev Normal file
View file

@ -0,0 +1,48 @@
FROM learn-sql-model
ENV DEBIAIN_FRONTEND=noninteractive
ENV PATH="$PATH:/root/.local/bin:/root/.cargo/bin"
ENV SHELL=zsh
ENV USER=root
RUN apt update && \
apt upgrade -y && \
apt install -y \
cmake \
htop \
stow \
zsh
WORKDIR /root/downloads
RUN wget https://github.com/neovim/neovim/releases/download/nightly/nvim.appimage && \
mkdir -p /root/.local/bin && \
chmod u+x /root/downloads/nvim.appimage && \
/root/downloads/nvim.appimage --appimage-extract && \
rm -rf nvim.appimage && \
ln -s ~/downloads/squashfs-root/usr/bin/nvim ~/.local/bin/nvim && \
cd ~ && \
git clone https://github.com/LazyVim/starter ~/.config/nvim && \
nvim --headless -c 'quitall'
RUN curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly -y && \
cargo install \
bat \
exa \
gitui \
ripgrep \
starship \
zellij
RUN python3 -m pip install --upgrade pip && \
python3 -m pip install ansible-core && \
python3 -m pip install lolcat && \
python3 -m pip install pyflyby && \
python3 -m pip install rich-cli && \
python3 -m pip install visidata
WORKDIR /app
ENTRYPOINT zellij

View file

@ -11,8 +11,8 @@ hero_router = APIRouter()
@hero_router.on_event("startup") @hero_router.on_event("startup")
def on_startup(config: Config = Depends(get_config)) -> None: def on_startup() -> None:
SQLModel.metadata.create_all(config.database.engine) SQLModel.metadata.create_all(get_config().database.engine)
@hero_router.get("/items/") @hero_router.get("/items/")
@ -26,6 +26,12 @@ def get_hero(id: int, config: Config = Depends(get_config)) -> Hero:
return Hero().get(id=id, config=config) return Hero().get(id=id, config=config)
@hero_router.get("/h/{id}")
def get_h(id: int, config: Config = Depends(get_config)) -> Hero:
"get one hero"
return Hero().get(id=id, config=config)
@hero_router.post("/hero/") @hero_router.post("/hero/")
def post_hero(hero: Hero, config: Config = Depends(get_config)) -> Hero: def post_hero(hero: Hero, config: Config = Depends(get_config)) -> Hero:
"read all the heros" "read all the heros"

View file

@ -1,3 +1,4 @@
from rich.console import Console
import typer import typer
import uvicorn import uvicorn
@ -27,3 +28,38 @@ def run(
), ),
): ):
uvicorn.run(**get_config().api_server.dict()) uvicorn.run(**get_config().api_server.dict())
@api_app.command()
def status(
verbose: bool = typer.Option(
False,
callback=verbose_callback,
help="show the log messages",
),
):
import httpx
config = get_config()
host = config.api_server.host
port = config.api_server.port
url = f"http://{host}:{port}/docs"
try:
r = httpx.get(url)
if r.status_code == 200:
Console().print(f"[green]API: ([gold1]{url}[green]) is running")
else:
Console().print(f"[red]API: ([gold1]{url}[red]) is not running")
except httpx.ConnectError:
Console().print(f"[red]API: ([gold1]{url}[red]) is not running")
try:
with config.database.engine.connect():
Console().print(
f"[green]database: ([gold1]{config.database.engine}[green]) is running"
)
except Exception as e:
Console().print(
f"[red]database: ([gold1]{config.database.engine}[red]) is not running"
)

View file

@ -5,16 +5,14 @@ from typer.main import get_group
from learn_sql_model.cli.api import api_app from learn_sql_model.cli.api import api_app
from learn_sql_model.cli.config import config_app from learn_sql_model.cli.config import config_app
from learn_sql_model.cli.hero import hero_app from learn_sql_model.cli.hero import hero_app
from learn_sql_model.cli.model import model_app
from learn_sql_model.cli.tui import tui_app
app = typer.Typer( app = typer.Typer(
name="learn_sql_model", name="learn_sql_model",
help="learn-sql-model cli for managing the project", help="learn-sql-model cli for managing the project",
) )
app.add_typer(config_app, name="config") app.add_typer(config_app, name="config")
app.add_typer(tui_app, name="tui") # app.add_typer(tui_app, name="tui")
app.add_typer(model_app, name="model") # app.add_typer(model_app, name="model")
app.add_typer(api_app, name="api") app.add_typer(api_app, name="api")
app.add_typer(hero_app, name="hero") app.add_typer(hero_app, name="hero")

View file

@ -1,15 +1,14 @@
import sys
from typing import List, Optional, Union from typing import List, Optional, Union
from pydantic_typer import expand_pydantic_args from engorgio import engorgio
from rich.console import Console from rich.console import Console
import typer import typer
from learn_sql_model.config import Config from learn_sql_model.config import Config, get_config
from learn_sql_model.factories.hero import HeroFactory from learn_sql_model.factories.hero import HeroFactory
from learn_sql_model.factories.pet import PetFactory from learn_sql_model.factories.pet import PetFactory
from learn_sql_model.models.hero import Hero from learn_sql_model.models.hero import Hero, HeroCreate
from learn_sql_model.models.pet import Pet
import sys
hero_app = typer.Typer() hero_app = typer.Typer()
@ -20,9 +19,9 @@ def hero():
@hero_app.command() @hero_app.command()
@expand_pydantic_args(typer=True) @engorgio(typer=True)
def get( def get(
id: Optional[int] = None, id: Optional[int] = typer.Argument(default=None),
config: Config = None, config: Config = None,
) -> Union[Hero, List[Hero]]: ) -> Union[Hero, List[Hero]]:
"get one hero" "get one hero"
@ -33,28 +32,36 @@ def get(
@hero_app.command() @hero_app.command()
@expand_pydantic_args(typer=True) @engorgio(typer=True)
def list(
config: Config = None,
) -> Union[Hero, List[Hero]]:
"get one hero"
hero = Hero().get()
Console().print(hero)
return hero
@hero_app.command()
@engorgio(typer=True)
def create( def create(
hero: Hero, hero: HeroCreate,
pet: Pet = None,
config: Config = None, config: Config = None,
) -> Hero: ) -> Hero:
"read all the heros" "read all the heros"
config.init() config.init()
hero.pet = pet
hero = hero.post(config=config) hero = hero.post(config=config)
Console().print(hero) Console().print(hero)
@hero_app.command() @hero_app.command()
@expand_pydantic_args(typer=True) @engorgio(typer=True)
def populate( def populate(
hero: Hero,
n: int = 10, n: int = 10,
config: Config = None,
) -> Hero: ) -> Hero:
"read all the heros" "read all the heros"
if config is None: config = get_config()
config = Config()
if config.env == "prod": if config.env == "prod":
Console().print("populate is not supported in production") Console().print("populate is not supported in production")
sys.exit(1) sys.exit(1)

View file

@ -6,8 +6,7 @@ from learn_sql_model.models.fast_model import FastModel
from learn_sql_model.models.pet import Pet from learn_sql_model.models.pet import Pet
class Hero(FastModel, table=True): class HeroBase(FastModel, table=False):
id: Optional[int] = Field(default=None, primary_key=True)
name: str name: str
secret_name: str secret_name: str
age: Optional[int] = None age: Optional[int] = None
@ -15,3 +14,23 @@ class Hero(FastModel, table=True):
pet_id: Optional[int] = Field(default=None, foreign_key="pet.id") pet_id: Optional[int] = Field(default=None, foreign_key="pet.id")
pet: Optional[Pet] = Relationship(back_populates="hero") pet: Optional[Pet] = Relationship(back_populates="hero")
class Hero(HeroBase, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
class HeroCreate(HeroBase):
...
class HeroRead(HeroBase):
id: Optional[int] = Field(default=None, primary_key=True)
class HeroUpdate(HeroBase):
...
class HeroDelete(HeroBase):
id: Optional[int] = Field(default=None, primary_key=True)

View file

@ -1,3 +1,4 @@
from datetime import datetime
from typing import Optional, TYPE_CHECKING from typing import Optional, TYPE_CHECKING
from sqlmodel import Field, Relationship from sqlmodel import Field, Relationship
@ -11,4 +12,5 @@ if TYPE_CHECKING:
class Pet(FastModel, table=True): class Pet(FastModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True) id: Optional[int] = Field(default=None, primary_key=True)
name: str = "Jim" name: str = "Jim"
birthday: Optional[datetime] = None
hero: "Hero" = Relationship(back_populates="pet") hero: "Hero" = Relationship(back_populates="pet")

View file

@ -4,6 +4,7 @@ from alembic import context
from sqlalchemy import engine_from_config, pool from sqlalchemy import engine_from_config, pool
from sqlmodel import SQLModel from sqlmodel import SQLModel
from learn_sql_model.config import get_config
from learn_sql_model.models.hero import Hero from learn_sql_model.models.hero import Hero
from learn_sql_model.models.pet import Pet from learn_sql_model.models.pet import Pet
@ -48,7 +49,7 @@ def run_migrations_offline() -> None:
script output. script output.
""" """
url = config.get_main_option("sqlalchemy.url") url = get_config().database_url
context.configure( context.configure(
url=url, url=url,
target_metadata=target_metadata, target_metadata=target_metadata,
@ -78,7 +79,7 @@ def run_migrations_online() -> None:
context.configure( context.configure(
connection=connection, connection=connection,
target_metadata=target_metadata, target_metadata=target_metadata,
render_as_batch=True, render_as_batch=False,
version_table=f'{config.get_main_option("project")}_alembic_version', version_table=f'{config.get_main_option("project")}_alembic_version',
) )

View file

@ -0,0 +1,29 @@
"""add birthday
Revision ID: a9bb6625c57b
Revises: c8516c888495
Create Date: 2023-05-25 19:00:58.137464
"""
from alembic import op
import sqlalchemy as sa
import sqlmodel
# revision identifiers, used by Alembic.
revision = 'a9bb6625c57b'
down_revision = 'c8516c888495'
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('pet', sa.Column('birthday', sa.DateTime(), nullable=True))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('pet', 'birthday')
# ### end Alembic commands ###

View file

@ -1,8 +1,8 @@
"""init """init
Revision ID: 2a221363b5f8 Revision ID: c8516c888495
Revises: Revises:
Create Date: 2023-05-21 20:52:12.693194 Create Date: 2023-05-25 18:42:37.057225
""" """
from alembic import op from alembic import op
@ -11,7 +11,7 @@ import sqlmodel
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = '2a221363b5f8' revision = 'c8516c888495'
down_revision = None down_revision = None
branch_labels = None branch_labels = None
depends_on = None depends_on = None

View file

@ -25,16 +25,19 @@ classifiers = [
] ]
dependencies = [ dependencies = [
"anyconfig", "anyconfig",
"trogon", "engorgio",
"fastapi", "fastapi",
"httpx", "httpx",
"passlib[bcrypt]", "passlib[bcrypt]",
"polyfactory", "polyfactory",
"psycopg2",
"python-jose[cryptography]", "python-jose[cryptography]",
"python-multipart", "python-multipart",
"rich", "rich",
"sqlmodel", "sqlmodel",
"textual", "textual",
"toml",
"trogon",
"typer", "typer",
"uvicorn[standard]", "uvicorn[standard]",
] ]
@ -49,6 +52,7 @@ Changelog = "https://github.com/waylonwalker/learn-sql-model"
[project.scripts] [project.scripts]
learn-sql-model = "learn_sql_model.cli.app:app" learn-sql-model = "learn_sql_model.cli.app:app"
lsm = "learn_sql_model.cli.app:app"
[tool.hatch.version] [tool.hatch.version]
path = "learn_sql_model/__about__.py" path = "learn_sql_model/__about__.py"
@ -82,6 +86,7 @@ lint-test = [
"cov", "cov",
] ]
test-lint = "lint-test" test-lint = "lint-test"
api = "learn-sql-model api run"
[[tool.hatch.envs.test.matrix]] [[tool.hatch.envs.test.matrix]]
python = ["37", "38", "39", "310", "311"] python = ["37", "38", "39", "310", "311"]