api.py 9.64 KB
Newer Older
1
import datetime
David Foucher's avatar
David Foucher committed
2
import os
David Foucher's avatar
David Foucher committed
3
import re
Yohan Boniface's avatar
Yohan Boniface committed
4 5
from http import HTTPStatus

6 7
import gitlab

8
from roll import HttpError, Roll
Yohan Boniface's avatar
Yohan Boniface committed
9
from roll.extensions import cors
David Foucher's avatar
David Foucher committed
10

David Foucher's avatar
David Foucher committed
11
import ujson as json
Yohan Boniface's avatar
Yohan Boniface committed
12

13 14 15 16 17 18 19
from . import (
    VERSION,
    get_financements,
    get_remunerations,
    simulate,
    simulate_remuneration,
)
David Foucher's avatar
David Foucher committed
20
from . import routine
David Foucher's avatar
David Foucher committed
21
from .config import AUTHORIZED, FINANCEMENTS, COMMIT_AUTHORIZED, GLOSSARY, IDCC, NAF, CERTIFINFO, RAW_RULES, SCHEMA, GITLAB_TOKEN
David Foucher's avatar
David Foucher committed
22 23
from .context import Context
from .debugging import SCENARIOS, data_from_lbf_url, make_scenario
David Foucher's avatar
David Foucher committed
24
from .exceptions import DataError, NotModifiedError, UnauthorizedAccess
25
from .helpers import flatten, fold_name
David Foucher's avatar
David Foucher committed
26
from .legacy import simulate_legacy
27
from .loggers import log_simulate, logger
Yohan Boniface's avatar
Yohan Boniface committed
28
from .openapis import OPENAPI
29
from .routine import get_formation_json, search_term
30
from .source import submit_modification
Yohan Boniface's avatar
Yohan Boniface committed
31 32

app = Roll()
Yohan Boniface's avatar
Yohan Boniface committed
33
cors(app)
Yohan Boniface's avatar
Yohan Boniface committed
34 35


David Foucher's avatar
Black  
David Foucher committed
36
@app.listen("response")
Yohan Boniface's avatar
Yohan Boniface committed
37
async def expose_version(request, response):
David Foucher's avatar
Black  
David Foucher committed
38
    response.headers["Version"] = VERSION
Yohan Boniface's avatar
Yohan Boniface committed
39 40


David Foucher's avatar
Black  
David Foucher committed
41
@app.listen("error")
42
async def json_error_response(request, response, error):
Régis Behmo's avatar
Régis Behmo committed
43
    if isinstance(error.message, (str, bytes)):
David Foucher's avatar
Black  
David Foucher committed
44
        error.message = {"error": error.message}
Régis Behmo's avatar
Régis Behmo committed
45
    response.json = error.message
Yohan Boniface's avatar
Yohan Boniface committed
46
    if error.status != HTTPStatus.NOT_FOUND:
David Foucher's avatar
Black  
David Foucher committed
47 48
        logger.debug(
            f"HttpError: status={error.status}, version={VERSION}, "
49
            f"message={response.body}, request={request.body}, url={request.url}"
David Foucher's avatar
Black  
David Foucher committed
50
        )
51 52


53 54 55 56 57 58 59 60 61 62
@app.route("/healthcheck")
async def healthcheck(request, response):
    response.json = {
        "status": "running",
        "version": f"{VERSION}",
        "last version": f"{os.environ.get('VERSION', 'NaN')}",
        "back version": f"{os.environ.get('OLD_VERSION', 'NaN')}",
    }


David Foucher's avatar
Black  
David Foucher committed
63
@app.route("/financement", methods=["POST"])
64
async def simulate_(request, response):
65
    context = request.json
David Foucher's avatar
Black  
David Foucher committed
66
    financements = get_financements(tags=request.query.list("tags", []))
Yohan Boniface's avatar
Yohan Boniface committed
67
    try:
68
        await simulate(context, financements)
Yohan Boniface's avatar
Yohan Boniface committed
69 70 71
    except DataError as err:
        error = {err.key: err.error}
        log_simulate(context, errors=error)
72
        raise HttpError(HTTPStatus.BAD_REQUEST, error)
Régis Behmo's avatar
Régis Behmo committed
73

David Foucher's avatar
David Foucher committed
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
    eligible = request.query.bool("eligible", None)
    if eligible is not None:
        financements = [f for f in financements if f["eligible"] == eligible]
    else:
        financements = sorted(
            financements, key=lambda value: value["eligible"], reverse=True
        )

    explain = request.query.bool("explain", False)
    for financement in financements:
        financement["explain"] = (
            [s.json for s in financement["explain"]] if explain else None
        )
    body = {"financements": financements}
    if request.query.bool("context", False):
        body["context"] = {
90 91
            k: v for k, v in context.items() if k in SCHEMA and "label" in SCHEMA[k]
        }
David Foucher's avatar
David Foucher committed
92 93 94 95 96 97 98
    if request.query.bool("scenario", False):
        body["scenario"] = make_scenario(context, financements)
    response.json = body

    log_simulate(context, financements=financements)


David Foucher's avatar
David Foucher committed
99
# TODO : add pointer error for bad region number + test
100
@app.route("/remuneration-aide", methods=["POST"])
David Foucher's avatar
David Foucher committed
101
async def remuneration_(request, response):
102
    context = request.json
David Foucher's avatar
David Foucher committed
103 104
    remunerations = get_remunerations(tags=request.query.list("tags", []))
    try:
105
        await simulate_remuneration(context, remunerations)
David Foucher's avatar
David Foucher committed
106 107 108
    except DataError as err:
        error = {err.key: err.error}
        log_simulate(context, errors=error)
109
        raise HttpError(HTTPStatus.BAD_REQUEST, error)
David Foucher's avatar
David Foucher committed
110 111 112 113 114 115 116 117 118 119 120 121 122 123

    # TODO: explain only for financement see routine.py check_remuneration
    # explain = request.query.bool("explain", False)
    # for remuneration in remunerations:
    #     remuneration["explain"] = (
    #         [s.json for s in remunerations["explain"]] if explain else None
    #     )

    body = {"remunerations": remunerations}
    # if request.query.bool("context", False):
    #     body["context"] = {
    #         k: v for k, v in context.items()
    #         if k in SCHEMA and "label" in SCHEMA[k]}
    response.json = body
Régis Behmo's avatar
Régis Behmo committed
124

Yohan Boniface's avatar
Yohan Boniface committed
125

David Foucher's avatar
Black  
David Foucher committed
126
app.route("/legacy", methods=["POST"])(simulate_legacy)
David Foucher's avatar
David Foucher committed
127 128


David Foucher's avatar
Black  
David Foucher committed
129
@app.route("/schema")
Yohan Boniface's avatar
Yohan Boniface committed
130
async def schema(request, response):
Yohan Boniface's avatar
Yohan Boniface committed
131
    response.json = OPENAPI
Yohan Boniface's avatar
Yohan Boniface committed
132 133


David Foucher's avatar
Black  
David Foucher committed
134
@app.route("/naf")
Yohan Boniface's avatar
Yohan Boniface committed
135
async def naf(request, response):
136
    response.json = search_term(NAF, request.query.get("q"))
Yohan Boniface's avatar
Yohan Boniface committed
137 138


David Foucher's avatar
David Foucher committed
139 140
@app.route("/idcc")
async def idcc(request, response):
141
    response.json = search_term(IDCC, request.query.get("q"))
David Foucher's avatar
David Foucher committed
142 143


David Foucher's avatar
David Foucher committed
144 145 146
@app.route("/certifinfo")
async def certifinfo(request, response):
    response.json = search_term(CERTIFINFO, request.query.get("q"))
David Foucher's avatar
David Foucher committed
147 148


David Foucher's avatar
Black  
David Foucher committed
149
@app.route("/explore/schema")
Yohan Boniface's avatar
Yohan Boniface committed
150 151 152 153
async def explore_schema(request, response):
    response.json = SCHEMA


David Foucher's avatar
Black  
David Foucher committed
154
@app.route("/explore/rules")
Yohan Boniface's avatar
Yohan Boniface committed
155 156
async def explore_rules(request, response):
    response.json = RAW_RULES
157 158


David Foucher's avatar
Black  
David Foucher committed
159
@app.route("/explore/glossary")
Yohan Boniface's avatar
Yohan Boniface committed
160 161 162 163
async def explore_glossary(request, response):
    response.json = GLOSSARY


David Foucher's avatar
Black  
David Foucher committed
164
@app.route("/explore/financements")
165 166 167 168
async def explore_financements(request, response):
    response.json = FINANCEMENTS


David Foucher's avatar
Black  
David Foucher committed
169
@app.route("/explore/scenarios")
170 171
async def explore_scenarios(request, response):
    response.json = SCENARIOS
Yohan Boniface's avatar
Yohan Boniface committed
172 173


David Foucher's avatar
Black  
David Foucher committed
174
@app.route("/explore/catalog")
175
async def explore_catalog(request, response):
David Foucher's avatar
Black  
David Foucher committed
176
    data = await get_formation_json(request.query.get("id"))
David Foucher's avatar
David Foucher committed
177
    response.body = json.dumps(data, indent=2, ensure_ascii=False)
178 179


David Foucher's avatar
Black  
David Foucher committed
180
@app.route("/explore/decode-lbf-url")
181
async def decode_lbf_url(request, response):
David Foucher's avatar
Black  
David Foucher committed
182
    response.json = data_from_lbf_url(request.query.get("url"))
183 184