stage partial
This commit is contained in:
@@ -252,6 +252,10 @@ def match_author(authors: List[Dict[str, Any]], q: str) -> bool:
|
||||
|
||||
_docid_map = {}
|
||||
|
||||
def clear_doc_cache():
|
||||
global _docid_map
|
||||
_docid_map = {}
|
||||
|
||||
def get_doc_by_id(root: str, identifier: str) -> Tuple[Optional[Dict[str, Any]], Optional[str], Optional[str]]:
|
||||
global _docid_map
|
||||
if not _docid_map:
|
||||
@@ -379,6 +383,7 @@ def check_linked_sections(content: str, root: str = 'docs_md/articles') -> Dict[
|
||||
|
||||
|
||||
def run_search(root: str, qkey: str, qval: str, mode: str = 'exact', targets: List[str] = None, or_queries: List[Dict[str, Any]] = None, expand_links: bool = False) -> List[Dict[str, Any]]:
|
||||
clear_doc_cache()
|
||||
out = []
|
||||
|
||||
# Normalize queries list
|
||||
|
||||
@@ -290,6 +290,37 @@ def search_page() -> None: # build UI
|
||||
|
||||
# Render initial query blocks
|
||||
render_query_blocks()
|
||||
|
||||
# Persistent Search History Container
|
||||
with ui.row().classes('w-full items-center gap-2 mt-2'):
|
||||
ui.label('Recent Searches:').classes('text-slate-400 text-xs font-semibold')
|
||||
history_container = ui.row().classes('gap-1 items-center')
|
||||
|
||||
def render_history_chips():
|
||||
history_container.clear()
|
||||
history = load_history()
|
||||
if not history:
|
||||
with history_container:
|
||||
ui.label('None').classes('text-slate-500 text-xs italic')
|
||||
return
|
||||
with history_container:
|
||||
for item in history[:8]:
|
||||
def make_click(saved=item['queries']):
|
||||
def handler():
|
||||
nonlocal queries
|
||||
queries.clear()
|
||||
for sq in saved:
|
||||
queries.append(json.loads(json.dumps(sq)))
|
||||
render_query_blocks()
|
||||
ui.notify("Search query restored!", type='info')
|
||||
return handler
|
||||
ui.chip(
|
||||
item['label'],
|
||||
clickable=True,
|
||||
on_click=make_click()
|
||||
).props('dense color="slate-800" text-color="blue-300" icon="history"').classes('text-xs hover:bg-slate-700')
|
||||
|
||||
render_history_chips()
|
||||
|
||||
with ui.row().classes('w-full justify-between items-center gap-4 mt-2'):
|
||||
with ui.row().classes('items-center gap-4'):
|
||||
@@ -424,6 +455,61 @@ def search_page() -> None: # build UI
|
||||
table.on('show_links_modal', lambda msg: show_links_modal_handler(msg.args))
|
||||
table.on('capture_article', lambda msg: asyncio.create_task(trigger_capture_handler(msg.args)))
|
||||
|
||||
async def wait_and_process_capture(docid: str, title: str):
|
||||
import glob
|
||||
processed = False
|
||||
for i in range(30):
|
||||
await asyncio.sleep(1.5)
|
||||
|
||||
# Check if JSON files matching the docid exist in input dir
|
||||
json_files = glob.glob(f"xhr_captured_async/*{docid}*.json")
|
||||
if json_files and not processed:
|
||||
with table.client:
|
||||
ui.notify(f"Captured data found for: {title}. Processing to Markdown...", type='info')
|
||||
try:
|
||||
await anyio.to_thread.run_sync(
|
||||
document_to_markdown.main,
|
||||
['--input-dir', 'xhr_captured_async', '--output-dir', 'docs_md']
|
||||
)
|
||||
processed = True
|
||||
except Exception as e:
|
||||
print(f"Error running document_to_markdown: {e}")
|
||||
|
||||
# Check if it has been successfully converted/cached
|
||||
def check_cache():
|
||||
search_md.clear_doc_cache()
|
||||
return search_md.get_doc_by_id('docs_md/articles', docid)
|
||||
fm, content, doc_path = await anyio.to_thread.run_sync(check_cache)
|
||||
|
||||
if fm:
|
||||
# Successfully processed! Reload row data in table!
|
||||
with table.client:
|
||||
row_index = -1
|
||||
for idx, r in enumerate(table.rows):
|
||||
if r.get('docid') == docid:
|
||||
row_index = idx
|
||||
break
|
||||
if row_index != -1:
|
||||
linked_fm_title = fm.get('title') or fm.get('pageTitle') or title
|
||||
updated_row = {
|
||||
'path': doc_path,
|
||||
'title': linked_fm_title,
|
||||
'docid': docid,
|
||||
'breadcrumbs': fm.get('breadcrumbs'),
|
||||
'authors': fm.get('authors'),
|
||||
'pageKeywords': fm.get('pageKeywords'),
|
||||
'reasons': table.rows[row_index].get('reasons', []),
|
||||
'snippet': content[:300] + '...' if content else '',
|
||||
'linked_info': search_md.check_linked_sections(content, root_input.value),
|
||||
'is_cached': True
|
||||
}
|
||||
table.rows[row_index] = updated_row
|
||||
table.rows = list(table.rows)
|
||||
ui.notify(f"Successfully cached and loaded: {linked_fm_title}!", type='positive')
|
||||
else:
|
||||
ui.notify(f"Cached document: {title}", type='positive')
|
||||
return
|
||||
|
||||
async def trigger_capture_handler(args: dict):
|
||||
docid = args.get('docid')
|
||||
title = args.get('title')
|
||||
@@ -439,11 +525,15 @@ def search_page() -> None: # build UI
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def start_capture_poll_task(d_id, t_title):
|
||||
asyncio.create_task(wait_and_process_capture(d_id, t_title))
|
||||
|
||||
with table.client:
|
||||
ui.notify(f"Checking capture browser status for: {title}...", type='info')
|
||||
ok = await asyncio.to_thread(check_and_send)
|
||||
if ok:
|
||||
ui.notify(f"Requested capture browser to open: {title}", type='positive')
|
||||
start_capture_poll_task(docid, title)
|
||||
return
|
||||
|
||||
ui.notify("Capture browser not responding. Cleaning up existing capture processes...", type='warning')
|
||||
@@ -465,6 +555,7 @@ def search_page() -> None: # build UI
|
||||
ok_again = await asyncio.to_thread(check_and_send)
|
||||
if ok_again:
|
||||
ui.notify(f"Capture browser launched successfully. Opened: {title}", type='positive')
|
||||
start_capture_poll_task(docid, title)
|
||||
else:
|
||||
ui.notify("Capture browser process launched, but command port timed out. Try clicking again in a few seconds.", type='warning')
|
||||
except Exception as e:
|
||||
@@ -738,6 +829,20 @@ def search_page() -> None: # build UI
|
||||
status.set_text('Running search...')
|
||||
search_btn.set_enabled(False)
|
||||
|
||||
# Save query configuration to persistent history
|
||||
active_queries = [q for q in queries if q['qval'].strip()]
|
||||
if active_queries:
|
||||
history_label = " OR ".join([f"'{q['qval']}' ({q['mode']})" for q in active_queries])
|
||||
history_item = {
|
||||
'label': history_label,
|
||||
'queries': json.loads(json.dumps(queries))
|
||||
}
|
||||
history = load_history()
|
||||
history = [h for h in history if h['label'] != history_label]
|
||||
history.insert(0, history_item)
|
||||
save_history(history[:20])
|
||||
render_history_chips()
|
||||
|
||||
try:
|
||||
or_queries_param = []
|
||||
for q in queries:
|
||||
@@ -984,6 +1089,25 @@ def linkify_references(content: str, root: str = 'docs_md/articles') -> str:
|
||||
return content
|
||||
|
||||
|
||||
HISTORY_FILE = 'search_history.json'
|
||||
|
||||
def load_history() -> list:
|
||||
try:
|
||||
if os.path.exists(HISTORY_FILE):
|
||||
with open(HISTORY_FILE, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
except Exception:
|
||||
pass
|
||||
return []
|
||||
|
||||
def save_history(history_list: list):
|
||||
try:
|
||||
with open(HISTORY_FILE, 'w', encoding='utf-8') as f:
|
||||
json.dump(history_list, f, indent=2, ensure_ascii=False)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def is_doc_cached(docid: str) -> bool:
|
||||
fm, _, _ = search_md.get_doc_by_id('docs_md/articles', docid)
|
||||
return fm is not None
|
||||
|
||||
Reference in New Issue
Block a user