<gh_stars>0
from functools import wraps
from os import environ
from typing import Dict

from flask import Flask, request

from api_carrinho import __VERSION__
from api_carrinho.log import log
from api_carrinho.models.carrinho import Carrinho
from api_carrinho.models.produto import Produto
from api_carrinho.persist.carrinhos import db_carrinho_fetch, db_carrinho_save
from api_carrinho.persist.cupons import db_cupom_fetch
from api_carrinho.persist.produtos import ProdutoSemEstoqueError, db_produto_fetch

app = Flask(__name__)


def return_wrapper(f):
    """
    Este decorator serve para "embalar" as funções da API, preparando o retorno da
    seguinte forma:

    * As funções da API sempre devem retornar um dicionário com dados compatíveis com JSON
      (bool, str, int, float).
    * Caso a função da API não levante uma exceção, este decorator preparará o retorno da
      seguinte forma:
      { "sucesso": true, "dados": { ... dados da função da API ... } }
    * Caso a função da API levante uma exceção, este decorator preparará o retorno da
      seguinte forma:
      { "sucesso": false, "erro": {
            "tipo": "NomeDaExcecao", "descricao": "valor da exceção"
        }}
    """

    @wraps(f)
    def wrapper(*args, **kwargs):
        try:
            # Chama a função da API e retorna seu dicionário de retorno ...
            return {"sucesso": True, "dados": f(*args, **kwargs)}
        except Exception as ex:
            # ... ou retorna a exceção caso ocorra.
            log.exception("exceção encontrada: %s", ex)
            return {
                "sucesso": False,
                "erro": {"tipo": str(ex.__class__.__name__), "descricao": str(ex)},
            }

    return wrapper


@app.post("/novo")
@return_wrapper
def novo() -> Dict:
    """
    Cria um novo cupom de desconto, persistindo. Gera e retorna um código (UUID4).
    """
    cliente = request.form.get("cliente")
    carrinho = Carrinho(cliente=cliente)
    db_carrinho_save(carrinho)
    return {"carrinho_codigo": carrinho.codigo}


@app.post("/cliente-define")
@return_wrapper
def define_cliente() -> Dict:
    """
    Altera o cliente associado ao carrinho.
    """
    cliente = int(request.form["cliente"])
    carrinho_codigo = request.form["carrinho"]
    carrinho = db_carrinho_fetch(carrinho_codigo)
    carrinho.define_cliente(cliente)
    return {}


@app.post("/produto-adiciona")
@return_wrapper
def produto_adiciona() -> Dict:
    """
    Adiciona um produto em um carrinho de compras.
    """
    carrinho_codigo = request.form["carrinho"]
    produto_codigo = request.form["produto"]
    carrinho = db_carrinho_fetch(carrinho_codigo)
    produto_persisted = db_produto_fetch(produto_codigo)
    produto = Produto(
        codigo=produto_persisted.codigo,
        descricao=produto_persisted.descricao,
        preco_de=produto_persisted.preco_de,
        preco_por=produto_persisted.preco_por,
        quantidade=1,
    )
    carrinho.adiciona_produto(produto)
    return {}


@app.post("/produto-remove")
@return_wrapper
def produto_remove() -> Dict:
    """
    Remove um produto de um carrinho de compras.
    """
    carrinho_codigo = request.form["carrinho"]
    produto_codigo = request.form["produto"]
    carrinho = db_carrinho_fetch(carrinho_codigo)
    carrinho.remove_produto(produto_codigo)
    return {}


@app.post("/produto-define-quantidade")
@return_wrapper
def produto_define_quantidade() -> Dict:
    """
    Altera a quantidade de um produto em um carrinho de compras.
    """
    carrinho_codigo = request.form["carrinho"]
    produto_codigo = request.form["produto"]
    quantidade = int(request.form["quantidade"])
    carrinho = db_carrinho_fetch(carrinho_codigo)
    produto_persisted = db_produto_fetch(produto_codigo)
    if produto_persisted.estoque < quantidade:
        raise ProdutoSemEstoqueError(
            "produto %s sem estoque suficiente (estoque: %d, carrinho: %d)"
            % (
                produto_codigo,
                produto_persisted.estoque,
                quantidade,
            )
        )

    carrinho.define_produto_quantidade(produto_codigo, quantidade)
    return {}


@app.post("/limpa")
@return_wrapper
def limpa() -> Dict:
    """
    Remove todos os produtos de um carrinho de compras.
    """
    carrinho_codigo = request.form["carrinho"]
    carrinho = db_carrinho_fetch(carrinho_codigo)
    carrinho.remove_todos_produtos()
    return {}


@app.post("/cupom-define")
@return_wrapper
def cupom_define() -> Dict:
    """
    Associa um cupom de desconto em um carrinho de compras.
    """
    carrinho_codigo = request.form["carrinho"]
    cupom_codigo = request.form["cupom"]
    carrinho = db_carrinho_fetch(carrinho_codigo)
    cupom = db_cupom_fetch(cupom_codigo)
    carrinho.define_cupom_desconto(cupom)
    return {}


@app.get("/carrinho/<codigo>")
@return_wrapper
def carrinho(codigo: str) -> Dict:
    """
    Retorna uma representação em JSON de todo o carrinho de compras.
    """
    carrinho = db_carrinho_fetch(codigo)
    retorno_dados = {
        "codigo": carrinho.codigo,
        "cliente": carrinho.cliente,
        "totais": {"subtotal": carrinho.totais.subtotal, "total": carrinho.totais.total},
        "produtos": [],
        "cupom": {},
    }
    if carrinho.cupom:
        retorno_dados["cupom"] = {
            "codigo": carrinho.cupom.codigo,
            "valor": carrinho.cupom.valor,
        }
    for codigo_produto in carrinho.produtos:
        produto = carrinho.produtos[codigo_produto]
        retorno_dados["produtos"].append(
            {
                "codigo": produto.codigo,
                "descricao": produto.descricao,
                "quantidade": produto.quantidade,
                "preco_de": produto.preco_de,
                "preco_por": produto.preco_por,
            }
        )
    return retorno_dados


if __name__ == "__main__":
    host = environ.get("HOST", "1172.16.17.32")
    port = environ.get("PORT", 5000)
    log.info("iniciando api-carrinho versão %s", __VERSION__)
    app.run(host=host, port=port, debug=True)

from functools import wraps
from os import environ
from typing import Dict

from flask import Flask, request

from api_carrinho import __VERSION__
from api_carrinho.log import log
from api_carrinho.models.carrinho import Carrinho
from api_carrinho.models.produto import Produto
from api_carrinho.persist.carrinhos import db_carrinho_fetch, db_carrinho_save
from api_carrinho.persist.cupons import db_cupom_fetch
from api_carrinho.persist.produtos import ProdutoSemEstoqueError, db_produto_fetch

app = Flask(__name__)


def return_wrapper(f):
    """
    Este decorator serve para "embalar" as funções da API, preparando o retorno da
    seguinte forma:

    * As funções da API sempre devem retornar um dicionário com dados compatíveis com JSON
      (bool, str, int, float).
    * Caso a função da API não levante uma exceção, este decorator preparará o retorno da
      seguinte forma:
      { "sucesso": true, "dados": { ... dados da função da API ... } }
    * Caso a função da API levante uma exceção, este decorator preparará o retorno da
      seguinte forma:
      { "sucesso": false, "erro": {
            "tipo": "NomeDaExcecao", "descricao": "valor da exceção"
        }}
    """

    @wraps(f)
    def wrapper(*args, **kwargs):
        try:
            # Chama a função da API e retorna seu dicionário de retorno ...
            return {"sucesso": True, "dados": f(*args, **kwargs)}
        except Exception as ex:
            # ... ou retorna a exceção caso ocorra.
            log.exception("exceção encontrada: %s", ex)
            return {
                "sucesso": False,
                "erro": {"tipo": str(ex.__class__.__name__), "descricao": str(ex)},
            }

    return wrapper


@app.post("/novo")
@return_wrapper
def novo() -> Dict:
    """
    Cria um novo cupom de desconto, persistindo. Gera e retorna um código (UUID4).
    """
    cliente = request.form.get("cliente")
    carrinho = Carrinho(cliente=cliente)
    db_carrinho_save(carrinho)
    return {"carrinho_codigo": carrinho.codigo}


@app.post("/cliente-define")
@return_wrapper
def define_cliente() -> Dict:
    """
    Altera o cliente associado ao carrinho.
    """
    cliente = int(request.form["cliente"])
    carrinho_codigo = request.form["carrinho"]
    carrinho = db_carrinho_fetch(carrinho_codigo)
    carrinho.define_cliente(cliente)
    return {}


@app.post("/produto-adiciona")
@return_wrapper
def produto_adiciona() -> Dict:
    """
    Adiciona um produto em um carrinho de compras.
    """
    carrinho_codigo = request.form["carrinho"]
    produto_codigo = request.form["produto"]
    carrinho = db_carrinho_fetch(carrinho_codigo)
    produto_persisted = db_produto_fetch(produto_codigo)
    produto = Produto(
        codigo=produto_persisted.codigo,
        descricao=produto_persisted.descricao,
        preco_de=produto_persisted.preco_de,
        preco_por=produto_persisted.preco_por,
        quantidade=1,
    )
    carrinho.adiciona_produto(produto)
    return {}


@app.post("/produto-remove")
@return_wrapper
def produto_remove() -> Dict:
    """
    Remove um produto de um carrinho de compras.
    """
    carrinho_codigo = request.form["carrinho"]
    produto_codigo = request.form["produto"]
    carrinho = db_carrinho_fetch(carrinho_codigo)
    carrinho.remove_produto(produto_codigo)
    return {}


@app.post("/produto-define-quantidade")
@return_wrapper
def produto_define_quantidade() -> Dict:
    """
    Altera a quantidade de um produto em um carrinho de compras.
    """
    carrinho_codigo = request.form["carrinho"]
    produto_codigo = request.form["produto"]
    quantidade = int(request.form["quantidade"])
    carrinho = db_carrinho_fetch(carrinho_codigo)
    produto_persisted = db_produto_fetch(produto_codigo)
    if produto_persisted.estoque < quantidade:
        raise ProdutoSemEstoqueError(
            "produto %s sem estoque suficiente (estoque: %d, carrinho: %d)"
            % (
                produto_codigo,
                produto_persisted.estoque,
                quantidade,
            )
        )

    carrinho.define_produto_quantidade(produto_codigo, quantidade)
    return {}


@app.post("/limpa")
@return_wrapper
def limpa() -> Dict:
    """
    Remove todos os produtos de um carrinho de compras.
    """
    carrinho_codigo = request.form["carrinho"]
    carrinho = db_carrinho_fetch(carrinho_codigo)
    carrinho.remove_todos_produtos()
    return {}


@app.post("/cupom-define")
@return_wrapper
def cupom_define() -> Dict:
    """
    Associa um cupom de desconto em um carrinho de compras.
    """
    carrinho_codigo = request.form["carrinho"]
    cupom_codigo = request.form["cupom"]
    carrinho = db_carrinho_fetch(carrinho_codigo)
    cupom = db_cupom_fetch(cupom_codigo)
    carrinho.define_cupom_desconto(cupom)
    return {}


@app.get("/carrinho/<codigo>")
@return_wrapper
def carrinho(codigo: str) -> Dict:
    """
    Retorna uma representação em JSON de todo o carrinho de compras.
    """
    carrinho = db_carrinho_fetch(codigo)
    retorno_dados = {
        "codigo": carrinho.codigo,
        "cliente": carrinho.cliente,
        "totais": {"subtotal": carrinho.totais.subtotal, "total": carrinho.totais.total},
        "produtos": [],
        "cupom": {},
    }
    if carrinho.cupom:
        retorno_dados["cupom"] = {
            "codigo": carrinho.cupom.codigo,
            "valor": carrinho.cupom.valor,
        }
    for codigo_produto in carrinho.produtos:
        produto = carrinho.produtos[codigo_produto]
        retorno_dados["produtos"].append(
            {
                "codigo": produto.codigo,
                "descricao": produto.descricao,
                "quantidade": produto.quantidade,
                "preco_de": produto.preco_de,
                "preco_por": produto.preco_por,
            }
        )
    return retorno_dados


if __name__ == "__main__":
    host = environ.get("HOST", "1172.16.17.32")
    port = environ.get("PORT", 5000)
    log.info("iniciando api-carrinho versão %s", __VERSION__)
    app.run(host=host, port=port, debug=True)
Este decorator serve para "embalar" as funções da API, preparando o retorno da     seguinte forma:      * As funções da API sempre devem retornar um dicionário com dados compatíveis com JSON       (bool, str, int, float).     * Caso a função da API não levante uma exceção, este decorator preparará o retorno da       seguinte forma:       { "sucesso": true, "dados": { ... dados da função da API ... } }     * Caso a função da API levante uma exceção, este decorator preparará o retorno da       seguinte forma:       { "sucesso": false, "erro": {             "tipo": "NomeDaExcecao", "descricao": "valor da exceção"         }} # Chama a função da API e retorna seu dicionário de retorno ... # ... ou retorna a exceção caso ocorra. Cria um novo cupom de desconto, persistindo. Gera e retorna um código (UUID4). Altera o cliente associado ao carrinho. Adiciona um produto em um carrinho de compras. Remove um produto de um carrinho de compras. Altera a quantidade de um produto em um carrinho de compras. Remove todos os produtos de um carrinho de compras. Associa um cupom de desconto em um carrinho de compras. Retorna uma representação em JSON de todo o carrinho de compras.