Spaces:
Running
Running
Update main.py
Browse files
main.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
|
| 2 |
|
| 3 |
import json
|
| 4 |
import os
|
|
@@ -92,13 +92,16 @@ def clamp_page(value: Any) -> int:
|
|
| 92 |
return page if page > 0 else 1
|
| 93 |
|
| 94 |
|
| 95 |
-
def build_list_query(status: str, q: str, page: int) -> str:
|
| 96 |
-
params = {
|
| 97 |
-
"status": status,
|
| 98 |
-
"page": page,
|
| 99 |
-
}
|
| 100 |
if q:
|
| 101 |
params["q"] = q
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
return urlencode(params)
|
| 103 |
|
| 104 |
|
|
@@ -303,40 +306,61 @@ def conversation_summary(conversation: dict[str, Any]) -> dict[str, Any]:
|
|
| 303 |
}
|
| 304 |
|
| 305 |
|
|
|
|
|
|
|
|
|
|
| 306 |
def sort_conversations_for_queue(
|
| 307 |
conversations: list[dict[str, Any]],
|
| 308 |
status: str,
|
| 309 |
query: str,
|
|
|
|
| 310 |
) -> list[dict[str, Any]]:
|
| 311 |
-
|
| 312 |
-
|
|
|
|
| 313 |
|
| 314 |
-
if
|
| 315 |
-
return sorted(
|
| 316 |
-
conversations,
|
| 317 |
-
key=lambda c: to_dt(c.get("created_at") or c.get("updated_at")),
|
| 318 |
-
reverse=True,
|
| 319 |
-
)
|
| 320 |
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
return (
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
|
|
|
|
|
|
| 330 |
|
| 331 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 332 |
|
|
|
|
|
|
|
| 333 |
|
| 334 |
-
|
|
|
|
| 335 |
status = clean_text(status).lower() or "unanswered"
|
| 336 |
if status not in {"unanswered", "answered", "all"}:
|
| 337 |
status = "unanswered"
|
| 338 |
q = clean_text(q)
|
| 339 |
page = clamp_page(page)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 340 |
|
| 341 |
conversations = [normalize_conversation(c) for c in load_conversations()]
|
| 342 |
|
|
@@ -348,7 +372,12 @@ def list_questions(status: str = "unanswered", q: str = "", page: int = 1) -> di
|
|
| 348 |
if q:
|
| 349 |
conversations = [c for c in conversations if matches_query(c, q)]
|
| 350 |
|
| 351 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 352 |
total = len(conversations)
|
| 353 |
start = (page - 1) * PAGE_SIZE
|
| 354 |
end = start + PAGE_SIZE
|
|
@@ -363,6 +392,9 @@ def list_questions(status: str = "unanswered", q: str = "", page: int = 1) -> di
|
|
| 363 |
"has_next": end < total,
|
| 364 |
"status": status,
|
| 365 |
"q": q,
|
|
|
|
|
|
|
|
|
|
| 366 |
}
|
| 367 |
|
| 368 |
|
|
@@ -535,8 +567,17 @@ def home(request: Request):
|
|
| 535 |
q = clean_text(request.query_params.get("q"))
|
| 536 |
page = clamp_page(request.query_params.get("page", 1))
|
| 537 |
conversation_id = clean_text(request.query_params.get("conversation_id"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 538 |
|
| 539 |
-
list_state = list_questions(status=status, q=q, page=page)
|
| 540 |
detail = load_conversation(conversation_id) if conversation_id else None
|
| 541 |
|
| 542 |
init = {
|
|
@@ -548,11 +589,14 @@ def home(request: Request):
|
|
| 548 |
"q": list_state["q"],
|
| 549 |
"page": list_state["page"],
|
| 550 |
"page_size": list_state["page_size"],
|
|
|
|
|
|
|
|
|
|
| 551 |
},
|
| 552 |
"list": list_state,
|
| 553 |
"detail": detail,
|
| 554 |
"conversation_id": conversation_id,
|
| 555 |
-
"back_query": build_list_query(list_state["status"], list_state["q"], list_state["page"]),
|
| 556 |
}
|
| 557 |
return templates.TemplateResponse(
|
| 558 |
request,
|
|
@@ -593,11 +637,14 @@ async def api(request: Request):
|
|
| 593 |
q = clean_text(payload.get("q"))
|
| 594 |
page = clamp_page(payload.get("page", 1))
|
| 595 |
conversation_id = clean_text(payload.get("conversation_id"))
|
|
|
|
|
|
|
|
|
|
| 596 |
return JSONResponse(
|
| 597 |
{
|
| 598 |
"ok": True,
|
| 599 |
"client_id": client_id,
|
| 600 |
-
"list": list_questions(status=status, q=q, page=page),
|
| 601 |
"detail": load_conversation(conversation_id) if conversation_id else None,
|
| 602 |
}
|
| 603 |
)
|
|
@@ -606,7 +653,10 @@ async def api(request: Request):
|
|
| 606 |
status = clean_text(payload.get("status")).lower() or "unanswered"
|
| 607 |
q = clean_text(payload.get("q"))
|
| 608 |
page = clamp_page(payload.get("page", 1))
|
| 609 |
-
|
|
|
|
|
|
|
|
|
|
| 610 |
|
| 611 |
if action in {"get_question_detail", "get_conversation"}:
|
| 612 |
conversation_id = clean_text(payload.get("conversation_id"))
|
|
@@ -670,4 +720,4 @@ async def api(request: Request):
|
|
| 670 |
if __name__ == "__main__":
|
| 671 |
import uvicorn
|
| 672 |
|
| 673 |
-
uvicorn.run("main:app", host="0.0.0.0", port=7860, reload=False)
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
|
| 3 |
import json
|
| 4 |
import os
|
|
|
|
| 92 |
return page if page > 0 else 1
|
| 93 |
|
| 94 |
|
| 95 |
+
def build_list_query(status: str, q: str, page: int, sort: str = "newest", min_words: int = 0, max_words: int = 0) -> str:
|
| 96 |
+
params: dict = {"status": status, "page": page}
|
|
|
|
|
|
|
|
|
|
| 97 |
if q:
|
| 98 |
params["q"] = q
|
| 99 |
+
if sort and sort != "newest":
|
| 100 |
+
params["sort"] = sort
|
| 101 |
+
if min_words > 0:
|
| 102 |
+
params["min_words"] = min_words
|
| 103 |
+
if max_words > 0:
|
| 104 |
+
params["max_words"] = max_words
|
| 105 |
return urlencode(params)
|
| 106 |
|
| 107 |
|
|
|
|
| 306 |
}
|
| 307 |
|
| 308 |
|
| 309 |
+
VALID_SORTS = {"newest", "oldest", "most_votes", "most_answers", "longest_answer", "shortest_answer"}
|
| 310 |
+
|
| 311 |
+
|
| 312 |
def sort_conversations_for_queue(
|
| 313 |
conversations: list[dict[str, Any]],
|
| 314 |
status: str,
|
| 315 |
query: str,
|
| 316 |
+
sort: str = "newest",
|
| 317 |
) -> list[dict[str, Any]]:
|
| 318 |
+
sort = clean_text(sort).lower()
|
| 319 |
+
if sort not in VALID_SORTS:
|
| 320 |
+
sort = "newest"
|
| 321 |
|
| 322 |
+
if sort == "oldest":
|
| 323 |
+
return sorted(conversations, key=lambda c: to_dt(c.get("created_at") or c.get("updated_at")))
|
|
|
|
|
|
|
|
|
|
|
|
|
| 324 |
|
| 325 |
+
if sort == "most_votes":
|
| 326 |
+
def vote_key(c: dict[str, Any]) -> int:
|
| 327 |
+
best = best_answer_payload(c)
|
| 328 |
+
return int(best.get("votes", 0)) if best else 0
|
| 329 |
+
return sorted(conversations, key=vote_key, reverse=True)
|
| 330 |
+
|
| 331 |
+
if sort == "most_answers":
|
| 332 |
+
return sorted(conversations, key=lambda c: len(c.get("answers", [])), reverse=True)
|
| 333 |
+
|
| 334 |
+
if sort == "longest_answer":
|
| 335 |
+
return sorted(conversations, key=answer_text_length, reverse=True)
|
| 336 |
|
| 337 |
+
if sort == "shortest_answer":
|
| 338 |
+
def short_key(c: dict[str, Any]) -> tuple:
|
| 339 |
+
length = answer_text_length(c)
|
| 340 |
+
return (0 if length == 0 else 1, length)
|
| 341 |
+
return sorted(conversations, key=short_key)
|
| 342 |
|
| 343 |
+
# newest (default)
|
| 344 |
+
return sorted(conversations, key=lambda c: to_dt(c.get("created_at") or c.get("updated_at")), reverse=True)
|
| 345 |
|
| 346 |
+
|
| 347 |
+
def list_questions(status: str = "unanswered", q: str = "", page: int = 1, sort: str = "newest", min_words: int = 0, max_words: int = 0) -> dict[str, Any]:
|
| 348 |
status = clean_text(status).lower() or "unanswered"
|
| 349 |
if status not in {"unanswered", "answered", "all"}:
|
| 350 |
status = "unanswered"
|
| 351 |
q = clean_text(q)
|
| 352 |
page = clamp_page(page)
|
| 353 |
+
sort = clean_text(sort).lower()
|
| 354 |
+
if sort not in VALID_SORTS:
|
| 355 |
+
sort = "newest"
|
| 356 |
+
try:
|
| 357 |
+
min_words = max(0, int(min_words))
|
| 358 |
+
except Exception:
|
| 359 |
+
min_words = 0
|
| 360 |
+
try:
|
| 361 |
+
max_words = max(0, int(max_words))
|
| 362 |
+
except Exception:
|
| 363 |
+
max_words = 0
|
| 364 |
|
| 365 |
conversations = [normalize_conversation(c) for c in load_conversations()]
|
| 366 |
|
|
|
|
| 372 |
if q:
|
| 373 |
conversations = [c for c in conversations if matches_query(c, q)]
|
| 374 |
|
| 375 |
+
if min_words > 0:
|
| 376 |
+
conversations = [c for c in conversations if len(clean_text(c.get("question")).split()) >= min_words]
|
| 377 |
+
if max_words > 0:
|
| 378 |
+
conversations = [c for c in conversations if len(clean_text(c.get("question")).split()) <= max_words]
|
| 379 |
+
|
| 380 |
+
conversations = sort_conversations_for_queue(conversations, status=status, query=q, sort=sort)
|
| 381 |
total = len(conversations)
|
| 382 |
start = (page - 1) * PAGE_SIZE
|
| 383 |
end = start + PAGE_SIZE
|
|
|
|
| 392 |
"has_next": end < total,
|
| 393 |
"status": status,
|
| 394 |
"q": q,
|
| 395 |
+
"sort": sort,
|
| 396 |
+
"min_words": min_words,
|
| 397 |
+
"max_words": max_words,
|
| 398 |
}
|
| 399 |
|
| 400 |
|
|
|
|
| 567 |
q = clean_text(request.query_params.get("q"))
|
| 568 |
page = clamp_page(request.query_params.get("page", 1))
|
| 569 |
conversation_id = clean_text(request.query_params.get("conversation_id"))
|
| 570 |
+
sort = clean_text(request.query_params.get("sort")) or "newest"
|
| 571 |
+
try:
|
| 572 |
+
min_words = max(0, int(request.query_params.get("min_words", 0)))
|
| 573 |
+
except Exception:
|
| 574 |
+
min_words = 0
|
| 575 |
+
try:
|
| 576 |
+
max_words = max(0, int(request.query_params.get("max_words", 0)))
|
| 577 |
+
except Exception:
|
| 578 |
+
max_words = 0
|
| 579 |
|
| 580 |
+
list_state = list_questions(status=status, q=q, page=page, sort=sort, min_words=min_words, max_words=max_words)
|
| 581 |
detail = load_conversation(conversation_id) if conversation_id else None
|
| 582 |
|
| 583 |
init = {
|
|
|
|
| 589 |
"q": list_state["q"],
|
| 590 |
"page": list_state["page"],
|
| 591 |
"page_size": list_state["page_size"],
|
| 592 |
+
"sort": list_state["sort"],
|
| 593 |
+
"min_words": list_state["min_words"],
|
| 594 |
+
"max_words": list_state["max_words"],
|
| 595 |
},
|
| 596 |
"list": list_state,
|
| 597 |
"detail": detail,
|
| 598 |
"conversation_id": conversation_id,
|
| 599 |
+
"back_query": build_list_query(list_state["status"], list_state["q"], list_state["page"], list_state["sort"], list_state["min_words"], list_state["max_words"]),
|
| 600 |
}
|
| 601 |
return templates.TemplateResponse(
|
| 602 |
request,
|
|
|
|
| 637 |
q = clean_text(payload.get("q"))
|
| 638 |
page = clamp_page(payload.get("page", 1))
|
| 639 |
conversation_id = clean_text(payload.get("conversation_id"))
|
| 640 |
+
sort = clean_text(payload.get("sort")) or "newest"
|
| 641 |
+
min_words = int(payload.get("min_words", 0) or 0)
|
| 642 |
+
max_words = int(payload.get("max_words", 0) or 0)
|
| 643 |
return JSONResponse(
|
| 644 |
{
|
| 645 |
"ok": True,
|
| 646 |
"client_id": client_id,
|
| 647 |
+
"list": list_questions(status=status, q=q, page=page, sort=sort, min_words=min_words, max_words=max_words),
|
| 648 |
"detail": load_conversation(conversation_id) if conversation_id else None,
|
| 649 |
}
|
| 650 |
)
|
|
|
|
| 653 |
status = clean_text(payload.get("status")).lower() or "unanswered"
|
| 654 |
q = clean_text(payload.get("q"))
|
| 655 |
page = clamp_page(payload.get("page", 1))
|
| 656 |
+
sort = clean_text(payload.get("sort")) or "newest"
|
| 657 |
+
min_words = int(payload.get("min_words", 0) or 0)
|
| 658 |
+
max_words = int(payload.get("max_words", 0) or 0)
|
| 659 |
+
return JSONResponse({"ok": True, **list_questions(status=status, q=q, page=page, sort=sort, min_words=min_words, max_words=max_words)})
|
| 660 |
|
| 661 |
if action in {"get_question_detail", "get_conversation"}:
|
| 662 |
conversation_id = clean_text(payload.get("conversation_id"))
|
|
|
|
| 720 |
if __name__ == "__main__":
|
| 721 |
import uvicorn
|
| 722 |
|
| 723 |
+
uvicorn.run("main:app", host="0.0.0.0", port=7860, reload=False)
|