Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pythonweek-1.0 #22

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Tests BeerLog
on:
push:
branches:
- "*"
pull_request:
branches:
- "*"
workflow_dispatch:

jobs:
test:
strategy:
fail-fast: true
matrix:
python-version: ['3.8', '3.10']
os: [ubuntu-latest]
runs-on: ${{matrix.os}}

steps:
- uses: actions/setup-python@v2
- uses: actions/checkout@v2
with:
python-version: ${{matrix.python-version}}
- name: Install Poetry
run: pip install --upgrade pip && pip install poetry

- name: Install Project
run: poetry install

- name: Look For Style Errors
run: poetry run flake8 beerlog

- name: Look for auto format erros
run: poetry run black -l 79 --check --diff beerlog tests

- name: Run tests
run: poety run pytest -v --junitxml=test-result.xml

- name: Publish Junit results
uses: EnricoMi/publish-unit-test-result-action@v1
if: always()
with:
files: test-result.xml
check-name: Test Result (Python ${{matrix.python-version}})
Binary file added beerlog.db
Binary file not shown.
25 changes: 25 additions & 0 deletions beerlog/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from typing import List
from fastapi import FastAPI
from beerlog.core import get_beers_from_database
from beerlog.serializers import BeerOut, BeerIn
from beerlog.database import get_session
from beerlog.models import Beer


api = FastAPI(title="Beerlog")


@api.get("/beers/", response_model=List[BeerOut])
def list_beers():
beers = get_beers_from_database()
return beers


@api.post("/beers/", response_model=BeerOut)
def add_beer(beer_in: BeerIn):
beer = Beer(**beer_in.dict())
with get_session() as session:
session.add(beer)
session.commit()
session.refresh(beer)
return beer
48 changes: 45 additions & 3 deletions beerlog/cli.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,47 @@
from .config import settings
import typer
from typing import Optional
from beerlog.core import add_beer_to_database, get_beers_from_database
from rich.table import Table
from rich.console import Console


def main():
print("Hello from", settings.NAME)
main = typer.Typer(help="Beer Management Application")

console = Console()


@main.command("add")
def add(
name: str,
style: str,
flavor: int = typer.Option(...),
image: int = typer.Option(...),
cost: int = typer.Option(...),
):
"""Adds a new beer to database"""
if add_beer_to_database(name, style, flavor, image, cost):
print("Beer add to database")


@main.command("list")
def list_beers(style: Optional[str] = None):
"""Lists beers in database"""
beers = get_beers_from_database()
table = Table(title="Beerlog :beer_mug:")
headers = [
"id",
"name",
"style",
"flavor",
"image",
"cost",
"rate",
"date",
]
for header in headers:
table.add_column(header, style="magenta")
for beer in beers:
beer.date = beer.date.strftime("%Y-%m-%d")
values = [str(getattr(beer, header)) for header in headers]
table.add_row(*values)
console.print(table)
26 changes: 26 additions & 0 deletions beerlog/core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from typing import List
from sqlmodel import select
from beerlog.database import get_session
from beerlog.models import Beer


def add_beer_to_database(
name: str,
style: str,
flavor: int,
image: int,
cost: int,
) -> bool:
with get_session() as session:
beer = Beer(
name=name, style=style, flavor=flavor, image=image, cost=cost
)
session.add(beer)
session.commit()
return True


def get_beers_from_database() -> List[Beer]:
with get_session() as session:
sql = select(Beer)
return list(session.exec(sql))
19 changes: 19 additions & 0 deletions beerlog/database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import warnings
from sqlalchemy.exc import SAWarning
from sqlmodel.sql.expression import Select, SelectOfScalar
from sqlmodel import create_engine, Session
from beerlog import models
from beerlog.config import settings

warnings.filterwarnings("ignore", category=SAWarning)
SelectOfScalar.inherit_cache = True
Select.inherit_cache = True


engine = create_engine(settings.database.url)

models.SQLModel.metadata.create_all(engine)


def get_session():
return Session(engine)
30 changes: 30 additions & 0 deletions beerlog/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from typing import Optional
from sqlmodel import SQLModel, Field
from pydantic import validator
from statistics import mean
from datetime import datetime


class Beer(SQLModel, table=True):
id: Optional[int] = Field(primary_key=True, default=None, index=True)
name: str
style: str
flavor: int
image: int
cost: int
rate: int = 0
date: datetime = Field(default_factory=datetime.now)

@validator("flavor", "image", "cost")
def validate_ratings(cls, v, field):
if v < 1 or v > 10:
raise RuntimeError(f"{field.name} must between 1 and 10")
return v

@validator("rate", always=True)
def calculate_rate(cls, v, values):
rate = mean([values["flavor"], values["image"], values["cost"]])
return int(rate)


# brewdog = Beer(name="Brewdog", style="NEIPA", flavor=10, image=8, cost=8)
32 changes: 32 additions & 0 deletions beerlog/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from pydantic import BaseModel, validator
from datetime import datetime
from fastapi import HTTPException, status


class BeerOut(BaseModel):
id: int
name: str
style: str
flavor: int
image: int
cost: int
rate: int
date: datetime


class BeerIn(BaseModel):
name: str
style: str
flavor: int
image: int
cost: int


@validator("image", "flavor", "cost")
def validate_ratings(cls, v, field):
if v < 1 or v > 10:
raise HTTPException(
detail=f"{field.name} must be between 1 and 10",
status_code=status.HTTP_400_BAD_REQUEST,
)
return v
3 changes: 3 additions & 0 deletions beerlog/settings.toml
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
name = "beerlog"

[database]
url = "sqlite:///beerlog.db"
14 changes: 14 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import pytest
from unittest.mock import patch
from sqlmodel import create_engine
from beerlog import models


@pytest.fixture(autouse=True, scope="function")
def each_test_uses_separate_database(request):
tmpdir = request.getfixturevalue("tmpdir")
test_db = tmpdir.join("beerlog.test.db")
engine = create_engine(f"sqlite:///{test_db}")
models.SQLModel.metadata.create_all(bind=engine)
with patch("beerlog.database.engine", engine):
yield
Loading