alessandro trinca tornidor
commited on
Commit
·
8a3e357
1
Parent(s):
04a6060
feat: add backend endpoint /thesaurus-wordsapi with related add functions (mongodb persistence)
Browse files- my_ghost_writer/app.py +90 -42
- my_ghost_writer/constants.py +11 -2
- my_ghost_writer/pymongo_get_database.py +0 -16
- my_ghost_writer/{thesaurus.py → pymongo_operations_rw.py} +5 -7
- my_ghost_writer/pymongo_utils.py +33 -0
- my_ghost_writer/type_hints.py +1 -3
- poetry.lock +84 -10
- pyproject.toml +5 -2
- requirements.txt +2 -1
- template.env +6 -1
my_ghost_writer/app.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
import json
|
| 2 |
from datetime import datetime
|
| 3 |
from http.client import HTTPException
|
|
@@ -10,15 +11,38 @@ from fastapi.exceptions import RequestValidationError
|
|
| 10 |
from fastapi.middleware.cors import CORSMiddleware
|
| 11 |
from fastapi.responses import FileResponse, JSONResponse
|
| 12 |
from fastapi.staticfiles import StaticFiles
|
|
|
|
|
|
|
| 13 |
|
| 14 |
-
from my_ghost_writer
|
| 15 |
-
|
| 16 |
-
|
|
|
|
| 17 |
from my_ghost_writer.type_hints import RequestTextFrequencyBody, RequestQueryThesaurusWordsapiBody
|
| 18 |
|
| 19 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
fastapi_title = "My Ghost Writer"
|
| 21 |
-
app = FastAPI(title=fastapi_title, version="1.0")
|
| 22 |
app_logger.info(f"allowed_origins:{ALLOWED_ORIGIN_LIST}, IS_TESTING:{IS_TESTING}, LOG_LEVEL:{LOG_LEVEL}!")
|
| 23 |
app.add_middleware(
|
| 24 |
CORSMiddleware,
|
|
@@ -26,6 +50,7 @@ app.add_middleware(
|
|
| 26 |
allow_credentials=True,
|
| 27 |
allow_methods=["GET", "POST"]
|
| 28 |
)
|
|
|
|
| 29 |
|
| 30 |
|
| 31 |
@app.middleware("http")
|
|
@@ -40,10 +65,25 @@ def health():
|
|
| 40 |
from nltk import __version__ as nltk_version
|
| 41 |
from fastapi import __version__ as fastapi_version
|
| 42 |
from my_ghost_writer.__version__ import __version__ as ghost_writer_version
|
| 43 |
-
app_logger.info(
|
|
|
|
| 44 |
return "Still alive..."
|
| 45 |
|
| 46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
@app.post("/words-frequency")
|
| 48 |
def get_words_frequency(body: RequestTextFrequencyBody | str) -> JSONResponse:
|
| 49 |
from my_ghost_writer.text_parsers import text_stemming
|
|
@@ -73,42 +113,49 @@ def get_thesaurus_wordsapi(body: RequestQueryThesaurusWordsapiBody | str) -> JSO
|
|
| 73 |
app_logger.info(f"body type: {type(body)} => {body}.")
|
| 74 |
body_validated = RequestQueryThesaurusWordsapiBody.model_validate_json(body)
|
| 75 |
query = body_validated.query
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
duration = (t1 - t0).total_seconds()
|
| 80 |
-
app_logger.info(f"found local data, duration: {duration:.3f}s.")
|
| 81 |
-
return JSONResponse(status_code=200, content={"duration": duration, "thesaurus": response, "source": "local"})
|
| 82 |
-
except Exception as e:
|
| 83 |
-
app_logger.info(f"e:{e}, document not found?")
|
| 84 |
-
url = f"{WORDSAPI_URL}/{query}"
|
| 85 |
-
app_logger.info(f"url: {type(url)} => {url}.")
|
| 86 |
-
headers = {
|
| 87 |
-
"x-rapidapi-key": WORDSAPI_KEY,
|
| 88 |
-
"x-rapidapi-host": RAPIDAPI_HOST
|
| 89 |
-
}
|
| 90 |
-
response = requests.get(url, headers=headers)
|
| 91 |
-
t1 = datetime.now()
|
| 92 |
-
duration = (t1 - t0).total_seconds()
|
| 93 |
-
app_logger.info(f"response.status_code: {response.status_code}, duration: {duration:.3f}s.")
|
| 94 |
-
msg = f"API response is not 200: '{response.status_code}', query={query}, url={url}, duration: {duration:.3f}s."
|
| 95 |
try:
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
del response_json["_id"] # since we inserted the wordsapi response on mongodb now it have a bson _id object not serializable by default
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
|
|
|
| 112 |
|
| 113 |
|
| 114 |
@app.exception_handler(RequestValidationError)
|
|
@@ -128,7 +175,8 @@ def http_exception_handler(request: Request, exc: HTTPException) -> JSONResponse
|
|
| 128 |
try:
|
| 129 |
app.mount("/static", StaticFiles(directory=STATIC_FOLDER, html=True), name="static")
|
| 130 |
except Exception as ex_mount_static:
|
| 131 |
-
app_logger.error(
|
|
|
|
| 132 |
if not API_MODE:
|
| 133 |
app_logger.exception(f"since API_MODE is {API_MODE} we will raise the exception!")
|
| 134 |
raise ex_mount_static
|
|
@@ -136,7 +184,6 @@ except Exception as ex_mount_static:
|
|
| 136 |
# add the CorrelationIdMiddleware AFTER the @app.middleware("http") decorated function to avoid missing request id
|
| 137 |
app.add_middleware(CorrelationIdMiddleware)
|
| 138 |
|
| 139 |
-
|
| 140 |
try:
|
| 141 |
@app.get("/")
|
| 142 |
@app.get("/static/")
|
|
@@ -151,7 +198,8 @@ except Exception as ex_route_main:
|
|
| 151 |
|
| 152 |
if __name__ == "__main__":
|
| 153 |
try:
|
| 154 |
-
app_logger.info(
|
|
|
|
| 155 |
uvicorn.run("my_ghost_writer.app:app", host=DOMAIN, port=PORT, reload=bool(IS_TESTING))
|
| 156 |
except Exception as ex_run:
|
| 157 |
print(f"fastapi/gradio application {fastapi_title}, exception:{ex_run}!")
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
import json
|
| 3 |
from datetime import datetime
|
| 4 |
from http.client import HTTPException
|
|
|
|
| 11 |
from fastapi.middleware.cors import CORSMiddleware
|
| 12 |
from fastapi.responses import FileResponse, JSONResponse
|
| 13 |
from fastapi.staticfiles import StaticFiles
|
| 14 |
+
from pymongo import __version__ as pymongo_version
|
| 15 |
+
from pymongo.errors import PyMongoError
|
| 16 |
|
| 17 |
+
from my_ghost_writer import pymongo_operations_rw
|
| 18 |
+
from my_ghost_writer.constants import (app_logger, ALLOWED_ORIGIN_LIST, API_MODE, DOMAIN, IS_TESTING, LOG_LEVEL, PORT,
|
| 19 |
+
STATIC_FOLDER, WORDSAPI_KEY, WORDSAPI_URL, RAPIDAPI_HOST, MONGO_USE_OK, MONGO_HEALTHCHECK_SLEEP)
|
| 20 |
+
from my_ghost_writer.pymongo_utils import mongodb_health_check
|
| 21 |
from my_ghost_writer.type_hints import RequestTextFrequencyBody, RequestQueryThesaurusWordsapiBody
|
| 22 |
|
| 23 |
|
| 24 |
+
async def mongo_health_check_background_task():
|
| 25 |
+
app_logger.info(f"starting task, MONGO_USE_OK:{MONGO_USE_OK}...")
|
| 26 |
+
while MONGO_USE_OK:
|
| 27 |
+
try:
|
| 28 |
+
db_ok["mongo_ok"] = health_mongo() == "Mongodb: still alive..."
|
| 29 |
+
except PyMongoError:
|
| 30 |
+
db_ok["mongo_ok"] = False
|
| 31 |
+
await asyncio.sleep(MONGO_HEALTHCHECK_SLEEP)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
async def lifespan(app: FastAPI):
|
| 35 |
+
task = asyncio.create_task(mongo_health_check_background_task())
|
| 36 |
+
yield
|
| 37 |
+
task.cancel()
|
| 38 |
+
try:
|
| 39 |
+
await task
|
| 40 |
+
except asyncio.CancelledError:
|
| 41 |
+
pass
|
| 42 |
+
|
| 43 |
+
|
| 44 |
fastapi_title = "My Ghost Writer"
|
| 45 |
+
app = FastAPI(title=fastapi_title, version="1.0", lifespan=lifespan)
|
| 46 |
app_logger.info(f"allowed_origins:{ALLOWED_ORIGIN_LIST}, IS_TESTING:{IS_TESTING}, LOG_LEVEL:{LOG_LEVEL}!")
|
| 47 |
app.add_middleware(
|
| 48 |
CORSMiddleware,
|
|
|
|
| 50 |
allow_credentials=True,
|
| 51 |
allow_methods=["GET", "POST"]
|
| 52 |
)
|
| 53 |
+
db_ok = {"mongo_ok": MONGO_USE_OK}
|
| 54 |
|
| 55 |
|
| 56 |
@app.middleware("http")
|
|
|
|
| 65 |
from nltk import __version__ as nltk_version
|
| 66 |
from fastapi import __version__ as fastapi_version
|
| 67 |
from my_ghost_writer.__version__ import __version__ as ghost_writer_version
|
| 68 |
+
app_logger.info(
|
| 69 |
+
f"still alive... FastAPI version:{fastapi_version}, nltk version:{nltk_version}, my-ghost-writer version:{ghost_writer_version}!")
|
| 70 |
return "Still alive..."
|
| 71 |
|
| 72 |
|
| 73 |
+
@app.get("/health-mongo")
|
| 74 |
+
def health_mongo() -> str:
|
| 75 |
+
app_logger.info(f"pymongo driver version:{pymongo_version}!")
|
| 76 |
+
if MONGO_USE_OK:
|
| 77 |
+
try:
|
| 78 |
+
db_ok["mongo_ok"] = mongodb_health_check()
|
| 79 |
+
return "Mongodb: still alive..."
|
| 80 |
+
except PyMongoError as pme:
|
| 81 |
+
app_logger.error(str(pme))
|
| 82 |
+
db_ok["mongo_ok"] = False
|
| 83 |
+
raise HTTPException("mongo not ok!")
|
| 84 |
+
return f"MONGO_USE_OK:{MONGO_USE_OK}..."
|
| 85 |
+
|
| 86 |
+
|
| 87 |
@app.post("/words-frequency")
|
| 88 |
def get_words_frequency(body: RequestTextFrequencyBody | str) -> JSONResponse:
|
| 89 |
from my_ghost_writer.text_parsers import text_stemming
|
|
|
|
| 113 |
app_logger.info(f"body type: {type(body)} => {body}.")
|
| 114 |
body_validated = RequestQueryThesaurusWordsapiBody.model_validate_json(body)
|
| 115 |
query = body_validated.query
|
| 116 |
+
use_mongo: bool = db_ok["mongo_ok"]
|
| 117 |
+
app_logger.info(f"query: {type(query)} => {query}, use mongo? {use_mongo}.")
|
| 118 |
+
if use_mongo:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
try:
|
| 120 |
+
response = pymongo_operations_rw.get_document_by_word(query=query)
|
| 121 |
+
t1 = datetime.now()
|
| 122 |
+
duration = (t1 - t0).total_seconds()
|
| 123 |
+
app_logger.info(f"found local data, duration: {duration:.3f}s.")
|
| 124 |
+
return JSONResponse(status_code=200, content={"duration": duration, "thesaurus": response, "source": "local"})
|
| 125 |
+
except (PyMongoError, AssertionError) as pme:
|
| 126 |
+
app_logger.info(f"{pme}! Let's try the remote service...")
|
| 127 |
+
|
| 128 |
+
url = f"{WORDSAPI_URL}/{query}"
|
| 129 |
+
app_logger.info(f"url: {type(url)} => {url}.")
|
| 130 |
+
headers = {
|
| 131 |
+
"x-rapidapi-key": WORDSAPI_KEY,
|
| 132 |
+
"x-rapidapi-host": RAPIDAPI_HOST
|
| 133 |
+
}
|
| 134 |
+
response = requests.get(url, headers=headers)
|
| 135 |
+
t1 = datetime.now()
|
| 136 |
+
duration = (t1 - t0).total_seconds()
|
| 137 |
+
app_logger.info(f"response.status_code: {response.status_code}, duration: {duration:.3f}s.")
|
| 138 |
+
msg = f"API response is not 200: '{response.status_code}', query={query}, url={url}, duration: {duration:.3f}s."
|
| 139 |
+
try:
|
| 140 |
+
assert response.status_code == 200, msg
|
| 141 |
+
response_json = response.json()
|
| 142 |
+
if use_mongo:
|
| 143 |
+
app_logger.debug(f"use_mongo:{use_mongo}, inserting response '{response_json}' by query '{query}' on db...")
|
| 144 |
+
pymongo_operations_rw.insert_document(response_json)
|
| 145 |
del response_json["_id"] # since we inserted the wordsapi response on mongodb now it have a bson _id object not serializable by default
|
| 146 |
+
t2 = datetime.now()
|
| 147 |
+
duration = (t2 - t1).total_seconds()
|
| 148 |
+
app_logger.info(f"response_json: inserted json on local db, duration: {duration:.3f}s. ...")
|
| 149 |
+
return JSONResponse(status_code=200,
|
| 150 |
+
content={"duration": duration, "thesaurus": response_json, "source": "wordsapi"})
|
| 151 |
+
except AssertionError as ae:
|
| 152 |
+
app_logger.error(f"URL: query => {type(query)} {query}; url => {type(url)} {url}.")
|
| 153 |
+
app_logger.error(f"headers type: {type(headers)}...")
|
| 154 |
+
# app_logger.error(f"headers: {headers}...")
|
| 155 |
+
app_logger.error("response:")
|
| 156 |
+
app_logger.error(str(response))
|
| 157 |
+
app_logger.error(str(ae))
|
| 158 |
+
raise HTTPException(ae)
|
| 159 |
|
| 160 |
|
| 161 |
@app.exception_handler(RequestValidationError)
|
|
|
|
| 175 |
try:
|
| 176 |
app.mount("/static", StaticFiles(directory=STATIC_FOLDER, html=True), name="static")
|
| 177 |
except Exception as ex_mount_static:
|
| 178 |
+
app_logger.error(
|
| 179 |
+
f"Failed to mount static folder: {STATIC_FOLDER}, exception: {ex_mount_static}, API_MODE: {API_MODE}!")
|
| 180 |
if not API_MODE:
|
| 181 |
app_logger.exception(f"since API_MODE is {API_MODE} we will raise the exception!")
|
| 182 |
raise ex_mount_static
|
|
|
|
| 184 |
# add the CorrelationIdMiddleware AFTER the @app.middleware("http") decorated function to avoid missing request id
|
| 185 |
app.add_middleware(CorrelationIdMiddleware)
|
| 186 |
|
|
|
|
| 187 |
try:
|
| 188 |
@app.get("/")
|
| 189 |
@app.get("/static/")
|
|
|
|
| 198 |
|
| 199 |
if __name__ == "__main__":
|
| 200 |
try:
|
| 201 |
+
app_logger.info(
|
| 202 |
+
f"Starting fastapi/gradio application {fastapi_title}, run in api mode: {API_MODE} (no static folder and main route)...")
|
| 203 |
uvicorn.run("my_ghost_writer.app:app", host=DOMAIN, port=PORT, reload=bool(IS_TESTING))
|
| 204 |
except Exception as ex_run:
|
| 205 |
print(f"fastapi/gradio application {fastapi_title}, exception:{ex_run}!")
|
my_ghost_writer/constants.py
CHANGED
|
@@ -21,8 +21,17 @@ N_WORDS_GRAM = int(os.getenv("N_WORDS_GRAM", 2))
|
|
| 21 |
WORDSAPI_KEY = os.getenv("WORDSAPI_KEY")
|
| 22 |
WORDSAPI_URL = os.getenv("WORDSAPI_URL", "https://wordsapiv1.p.rapidapi.com/words")
|
| 23 |
RAPIDAPI_HOST = os.getenv("RAPIDAPI_HOST", "wordsapiv1.p.rapidapi.com")
|
| 24 |
-
|
| 25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
DEFAULT_COLLECTION_THESAURUS =os.getenv("DEFAULT_COLLECTION_THESAURUS", "wordsapi")
|
| 27 |
session_logger.setup_logging(json_logs=LOG_JSON_FORMAT, log_level=LOG_LEVEL)
|
| 28 |
app_logger = structlog.stdlib.get_logger(__name__)
|
|
|
|
| 21 |
WORDSAPI_KEY = os.getenv("WORDSAPI_KEY")
|
| 22 |
WORDSAPI_URL = os.getenv("WORDSAPI_URL", "https://wordsapiv1.p.rapidapi.com/words")
|
| 23 |
RAPIDAPI_HOST = os.getenv("RAPIDAPI_HOST", "wordsapiv1.p.rapidapi.com")
|
| 24 |
+
MONGO_USE_OK = bool(os.getenv("MONGO_USE_OK", ""))
|
| 25 |
+
MONGO_CONNECTION_STRING_LOCAL = "mongodb://localhost:27017"
|
| 26 |
+
MONGO_CONNECTION_STRING = os.getenv("MONGO_CONNECTION_STRING", MONGO_CONNECTION_STRING_LOCAL)
|
| 27 |
+
MONGO_CONNECTION_TIMEOUT_LOCAL = int(os.getenv("MONGO_CONNECTION_TIMEOUT_LOCAL", 200))
|
| 28 |
+
MONGO_CONNECTION_TIMEOUT_REMOTE = int(os.getenv("MONGO_CONNECTION_TIMEOUT_REMOTE", 3000))
|
| 29 |
+
MONGO_CONNECTION_TIMEOUT = int(os.getenv(
|
| 30 |
+
"MONGO_CONNECTION_TIMEOUT",
|
| 31 |
+
MONGO_CONNECTION_TIMEOUT_LOCAL if MONGO_CONNECTION_STRING == MONGO_CONNECTION_STRING_LOCAL else MONGO_CONNECTION_TIMEOUT_REMOTE
|
| 32 |
+
))
|
| 33 |
+
MONGO_HEALTHCHECK_SLEEP = int(os.getenv("MONGO_HEALTHCHECK_SLEEP", 900))
|
| 34 |
+
DEFAULT_DBNAME_THESAURUS = "thesaurus"
|
| 35 |
DEFAULT_COLLECTION_THESAURUS =os.getenv("DEFAULT_COLLECTION_THESAURUS", "wordsapi")
|
| 36 |
session_logger.setup_logging(json_logs=LOG_JSON_FORMAT, log_level=LOG_LEVEL)
|
| 37 |
app_logger = structlog.stdlib.get_logger(__name__)
|
my_ghost_writer/pymongo_get_database.py
DELETED
|
@@ -1,16 +0,0 @@
|
|
| 1 |
-
from pymongo import MongoClient
|
| 2 |
-
|
| 3 |
-
from my_ghost_writer.constants import MONGO_CONNECTION_STRING, DEFAULT_COLLECTION_THESAURUS, MONGO_CONNECTION_TIMEOUT
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
def get_database(db_name: str):
|
| 7 |
-
# Create a connection using MongoClient. You can import MongoClient or use pymongo.MongoClient
|
| 8 |
-
client = MongoClient(MONGO_CONNECTION_STRING, timeoutMS=MONGO_CONNECTION_TIMEOUT)
|
| 9 |
-
|
| 10 |
-
# Create the database for our example (we will use the same database throughout the tutorial
|
| 11 |
-
return client[db_name]
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
def get_thesaurus_collection(collection_name: str = DEFAULT_COLLECTION_THESAURUS):
|
| 15 |
-
dbname = get_database(db_name="thesaurus")
|
| 16 |
-
return dbname[collection_name]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
my_ghost_writer/{thesaurus.py → pymongo_operations_rw.py}
RENAMED
|
@@ -1,22 +1,20 @@
|
|
| 1 |
-
from datetime import time
|
| 2 |
import json
|
| 3 |
-
from
|
| 4 |
-
from bson import json_util
|
| 5 |
from my_ghost_writer.constants import app_logger
|
| 6 |
-
from my_ghost_writer.pymongo_get_database import get_thesaurus_collection
|
| 7 |
|
| 8 |
|
| 9 |
def get_document_by_word(query: str) -> dict:
|
| 10 |
-
collection = get_thesaurus_collection()
|
| 11 |
output: dict = collection.find_one({"word": query})
|
|
|
|
| 12 |
del output["_id"]
|
| 13 |
return output
|
| 14 |
|
| 15 |
|
| 16 |
def insert_document(document: dict) -> None:
|
| 17 |
-
collection = get_thesaurus_collection()
|
| 18 |
result = collection.insert_one(document)
|
| 19 |
-
|
| 20 |
try:
|
| 21 |
assert result.inserted_id
|
| 22 |
except AssertionError:
|
|
|
|
|
|
|
| 1 |
import json
|
| 2 |
+
from my_ghost_writer import pymongo_utils
|
|
|
|
| 3 |
from my_ghost_writer.constants import app_logger
|
|
|
|
| 4 |
|
| 5 |
|
| 6 |
def get_document_by_word(query: str) -> dict:
|
| 7 |
+
collection = pymongo_utils.get_thesaurus_collection()
|
| 8 |
output: dict = collection.find_one({"word": query})
|
| 9 |
+
assert output, f"not found document with query '{query}'..."
|
| 10 |
del output["_id"]
|
| 11 |
return output
|
| 12 |
|
| 13 |
|
| 14 |
def insert_document(document: dict) -> None:
|
| 15 |
+
collection = pymongo_utils.get_thesaurus_collection()
|
| 16 |
result = collection.insert_one(document)
|
| 17 |
+
app_logger.info(f"result:{result}.")
|
| 18 |
try:
|
| 19 |
assert result.inserted_id
|
| 20 |
except AssertionError:
|
my_ghost_writer/pymongo_utils.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pymongo import MongoClient
|
| 2 |
+
|
| 3 |
+
from my_ghost_writer.constants import DEFAULT_DBNAME_THESAURUS, MONGO_CONNECTION_STRING, DEFAULT_COLLECTION_THESAURUS, MONGO_CONNECTION_TIMEOUT, app_logger
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def get_client() -> MongoClient:
|
| 7 |
+
client = MongoClient(MONGO_CONNECTION_STRING, timeoutMS=MONGO_CONNECTION_TIMEOUT)
|
| 8 |
+
return client
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def get_database(db_name: str = DEFAULT_DBNAME_THESAURUS):
|
| 12 |
+
client = get_client()
|
| 13 |
+
return client[db_name]
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def get_thesaurus_collection(db_name: str = DEFAULT_DBNAME_THESAURUS, collection_name: str = DEFAULT_COLLECTION_THESAURUS):
|
| 17 |
+
dbname = get_database(db_name=db_name)
|
| 18 |
+
return dbname[collection_name]
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def mongodb_health_check(db_name: str = DEFAULT_DBNAME_THESAURUS, collection_name: str = DEFAULT_COLLECTION_THESAURUS) -> bool:
|
| 22 |
+
client = get_client()
|
| 23 |
+
# Check server is available
|
| 24 |
+
client.admin.command('ping', check=True)
|
| 25 |
+
server_info = client.server_info()
|
| 26 |
+
server_version = server_info["version"]
|
| 27 |
+
app_logger.info(f"mongodb server_version:{server_version}!")
|
| 28 |
+
# Try a simple find operation
|
| 29 |
+
db = client[db_name]
|
| 30 |
+
collection = db[collection_name]
|
| 31 |
+
collection.find_one()
|
| 32 |
+
app_logger.info("mongodb: still alive...")
|
| 33 |
+
return True
|
my_ghost_writer/type_hints.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
from pydantic import BaseModel
|
| 2 |
|
| 3 |
|
|
@@ -13,9 +14,6 @@ class RequestQueryThesaurusWordsapiBody(BaseModel):
|
|
| 13 |
query: str
|
| 14 |
|
| 15 |
|
| 16 |
-
from typing import TypedDict
|
| 17 |
-
|
| 18 |
-
|
| 19 |
class InputTextRow(TypedDict):
|
| 20 |
"""
|
| 21 |
TypedDict for input text row.
|
|
|
|
| 1 |
+
from typing import TypedDict
|
| 2 |
from pydantic import BaseModel
|
| 3 |
|
| 4 |
|
|
|
|
| 14 |
query: str
|
| 15 |
|
| 16 |
|
|
|
|
|
|
|
|
|
|
| 17 |
class InputTextRow(TypedDict):
|
| 18 |
"""
|
| 19 |
TypedDict for input text row.
|
poetry.lock
CHANGED
|
@@ -18,7 +18,7 @@ version = "4.9.0"
|
|
| 18 |
description = "High level compatibility layer for multiple asynchronous event loop implementations"
|
| 19 |
optional = false
|
| 20 |
python-versions = ">=3.9"
|
| 21 |
-
groups = ["webserver"]
|
| 22 |
files = [
|
| 23 |
{file = "anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c"},
|
| 24 |
{file = "anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028"},
|
|
@@ -54,6 +54,18 @@ starlette = ">=0.18"
|
|
| 54 |
[package.extras]
|
| 55 |
celery = ["celery"]
|
| 56 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
[[package]]
|
| 58 |
name = "click"
|
| 59 |
version = "8.1.8"
|
|
@@ -167,7 +179,7 @@ version = "2.7.0"
|
|
| 167 |
description = "DNS toolkit"
|
| 168 |
optional = false
|
| 169 |
python-versions = ">=3.9"
|
| 170 |
-
groups = ["
|
| 171 |
files = [
|
| 172 |
{file = "dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86"},
|
| 173 |
{file = "dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1"},
|
|
@@ -188,7 +200,7 @@ version = "1.2.2"
|
|
| 188 |
description = "Backport of PEP 654 (exception groups)"
|
| 189 |
optional = false
|
| 190 |
python-versions = ">=3.7"
|
| 191 |
-
groups = ["test", "webserver"]
|
| 192 |
markers = "python_version == \"3.10\""
|
| 193 |
files = [
|
| 194 |
{file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
|
|
@@ -225,19 +237,66 @@ version = "0.14.0"
|
|
| 225 |
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
|
| 226 |
optional = false
|
| 227 |
python-versions = ">=3.7"
|
| 228 |
-
groups = ["webserver"]
|
| 229 |
files = [
|
| 230 |
{file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
|
| 231 |
{file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
|
| 232 |
]
|
| 233 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 234 |
[[package]]
|
| 235 |
name = "idna"
|
| 236 |
version = "3.10"
|
| 237 |
description = "Internationalized Domain Names in Applications (IDNA)"
|
| 238 |
optional = false
|
| 239 |
python-versions = ">=3.6"
|
| 240 |
-
groups = ["webserver"]
|
| 241 |
files = [
|
| 242 |
{file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"},
|
| 243 |
{file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"},
|
|
@@ -464,7 +523,7 @@ version = "4.13.2"
|
|
| 464 |
description = "PyMongo - the Official MongoDB Python driver"
|
| 465 |
optional = false
|
| 466 |
python-versions = ">=3.9"
|
| 467 |
-
groups = ["
|
| 468 |
files = [
|
| 469 |
{file = "pymongo-4.13.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:01065eb1838e3621a30045ab14d1a60ee62e01f65b7cf154e69c5c722ef14d2f"},
|
| 470 |
{file = "pymongo-4.13.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9ab0325d436075f5f1901cde95afae811141d162bc42d9a5befb647fda585ae6"},
|
|
@@ -538,6 +597,21 @@ snappy = ["python-snappy"]
|
|
| 538 |
test = ["pytest (>=8.2)", "pytest-asyncio (>=0.24.0)"]
|
| 539 |
zstd = ["zstandard"]
|
| 540 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 541 |
[[package]]
|
| 542 |
name = "pytest"
|
| 543 |
version = "8.3.5"
|
|
@@ -705,7 +779,7 @@ version = "1.3.1"
|
|
| 705 |
description = "Sniff out which async library your code is running under"
|
| 706 |
optional = false
|
| 707 |
python-versions = ">=3.7"
|
| 708 |
-
groups = ["webserver"]
|
| 709 |
files = [
|
| 710 |
{file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
|
| 711 |
{file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
|
|
@@ -821,12 +895,12 @@ version = "4.13.2"
|
|
| 821 |
description = "Backported and Experimental Type Hints for Python 3.8+"
|
| 822 |
optional = false
|
| 823 |
python-versions = ">=3.8"
|
| 824 |
-
groups = ["main", "webserver"]
|
| 825 |
files = [
|
| 826 |
{file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"},
|
| 827 |
{file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"},
|
| 828 |
]
|
| 829 |
-
markers = {main = "python_version
|
| 830 |
|
| 831 |
[[package]]
|
| 832 |
name = "typing-inspection"
|
|
@@ -866,4 +940,4 @@ standard = ["colorama (>=0.4) ; sys_platform == \"win32\"", "httptools (>=0.6.3)
|
|
| 866 |
[metadata]
|
| 867 |
lock-version = "2.1"
|
| 868 |
python-versions = ">=3.10,<4.0.0"
|
| 869 |
-
content-hash = "
|
|
|
|
| 18 |
description = "High level compatibility layer for multiple asynchronous event loop implementations"
|
| 19 |
optional = false
|
| 20 |
python-versions = ">=3.9"
|
| 21 |
+
groups = ["main", "test", "webserver"]
|
| 22 |
files = [
|
| 23 |
{file = "anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c"},
|
| 24 |
{file = "anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028"},
|
|
|
|
| 54 |
[package.extras]
|
| 55 |
celery = ["celery"]
|
| 56 |
|
| 57 |
+
[[package]]
|
| 58 |
+
name = "certifi"
|
| 59 |
+
version = "2025.6.15"
|
| 60 |
+
description = "Python package for providing Mozilla's CA Bundle."
|
| 61 |
+
optional = false
|
| 62 |
+
python-versions = ">=3.7"
|
| 63 |
+
groups = ["main", "test"]
|
| 64 |
+
files = [
|
| 65 |
+
{file = "certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057"},
|
| 66 |
+
{file = "certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b"},
|
| 67 |
+
]
|
| 68 |
+
|
| 69 |
[[package]]
|
| 70 |
name = "click"
|
| 71 |
version = "8.1.8"
|
|
|
|
| 179 |
description = "DNS toolkit"
|
| 180 |
optional = false
|
| 181 |
python-versions = ">=3.9"
|
| 182 |
+
groups = ["test", "webserver"]
|
| 183 |
files = [
|
| 184 |
{file = "dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86"},
|
| 185 |
{file = "dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1"},
|
|
|
|
| 200 |
description = "Backport of PEP 654 (exception groups)"
|
| 201 |
optional = false
|
| 202 |
python-versions = ">=3.7"
|
| 203 |
+
groups = ["main", "test", "webserver"]
|
| 204 |
markers = "python_version == \"3.10\""
|
| 205 |
files = [
|
| 206 |
{file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
|
|
|
|
| 237 |
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
|
| 238 |
optional = false
|
| 239 |
python-versions = ">=3.7"
|
| 240 |
+
groups = ["main", "test", "webserver"]
|
| 241 |
files = [
|
| 242 |
{file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
|
| 243 |
{file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
|
| 244 |
]
|
| 245 |
|
| 246 |
+
[[package]]
|
| 247 |
+
name = "httpcore"
|
| 248 |
+
version = "1.0.8"
|
| 249 |
+
description = "A minimal low-level HTTP client."
|
| 250 |
+
optional = false
|
| 251 |
+
python-versions = ">=3.8"
|
| 252 |
+
groups = ["main", "test"]
|
| 253 |
+
files = [
|
| 254 |
+
{file = "httpcore-1.0.8-py3-none-any.whl", hash = "sha256:5254cf149bcb5f75e9d1b2b9f729ea4a4b883d1ad7379fc632b727cec23674be"},
|
| 255 |
+
{file = "httpcore-1.0.8.tar.gz", hash = "sha256:86e94505ed24ea06514883fd44d2bc02d90e77e7979c8eb71b90f41d364a1bad"},
|
| 256 |
+
]
|
| 257 |
+
|
| 258 |
+
[package.dependencies]
|
| 259 |
+
certifi = "*"
|
| 260 |
+
h11 = ">=0.13,<0.15"
|
| 261 |
+
|
| 262 |
+
[package.extras]
|
| 263 |
+
asyncio = ["anyio (>=4.0,<5.0)"]
|
| 264 |
+
http2 = ["h2 (>=3,<5)"]
|
| 265 |
+
socks = ["socksio (==1.*)"]
|
| 266 |
+
trio = ["trio (>=0.22.0,<1.0)"]
|
| 267 |
+
|
| 268 |
+
[[package]]
|
| 269 |
+
name = "httpx"
|
| 270 |
+
version = "0.28.1"
|
| 271 |
+
description = "The next generation HTTP client."
|
| 272 |
+
optional = false
|
| 273 |
+
python-versions = ">=3.8"
|
| 274 |
+
groups = ["main", "test"]
|
| 275 |
+
files = [
|
| 276 |
+
{file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"},
|
| 277 |
+
{file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"},
|
| 278 |
+
]
|
| 279 |
+
|
| 280 |
+
[package.dependencies]
|
| 281 |
+
anyio = "*"
|
| 282 |
+
certifi = "*"
|
| 283 |
+
httpcore = "==1.*"
|
| 284 |
+
idna = "*"
|
| 285 |
+
|
| 286 |
+
[package.extras]
|
| 287 |
+
brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""]
|
| 288 |
+
cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
|
| 289 |
+
http2 = ["h2 (>=3,<5)"]
|
| 290 |
+
socks = ["socksio (==1.*)"]
|
| 291 |
+
zstd = ["zstandard (>=0.18.0)"]
|
| 292 |
+
|
| 293 |
[[package]]
|
| 294 |
name = "idna"
|
| 295 |
version = "3.10"
|
| 296 |
description = "Internationalized Domain Names in Applications (IDNA)"
|
| 297 |
optional = false
|
| 298 |
python-versions = ">=3.6"
|
| 299 |
+
groups = ["main", "test", "webserver"]
|
| 300 |
files = [
|
| 301 |
{file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"},
|
| 302 |
{file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"},
|
|
|
|
| 523 |
description = "PyMongo - the Official MongoDB Python driver"
|
| 524 |
optional = false
|
| 525 |
python-versions = ">=3.9"
|
| 526 |
+
groups = ["test", "webserver"]
|
| 527 |
files = [
|
| 528 |
{file = "pymongo-4.13.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:01065eb1838e3621a30045ab14d1a60ee62e01f65b7cf154e69c5c722ef14d2f"},
|
| 529 |
{file = "pymongo-4.13.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9ab0325d436075f5f1901cde95afae811141d162bc42d9a5befb647fda585ae6"},
|
|
|
|
| 597 |
test = ["pytest (>=8.2)", "pytest-asyncio (>=0.24.0)"]
|
| 598 |
zstd = ["zstandard"]
|
| 599 |
|
| 600 |
+
[[package]]
|
| 601 |
+
name = "pymongo-inmemory"
|
| 602 |
+
version = "0.5.0"
|
| 603 |
+
description = "A mongo mocking library with an ephemeral MongoDB running in memory."
|
| 604 |
+
optional = false
|
| 605 |
+
python-versions = "<4.0,>=3.9"
|
| 606 |
+
groups = ["test"]
|
| 607 |
+
files = [
|
| 608 |
+
{file = "pymongo_inmemory-0.5.0-py3-none-any.whl", hash = "sha256:ebad4ccc9d9bed859ad25932f039aadb476f29c6945df57fdba0f9171f6626a1"},
|
| 609 |
+
{file = "pymongo_inmemory-0.5.0.tar.gz", hash = "sha256:2af2a6bab1cda9a27f524737ce6d3c9ff8cb9e52c224537e5742d610c4aa677e"},
|
| 610 |
+
]
|
| 611 |
+
|
| 612 |
+
[package.dependencies]
|
| 613 |
+
pymongo = "*"
|
| 614 |
+
|
| 615 |
[[package]]
|
| 616 |
name = "pytest"
|
| 617 |
version = "8.3.5"
|
|
|
|
| 779 |
description = "Sniff out which async library your code is running under"
|
| 780 |
optional = false
|
| 781 |
python-versions = ">=3.7"
|
| 782 |
+
groups = ["main", "test", "webserver"]
|
| 783 |
files = [
|
| 784 |
{file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
|
| 785 |
{file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
|
|
|
|
| 895 |
description = "Backported and Experimental Type Hints for Python 3.8+"
|
| 896 |
optional = false
|
| 897 |
python-versions = ">=3.8"
|
| 898 |
+
groups = ["main", "test", "webserver"]
|
| 899 |
files = [
|
| 900 |
{file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"},
|
| 901 |
{file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"},
|
| 902 |
]
|
| 903 |
+
markers = {main = "python_version < \"3.13\"", test = "python_version < \"3.13\""}
|
| 904 |
|
| 905 |
[[package]]
|
| 906 |
name = "typing-inspection"
|
|
|
|
| 940 |
[metadata]
|
| 941 |
lock-version = "2.1"
|
| 942 |
python-versions = ">=3.10,<4.0.0"
|
| 943 |
+
content-hash = "a31dd577fbe56afb94b935a7ec1feb8e146f5677cd9edb8869296d272afed9cc"
|
pyproject.toml
CHANGED
|
@@ -12,23 +12,26 @@ dependencies = [
|
|
| 12 |
"nltk (>=3.9.1,<4.0.0)",
|
| 13 |
"python-dotenv (>=1.1.0,<2.0.0)",
|
| 14 |
"structlog (>=25.2.0,<26.0.0)",
|
| 15 |
-
"
|
| 16 |
]
|
| 17 |
|
| 18 |
[tool.poetry.group.test]
|
| 19 |
optional = true
|
| 20 |
|
| 21 |
[tool.poetry.group.test.dependencies]
|
|
|
|
| 22 |
pytest = "^8.3.5"
|
| 23 |
pytest-cov = "^6.1.1"
|
|
|
|
| 24 |
|
| 25 |
[tool.poetry.group.webserver]
|
| 26 |
optional = true
|
| 27 |
|
| 28 |
[tool.poetry.group.webserver.dependencies]
|
|
|
|
| 29 |
fastapi = "^0.115.12"
|
| 30 |
uvicorn = "^0.34.2"
|
| 31 |
-
|
| 32 |
|
| 33 |
[tool.pytest.ini_options]
|
| 34 |
addopts = "--cov=my_ghost_writer --cov-report html"
|
|
|
|
| 12 |
"nltk (>=3.9.1,<4.0.0)",
|
| 13 |
"python-dotenv (>=1.1.0,<2.0.0)",
|
| 14 |
"structlog (>=25.2.0,<26.0.0)",
|
| 15 |
+
"httpx (>=0.28.1,<0.29.0)"
|
| 16 |
]
|
| 17 |
|
| 18 |
[tool.poetry.group.test]
|
| 19 |
optional = true
|
| 20 |
|
| 21 |
[tool.poetry.group.test.dependencies]
|
| 22 |
+
pymongo-inmemory = "^0.5.0"
|
| 23 |
pytest = "^8.3.5"
|
| 24 |
pytest-cov = "^6.1.1"
|
| 25 |
+
httpx = "^0.28.1"
|
| 26 |
|
| 27 |
[tool.poetry.group.webserver]
|
| 28 |
optional = true
|
| 29 |
|
| 30 |
[tool.poetry.group.webserver.dependencies]
|
| 31 |
+
asgi-correlation-id = "^4.3.4"
|
| 32 |
fastapi = "^0.115.12"
|
| 33 |
uvicorn = "^0.34.2"
|
| 34 |
+
pymongo = {extras = ["srv"], version = "^4.13.2"}
|
| 35 |
|
| 36 |
[tool.pytest.ini_options]
|
| 37 |
addopts = "--cov=my_ghost_writer --cov-report html"
|
requirements.txt
CHANGED
|
@@ -1,3 +1,4 @@
|
|
| 1 |
nltk==3.9.1
|
|
|
|
| 2 |
python-dotenv==1.1.0
|
| 3 |
-
structlog==25.2.0
|
|
|
|
| 1 |
nltk==3.9.1
|
| 2 |
+
pymongo~=4.13.2
|
| 3 |
python-dotenv==1.1.0
|
| 4 |
+
structlog==25.2.0
|
template.env
CHANGED
|
@@ -4,6 +4,11 @@ ALLOWED_ORIGIN=http://localhost:7860,http://localhost:8000
|
|
| 4 |
WORDSAPI_KEY=
|
| 5 |
WORDSAPI_URL=https://wordsapiv1.p.rapidapi.com/words
|
| 6 |
RAPIDAPI_HOST=wordsapiv1.p.rapidapi.com
|
|
|
|
|
|
|
| 7 |
MONGO_CONNECTION_STRING=mongodb://localhost:27017
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
| 9 |
DEFAULT_COLLECTION_THESAURUS=wordsapi
|
|
|
|
| 4 |
WORDSAPI_KEY=
|
| 5 |
WORDSAPI_URL=https://wordsapiv1.p.rapidapi.com/words
|
| 6 |
RAPIDAPI_HOST=wordsapiv1.p.rapidapi.com
|
| 7 |
+
MONGO_USE_OK=
|
| 8 |
+
MONGO_CONNECTION_STRING_LOCAL=mongodb://localhost:27017
|
| 9 |
MONGO_CONNECTION_STRING=mongodb://localhost:27017
|
| 10 |
+
MONGO_CONNECTION_TIMEOUT_LOCAL=200
|
| 11 |
+
MONGO_CONNECTION_TIMEOUT_REMOTE=3000
|
| 12 |
+
MONGO_CONNECTION_TIMEOUT=
|
| 13 |
+
MONGO_HEALTHCHECK_SLEEP=900
|
| 14 |
DEFAULT_COLLECTION_THESAURUS=wordsapi
|