api recipe and ingredient, GET and PUT
This commit is contained in:
parent
7e4c1dca7e
commit
4ddb2ea8ff
|
|
@ -0,0 +1,162 @@
|
||||||
|
# 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/#use-with-ide
|
||||||
|
.pdm.toml
|
||||||
|
|
||||||
|
# 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/
|
||||||
|
|
||||||
|
data/
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
from fastapi import FastAPI, HTTPException
|
||||||
|
from data import Recipe, Ingredient
|
||||||
|
import db
|
||||||
|
from psycopg2.errors import UniqueViolation
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
conn = db.connect()
|
||||||
|
|
||||||
|
|
||||||
|
@app.put("/recipe")
|
||||||
|
def new_recipe(recipe: Recipe) -> Recipe | None:
|
||||||
|
try:
|
||||||
|
recipe = db.insert_recipe(recipe, conn)
|
||||||
|
except UniqueViolation:
|
||||||
|
raise HTTPException(status_code=400, detail="id conflict")
|
||||||
|
return recipe
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/recipe/{item_id}")
|
||||||
|
def get_recipe(item_id: int):
|
||||||
|
recipe = db.get_recipe(item_id, conn)
|
||||||
|
if not recipe:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail=f"Recipe not found: id = {id}",
|
||||||
|
)
|
||||||
|
return recipe
|
||||||
|
|
||||||
|
|
||||||
|
@app.put("/ingredient")
|
||||||
|
def new_ingredient(ingredient: Ingredient) -> Ingredient | None:
|
||||||
|
try:
|
||||||
|
ingredient = db.insert_ingredient(ingredient, conn)
|
||||||
|
except UniqueViolation:
|
||||||
|
raise HTTPException(status_code=400, detail="id conflict")
|
||||||
|
return ingredient
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/ingredient/{item_id}")
|
||||||
|
def get_ingredient(id):
|
||||||
|
ingredient = db.get_ingredient(id, conn)
|
||||||
|
print(ingredient)
|
||||||
|
if not ingredient:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail=f"Ingredient not found: id = {id}",
|
||||||
|
)
|
||||||
|
return ingredient
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from recipe_scrapers._abstract import AbstractScraper
|
||||||
|
from ingredient_parser.postprocess import ParsedIngredient
|
||||||
|
|
||||||
|
|
||||||
|
class Recipe(BaseModel):
|
||||||
|
id: int | None = None
|
||||||
|
title: str | None
|
||||||
|
url: str | None
|
||||||
|
site_name: str
|
||||||
|
cuisine: str
|
||||||
|
category: str
|
||||||
|
description: str
|
||||||
|
cook_time: float
|
||||||
|
prep_time: float
|
||||||
|
yields: str
|
||||||
|
|
||||||
|
|
||||||
|
class Ingredient(BaseModel):
|
||||||
|
id: int | None = None
|
||||||
|
recipe_id: int | None = None
|
||||||
|
text: str | None = None
|
||||||
|
purpose: str | None = None
|
||||||
|
name: str | None
|
||||||
|
name_conf: float | None
|
||||||
|
amount_quantity: list[str] | None
|
||||||
|
amount_unit: list[str] | None
|
||||||
|
amount_conf: list[float] | None
|
||||||
|
preparation: str | None
|
||||||
|
preparation_conf: float | None
|
||||||
|
comment: str | None
|
||||||
|
comment_conf: float | None
|
||||||
|
|
||||||
|
|
||||||
|
def parsed_ingredient_to_ingredient(
|
||||||
|
parsed_ingredient: ParsedIngredient,
|
||||||
|
purpose=None,
|
||||||
|
recipe_id=None,
|
||||||
|
) -> Ingredient:
|
||||||
|
data = {}
|
||||||
|
data["purpose"] = purpose
|
||||||
|
data["recipe_id"] = recipe_id
|
||||||
|
fields = ["name", "preparation", "comment"]
|
||||||
|
|
||||||
|
data["text"] = parsed_ingredient.sentence
|
||||||
|
|
||||||
|
for field in fields:
|
||||||
|
attr = getattr(parsed_ingredient, field)
|
||||||
|
data[field] = attr.text if attr else None
|
||||||
|
data[field + "_conf"] = attr.confidence if attr else None
|
||||||
|
|
||||||
|
amounts = parsed_ingredient.amount or []
|
||||||
|
if amounts:
|
||||||
|
data["amount_quantity"] = []
|
||||||
|
data["amount_unit"] = []
|
||||||
|
data["amount_conf"] = []
|
||||||
|
for amount in amounts:
|
||||||
|
data["amount_quantity"].append(amount.quantity if amount else "")
|
||||||
|
data["amount_unit"].append(amount.unit if amount else "")
|
||||||
|
data["amount_conf"].append(amount.confidence if amount else 0)
|
||||||
|
else:
|
||||||
|
data["amount_quantity"] = None
|
||||||
|
data["amount_unit"] = None
|
||||||
|
data["amount_conf"] = None
|
||||||
|
|
||||||
|
return Ingredient(**data)
|
||||||
|
|
||||||
|
|
||||||
|
def scraped_reciepe_to_recipe(recipe: AbstractScraper) -> Recipe:
|
||||||
|
site_name = recipe.site_name()
|
||||||
|
if not site_name:
|
||||||
|
site_name = ""
|
||||||
|
site_name = str(site_name)
|
||||||
|
|
||||||
|
return Recipe(
|
||||||
|
title=recipe.title(),
|
||||||
|
site_name=site_name,
|
||||||
|
url=recipe.url,
|
||||||
|
cuisine=recipe.cuisine(),
|
||||||
|
category=recipe.category(),
|
||||||
|
description=recipe.description(),
|
||||||
|
cook_time=recipe.cook_time(),
|
||||||
|
prep_time=recipe.prep_time(),
|
||||||
|
yields=recipe.yields(),
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,176 @@
|
||||||
|
from pydantic import BaseModel
|
||||||
|
import psycopg2 as pg
|
||||||
|
from psycopg2 import sql
|
||||||
|
from psycopg2.extras import RealDictCursor
|
||||||
|
from os import environ as env, stat
|
||||||
|
from functools import wraps
|
||||||
|
from inspect import getfullargspec
|
||||||
|
from typing import Callable, Type
|
||||||
|
from data import Recipe, Ingredient
|
||||||
|
|
||||||
|
|
||||||
|
class NoConnectionException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def connect():
|
||||||
|
return pg.connect(
|
||||||
|
dbname=env.get("POSTGRES_DB"),
|
||||||
|
user=env.get("POSTGRES_USER"),
|
||||||
|
password=env.get("POSTGRES_PASSWORD"),
|
||||||
|
host=env.get("POSTGRES_HOST"),
|
||||||
|
port=env.get("POSTGRES_PORT"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def connect_by_default(
|
||||||
|
func: Callable,
|
||||||
|
kword: str = "conn",
|
||||||
|
conn_func: Callable[..., pg.extensions.connection] = connect,
|
||||||
|
) -> Callable:
|
||||||
|
@wraps(func)
|
||||||
|
def wrapped(*args, **kargs):
|
||||||
|
for i, arg in enumerate(args):
|
||||||
|
kargs[getfullargspec(func).args[i]] = arg
|
||||||
|
if not kargs.get(kword):
|
||||||
|
kargs[kword] = conn_func()
|
||||||
|
return func(**kargs)
|
||||||
|
|
||||||
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
|
@connect_by_default
|
||||||
|
def select_by_id(
|
||||||
|
schema_name: str,
|
||||||
|
table_name: str,
|
||||||
|
id: int,
|
||||||
|
columns: list[str] | None = None,
|
||||||
|
conn: pg.extensions.connection | None = None,
|
||||||
|
) -> dict | None:
|
||||||
|
if not conn:
|
||||||
|
raise NoConnectionException()
|
||||||
|
|
||||||
|
query = sql.SQL(
|
||||||
|
f"""
|
||||||
|
SELECT {'{}' if columns else '*'}
|
||||||
|
FROM {{}}.{{}}
|
||||||
|
WHERE id = %s;"""
|
||||||
|
)
|
||||||
|
if columns:
|
||||||
|
cols = sql.SQL(",").join([sql.Identifier(col) for col in columns])
|
||||||
|
query = query.format(
|
||||||
|
cols, sql.Identifier(schema_name), sql.Identifier(table_name)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
query = query.format(
|
||||||
|
sql.Identifier(schema_name), sql.Identifier(table_name)
|
||||||
|
)
|
||||||
|
|
||||||
|
with conn.cursor(cursor_factory=RealDictCursor) as curs:
|
||||||
|
curs.execute(query, (id,))
|
||||||
|
data = curs.fetchone()
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
return
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
@connect_by_default
|
||||||
|
def insert_model(
|
||||||
|
schema_name: str,
|
||||||
|
table_name: str,
|
||||||
|
model: BaseModel,
|
||||||
|
conn: pg.extensions.connection | None = None,
|
||||||
|
) -> dict | None:
|
||||||
|
if not conn:
|
||||||
|
raise NoConnectionException()
|
||||||
|
|
||||||
|
data = model.model_dump(exclude_none=True)
|
||||||
|
cols = data.keys()
|
||||||
|
vals = data.values()
|
||||||
|
|
||||||
|
query = sql.SQL(
|
||||||
|
f"""
|
||||||
|
INSERT INTO {{schema}}.{{table}} (
|
||||||
|
{{fields}}
|
||||||
|
) VALUES (
|
||||||
|
{", ".join(["%s" for _ in vals])}
|
||||||
|
)
|
||||||
|
RETURNING id;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
with conn.cursor() as curs:
|
||||||
|
try:
|
||||||
|
curs.execute(
|
||||||
|
query.format(
|
||||||
|
schema=sql.Identifier(schema_name),
|
||||||
|
table=sql.Identifier(table_name),
|
||||||
|
fields=sql.SQL(",").join(
|
||||||
|
[sql.Identifier(col) for col in cols]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(*vals,),
|
||||||
|
)
|
||||||
|
row = curs.fetchone()
|
||||||
|
conn.commit()
|
||||||
|
print(row)
|
||||||
|
except pg.errors.UniqueViolation as error:
|
||||||
|
conn.rollback()
|
||||||
|
raise error
|
||||||
|
|
||||||
|
if not row:
|
||||||
|
return
|
||||||
|
id = row[0]
|
||||||
|
|
||||||
|
data["id"] = id
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
@connect_by_default
|
||||||
|
def insert_recipe(
|
||||||
|
recipe: Recipe,
|
||||||
|
conn: pg.extensions.connection | None = None,
|
||||||
|
) -> Recipe | None:
|
||||||
|
data = insert_model("recipe", "recipe", recipe, conn)
|
||||||
|
if not data:
|
||||||
|
return
|
||||||
|
return Recipe(**data)
|
||||||
|
|
||||||
|
|
||||||
|
@connect_by_default
|
||||||
|
def get_recipe(
|
||||||
|
id: int,
|
||||||
|
conn: pg.extensions.connection | None = None,
|
||||||
|
) -> Recipe | None:
|
||||||
|
data = select_by_id("recipe", "recipe", id, conn=conn)
|
||||||
|
if not data:
|
||||||
|
return
|
||||||
|
|
||||||
|
recipe = Recipe(**data)
|
||||||
|
return recipe
|
||||||
|
|
||||||
|
|
||||||
|
@connect_by_default
|
||||||
|
def insert_ingredient(
|
||||||
|
ingredient: Ingredient,
|
||||||
|
conn: pg.extensions.connection | None = None,
|
||||||
|
) -> Ingredient | None:
|
||||||
|
data = insert_model("recipe", "ingredient", ingredient, conn)
|
||||||
|
if not data:
|
||||||
|
return
|
||||||
|
return Ingredient(**data)
|
||||||
|
|
||||||
|
|
||||||
|
@connect_by_default
|
||||||
|
def get_ingredient(
|
||||||
|
id: int,
|
||||||
|
conn: pg.extensions.connection | None = None,
|
||||||
|
) -> Ingredient | None:
|
||||||
|
data = select_by_id("recipe", "ingredient", id, conn=conn)
|
||||||
|
if not data:
|
||||||
|
return
|
||||||
|
|
||||||
|
ingredient = Ingredient(**data)
|
||||||
|
return ingredient
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
services:
|
||||||
|
psql:
|
||||||
|
image: postgres
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: ${POSTGRES_USER}
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB}
|
||||||
|
ports:
|
||||||
|
- "${POSTGRES_PORT}:5432"
|
||||||
|
volumes:
|
||||||
|
- "./data/${COMPOSE_PROJECT_NAME}:/var/lib/postgresql/data"
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
pandas
|
||||||
|
psycopg2-binary
|
||||||
|
fastapi
|
||||||
|
recipe-scrapers
|
||||||
|
ingredient-parser-nlp
|
||||||
|
pydantic
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
[core]
|
||||||
|
engine = pg
|
||||||
|
top_dir = sqitch
|
||||||
|
# plan_file = sqitch/sqitch.plan
|
||||||
|
# [engine "pg"]
|
||||||
|
# target = db:pg:
|
||||||
|
# registry = sqitch
|
||||||
|
# client = psql
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
-- Deploy rsugest:ingredient-table to pg
|
||||||
|
-- requires: recipe-schema
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
CREATE TABLE recipe.ingredient (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
recipe_id INT,
|
||||||
|
text TEXT,
|
||||||
|
purpose TEXT,
|
||||||
|
name TEXT,
|
||||||
|
name_conf FLOAT,
|
||||||
|
amount_quantity TEXT[],
|
||||||
|
amount_unit TEXT[],
|
||||||
|
amount_conf FLOAT[],
|
||||||
|
preparation TEXT,
|
||||||
|
preparation_conf FLOAT,
|
||||||
|
comment TEXT,
|
||||||
|
comment_conf FLOAT);
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- Deploy rsugest:recipe-schema to pg
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
CREATE SCHEMA recipe;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
-- Deploy rsugest:recipe-table to pg
|
||||||
|
-- requires: recipe-schema
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
CREATE TABLE recipe.recipe (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
title TEXT,
|
||||||
|
url TEXT,
|
||||||
|
site_name TEXT,
|
||||||
|
cuisine TEXT,
|
||||||
|
category TEXT,
|
||||||
|
description TEXT,
|
||||||
|
cook_time REAL,
|
||||||
|
prep_time REAL,
|
||||||
|
yields TEXT);
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- Revert rsugest:ingredient-table from pg
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
DROP TABLE recipe.ingredient;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- Revert rsugest:recipe-schema from pg
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
DROP SCHEMA recipe;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- Revert rsugest:recipe-table from pg
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
DROP TABLE recipe.recipe;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
%syntax-version=1.0.0
|
||||||
|
%project=rsugest
|
||||||
|
|
||||||
|
recipe-schema 2024-02-07T01:24:03Z Andrei,,, <andrei@bloated-boat> # adding schema for recipes
|
||||||
|
recipe-table [recipe-schema] 2024-02-07T01:42:28Z Andrei,,, <andrei@bloated-boat> # adding table for recipes
|
||||||
|
ingredient-table [recipe-schema] 2024-02-07T03:50:20Z Andrei,,, <andrei@bloated-boat> # adding table for ingredients
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
-- Verify rsugest:ingredient-table on pg
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
SELECT id, recipe_id, text, purpose, name, name_conf,
|
||||||
|
amount_quantity, amount_unit, amount_conf, preparation,
|
||||||
|
preparation_conf, comment, comment_conf
|
||||||
|
FROM recipe.ingredient
|
||||||
|
WHERE FALSE;
|
||||||
|
|
||||||
|
ROLLBACK;
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- Verify rsugest:recipe-schema on pg
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
SELECT pg_catalog.has_schema_privilege('recipe', 'usage');
|
||||||
|
|
||||||
|
ROLLBACK;
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
-- Verify rsugest:recipe-table on pg
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
|
||||||
|
select id, title, url, site_name, cuisine, category, description,
|
||||||
|
cook_time, prep_time, yields
|
||||||
|
FROM recipe.recipe
|
||||||
|
WHERE FALSE;
|
||||||
|
|
||||||
|
ROLLBACK;
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"title": "string",
|
||||||
|
"url": "string",
|
||||||
|
"site_name": "string",
|
||||||
|
"cuisine": "string",
|
||||||
|
"category": "string",
|
||||||
|
"description": "string",
|
||||||
|
"cook_time": 0,
|
||||||
|
"prep_time": 0,
|
||||||
|
"yields": "string"
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue