From 788e2409df8171ccf3905f03123cf42524268d2a Mon Sep 17 00:00:00 2001 From: beabigegg Date: Sun, 17 Aug 2025 18:34:38 +0800 Subject: [PATCH] fix6 --- __pycache__/api_routes.cpython-312.pyc | Bin 24277 -> 24408 bytes __pycache__/tasks.cpython-312.pyc | Bin 22411 -> 21906 bytes api_routes.py | 11 ++++--- frontend/src/pages/MeetingDetailPage.jsx | 12 ++++++-- .../__pycache__/dify_client.cpython-312.pyc | Bin 4163 -> 4539 bytes services/dify_client.py | 13 +++++--- tasks.py | 29 ++++++------------ 7 files changed, 35 insertions(+), 30 deletions(-) diff --git a/__pycache__/api_routes.cpython-312.pyc b/__pycache__/api_routes.cpython-312.pyc index ea9fd4ce65f7ce25f57db7fb1f1d494e413cc2fa..2e8c7341bb7773c2ff97c17e46052f023034aaf7 100644 GIT binary patch delta 408 zcmcb*m+{6vM!wU$yj%=GP`Y+u#w_cNe3gcbS0)FzyKFvVD8bCQWOBIC{LNaXv5ZVr zwv#o?G(}QUb5cuE6-qKPixqNHQ%f@Q()Bj?n5i%_zSz9cJdn{>O6#(eP6z)TVX4WM z(`_f(E)csctg*uVqOfrX$3uScInE2%uPc~dR4~0PW41?mNB(6ize_THK;<)BFDNHn zkWRiVoN^&8^MY{Z1)j{!T9)=qj87(KS?^-{#4tI~-Dz?|fEY_HQw`H(AuIdI7qmF} zYMDzoK|TP18s^CjRw67ZEMSH)zp5|9i3%Bs#R_@(lOt_)csYs$fGUgZK}7fD>9+A~ zZ-LC>g30;TGRneI*M(It3ad8w+~gL&!!I_2{eq&$1#!>I{9Y%VFYx=5QhIjw~ delta 421 zcmcbykMZhWM!wU$yj%=G@U44c#w)9he3gbwRVjID0=Es)yOl%Kn?zEGc>|!&MiH{LzRRn7VV=)io-A7aQ)55D6-kb-GiM`YICoj3oE1J=1aj% y%#7iiL&Fy^GA2y^7V(cUZS&ViS7uRVMzzn(3`_zb!jJI-3y}Z8YqNhWCldhAw}emt diff --git a/__pycache__/tasks.cpython-312.pyc b/__pycache__/tasks.cpython-312.pyc index 6dfca8226e242f2065619238eb56b3a9a01877f6..9c6deca873de98be4c3ced487f005aaed36047b8 100644 GIT binary patch delta 924 zcmYjNT}V?=96$Ho-FH+?~%|5@m**ahJil?=j^a>xKsM0iF!g}Z!jr!~-4I(8n1N0!1WGJ8m2~^uq zrcsHks7^7czu*X3KHE+^_S|L`yY(rClm&nSVnG;)+$4=goTWSs5==ssqVmBcyO&`i z8HEQk60FFfAB`4Uab5_36Trcw8czWR^hr%Z(`QChK}lIrLOlVV(xPG3n4 zqRA-oifunTp-xe#=$w^P?EhWJ>$f=$*!xq7rb#+PUj-sas_&}t*k*>@5B)XeE-F_l z4JoiUqy+IVM4R|dW!VRDiv*F4PDBY5!+d{lwj>RO=Erg;GMT#hb z92ca)m~!U?oWN=q4JYxHWAmlDTZMvQ=Q#+YP>>@6dK9pPy+S8$t(AsCK{;|G3ZIcC zQv}EZ!8Zh!F}RE{MfeJVZ32hMOcjLf5GW<4iGWDV7Z`GyfoM1^N5b$eDh!l2X`zLL z{u0)W^IWE=<w>bHXYoOczgI$sBE8<2ts~z*>Q}168%WX`QcL<*TPdGj$nH z=NjLYt#Z%7)pGBS3FvLBI_sttRCwm}?`@|?FMZ;SX!gA2xFa9>y;TW}mY+bYtN+4T zvs}q|+gMws;>-fSz+|kx6=k$-jq60$TAF(5@)>_NDzSKXrwW@Hopm?ffH!%f@wu6) zW=BgMy{NYntkd~i>|#T+#ph&~OcXYj%2ZgiLNcg&7U&!OP!IU%x!c osL6vs5BlD9NbkiI9}thGOS=7(!A8~pVgP+?gT`#bi{zflf3!62fB*mh delta 1320 zcmZvcUr1Y59LLYSH#afKjrnIZf3%5-e;Re#wT=eopJvS;Go6N-LZvO4cyHC9NjUco zT^s9bSo@HA7*;lB$`}+&OO|_xJKQ1OjZ7<(`f*{cC!yA0%2&f`8*l1pZ}wn&pYq`pOg>l@_NDZpzos>7|=F5Bf< z3Aaoqqtqbt4Y~n9mI7qOQE<0+7R)iDV3zU#sY%Yk86)$W3`lrM*Md=Ml_MFAyn{rW z>^m8y4w+B#c+;o!BeT2{+;P?aW?7JZD1z<;IrKQ#(O#+xL_(u+|HW8jcqksc61#+P z_)^>-jl>6nqfn1e6prI!(0~aUL*{1a#pdr(EH};3F(^(YCtOev)2XB=w70k4R9?H< z-g50aY6!U&;Adp&3K0{Lbs`&ByjHkI_&JeUB5xDvCbCbYiWIgG;fWc>Qc{Y-7#yE0 zDFeeX|KK1zAQ2ylu+ynAUW69His>m)D(T-H86L6*gz*$F1R+epk~;Qb`Uv6Y=;x3% zs)aVPz&}I|oAHq8n)LM8R5C6Ja}o>^hjuI}&I<6*Q{vKi#{wUpl7tx%{!QW)2{Dz7 zixNaPOKvs>iq*kv_?69pJ_>sl zJZ(?lmbGhN4>XP~wSCV7tggIj)n2UW%=T~F-79U&ZTYz;rlP$wJFv|eUUIH2&XqUi zM>bpfUT_z79Cg{jGOg3Of3whHPp7XiEJ&O`p-TAgJ6)$Su+V(W&2Fo1K8PTs{+u5F@j{e#^3oN#m zRj0SAPCxQ)nmcy^t#j`<>le8)i|O?q2Mo4dz^Z-d`a8V~5$b5(>vAu49qjjEb5m_U z5Z}iyY>5g3X4&rz*3;i|=dgYU{LUbtzjxMPV;wkxr`UA|O}LICe2O7luMYLF>n(jD zAG_hDFmJRd@Io6tmT(V)`3Jy;)v9r(@T~s}6?-0^z+&M>G|Rj}4g?Y#k}nq45y5{2 qe0JS4DgJ|Lp%?mb6yHEh7aXw=WoV*&e=>k>Ez_9FQ3|CmyZ;4D7eA8# diff --git a/api_routes.py b/api_routes.py index 4c5aacf..95cd975 100644 --- a/api_routes.py +++ b/api_routes.py @@ -213,8 +213,11 @@ def handle_meeting_detail(meeting_id): return jsonify(meeting.to_dict()) if request.method == 'DELETE': - if get_jwt().get('role') != 'admin': - return jsonify({"msg": "Administration rights required"}), 403 + current_user_id = get_jwt_identity() + is_admin = get_jwt().get('role') == 'admin' + + if not is_admin and str(meeting.created_by_id) != str(current_user_id): + return jsonify({"msg": "Only the meeting creator or an admin can delete this meeting."}), 403 db.session.delete(meeting) db.session.commit() return jsonify({"msg": "Meeting and associated action items deleted"}), 200 @@ -235,9 +238,9 @@ def summarize_meeting(meeting_id): @jwt_required() def preview_actions(meeting_id): meeting = Meeting.query.get_or_404(meeting_id) - text_content = meeting.summary or meeting.transcript + text_content = meeting.transcript # Always use the full transcript if not text_content: - return jsonify({'error': 'Meeting has no summary or transcript to analyze.'}), 400 + return jsonify({'error': 'Meeting has no transcript to analyze.'}), 400 task = preview_action_items_task.delay(text_content) return jsonify({'task_id': task.id, 'status_url': f'/status/{task.id}'}), 202 diff --git a/frontend/src/pages/MeetingDetailPage.jsx b/frontend/src/pages/MeetingDetailPage.jsx index 73ce933..8ce97b8 100644 --- a/frontend/src/pages/MeetingDetailPage.jsx +++ b/frontend/src/pages/MeetingDetailPage.jsx @@ -40,6 +40,7 @@ const MeetingDetailPage = () => { const [isAddActionItemOpen, setIsAddActionItemOpen] = useState(false); const [newActionItem, setNewActionItem] = useState({ action: '', owner_id: '', due_date: '', item: '' }); const [previewedItems, setPreviewedItems] = useState([]); + const [previewMessage, setPreviewMessage] = useState(''); // State for the preview result message const fetchMeetingData = useCallback(async () => { try { @@ -94,7 +95,11 @@ const MeetingDetailPage = () => { } else if (previewTask) { // Handle preview success setPreviewTask(null); if (updatedTask.state === 'SUCCESS' && updatedTask.info.items) { - setPreviewedItems(updatedTask.info.items); + if (updatedTask.info.items.length > 0) { + setPreviewedItems(updatedTask.info.items); + } else { + setPreviewMessage('文本中未找到可提取的行動項目。'); + } } } } @@ -141,6 +146,8 @@ const MeetingDetailPage = () => { }; const handlePreviewActionItems = async () => { + setPreviewedItems([]); // Clear previous results + setPreviewMessage(''); // Clear previous messages setPreviewTask({ state: 'PENDING', info: 'Initializing preview task...' }); try { // This now calls the async task endpoint @@ -244,9 +251,10 @@ const MeetingDetailPage = () => { )} {canManageMeeting && ( - + {previewMessage && {previewMessage}} {previewedItems.length > 0 && ( Context/ItemActionOwnerDue Date diff --git a/services/__pycache__/dify_client.cpython-312.pyc b/services/__pycache__/dify_client.cpython-312.pyc index 39614f100981c161452c7859908ce228fd59d4be..28616212356e9fd7a906cc2f6907616b6ad7f228 100644 GIT binary patch delta 1995 zcmaJ?Yiv|S6rS0=_wMe!`)U`qwDhs2Ji74#eHaJ=1zRPOP>2Od)|k3#EY5hY9{34imX78>o#CVc9 z=gc{0zL_)koVh;)u9drgcDZB(^v9)h@6@S=0uhrI(-LwJDI^n{st`L2hw5yRhj;x3(xI#E$N024PN~|iH-$Sp znFAlef3X;@FA&TFA{3Q!8kZ~}7uktuScedi*x$H}-NvQZ!yY?|YYeL)7#2N07k^1k zW>FHN{y%s}h+>G*Rk>x$683O{$G>dJX+!7>A&JcyM*qEwaKBMN%heaoF-m#;q3aw4$%z-ximRjsq5T~h-UUDn{X zz~UqTq@{4X_MS?EZuZz(=%+m0v=r-TZB=PnBwD%owJLS6?B1%`Hq<%`?8y)%M;O7PDJ#Zy9Ys%*=c zbu>Qq^0)Wo$jj7O^sc6S^ZD(rdyQva7EoiffZ(v8?jzQl@>*$^z4^J!mdg^Bd z&yy%sqctDMv8Y^JMDsvhom;moiOfEbgVl;52TO9f5?$9?C;buM3(H@BCYMT~>z}c}2b7!v3kBkG--p2h6pv{dBEL{I| zZs_cvJ-sWD=PvZZUiZd5XJnbKs5bUcxw^d;jzo8Hwwtq;fkE{>X-(TnTy27;Rf9fS z&BJOqTgPUdhp?Azb5#~NpDtLlA6UXw(d~p_UbZwJO}BR-(icgs+47rvYtNn~0eKoMO9<_^G;+~kImm_UkfrC9B?P zymDr~yp}0{QE5H*3nbF4Q~o4mTAK3yqX#(SNqmhc-_p$0zI{Vm2Z_hjo%C%m@<(0! z64;UzV|*8t%%GB6sPq;pVV6CDB0t_RShs{gTrAIsG2sE9-QUdadR}^F9rh1uO9;fp S#*CN{CJuftKjfCop8o)~Sl0jm delta 1557 zcmZWpUuYaf7@xV@f4950_b>69q?g?F5}S=uO@fWBNz>?+(mWIM8SekP7O316Cf0%Zec$}P zKl9DbH+w(*ZPNH57*r9AbE{vx*R9_)cHsv%_qNXYkR`5g9tK9RLSuS)wG)Y-2rIra z(=QXTUm=neATmtFQb!~bxFYmxBtleRG?+TfpcNQVh;c>i*DDr}l}HHKAg~D@P^Jw_ zt$`gNSs09=S!_l>#nr44pTqO)Bb-TyUPVN7{kIU29NA;W6xO51SlU<5Ts6T4ee$T` z+3u<_FrO%;Hmf0~X{e$t{qAwf|f!_KhH?aA`^ z`wVLp4>MbAR`&qQJHWorz7!9>qZahWrM{&j`RLuuYVUIIy^HG~6i%PrcyXZc!eBui z+7RCTRg2x|y4tlETN=H0^t;m=+VT0!6IpvC$A6ULi|s$j^^cWcA#COwSIuQJUso_* z*--YgL8%qjt$iXb;9kC`DbYF*w2?y-hf@9fxW*4*^D~vJr72L0hC|^Cj1N23kYinP zXgiQ8c2;>Cr`eiv{kREh+QQ)_4le_kLV1tv^rg)F-g)LqUVD0CVwh$xSMu%$Ya6>B z=!U!x12w+SeC(le(#Q$hedP-5l-h(v_MzG!ibQfnc1P{OGJC2v?d9DRgB;HcqIg$N zBc%?}wB78OmTFgB(R)KV$(6t=jq#3uW)RJb&x!1|)-WgjyOx~>&)Jooq^EmXZOuu0 z9MQd~1w4v!iYsT(Ko>$ec$c76fu_2u8<@d1zNYMmGhB6NmoH#JK(4~N_3DlQ3GsT} z00~!Qq#~nSlGx{hw<$)~x_KOQeHHsyPwI5Xt+4~auR5U?L=SQ3=5QDQZt=ftQ6baT z5cITV#%L>dYvZtonZ_v`Vsl1oRQun>v+s@Ohz5TA<|~7y7!K98oB_5IrhNe3@H~2K zgl}Z8W(&JIel}j6Kl&tEwONzi497Pk)obTN2|UNYriqJ^J!?DGgc&Z^5CWEFIK%;p z5}9(W$r>2d22 z9RM!Q|1|a;fGt19_&1bzgc84?de#(4)>h-^7vI=IAoA&w_*(drum0G_h9a+}Quw(= WdkcZc_m#xDuzvCz^)JqpHv9+7H8=bK diff --git a/services/dify_client.py b/services/dify_client.py index d2f80c1..e45b3f7 100644 --- a/services/dify_client.py +++ b/services/dify_client.py @@ -1,6 +1,7 @@ # services/dify_client.py import os, json, re, requests from dotenv import load_dotenv +from flask import current_app load_dotenv() @@ -19,12 +20,13 @@ def _post_request(endpoint: str, api_key: str, payload: dict): } # For debugging the exact payload sent to the API - print(f"Sending Dify request to {url}: {json.dumps(payload, indent=2, ensure_ascii=False)}") + current_app.logger.debug(f"Sending Dify request to {url}: {json.dumps(payload, indent=2, ensure_ascii=False)}") resp = requests.post(url, headers=headers, json=payload, timeout=TIMEOUT) + current_app.logger.debug(f"Dify API Response ({resp.status_code}): {resp.text}") + if resp.status_code != 200: - print(f"Dify API Error Response: {resp.text}") resp.raise_for_status() data = resp.json() @@ -59,11 +61,14 @@ def summarize_text(text: str, user_id: str = "system") -> str: def extract_action_items(text: str, user_id: str = "system") -> list[dict]: api_key = os.getenv("DIFY_ACTION_EXTRACTOR_API_KEY") + + query = f"請從以下會議記錄中,提取所有行動項目(action items),並嚴格以JSON格式返回。會議記錄如下:\n\n{text}" + payload = { "inputs": {}, "response_mode": "blocking", "user": user_id, - "query": text, + "query": query, } raw = _post_request("/chat-messages", api_key, payload) @@ -90,4 +95,4 @@ def extract_action_items(text: str, user_id: str = "system") -> list[dict]: "owner": i["owner"], "due_date": i["duedate"], }) - return normalized \ No newline at end of file + return normalized diff --git a/tasks.py b/tasks.py index c204708..7434fc5 100644 --- a/tasks.py +++ b/tasks.py @@ -362,37 +362,26 @@ def summarize_text_task(self, meeting_id): def preview_action_items_task(self, text_content): from app import app with app.app_context(): + from services.dify_client import extract_action_items try: self.update_progress(10, 100, "Requesting Dify for action items...") - api_key = app.config.get("DIFY_ACTION_EXTRACTOR_API_KEY") + + # We can reuse the logic from the summarizer to strip timestamps plain_text = re.sub(r'^(\s*\[.*?\])\s*', '', text_content, flags=re.MULTILINE) if not plain_text.strip(): + self.update_progress(100, 100, "Preview skipped, content is empty.") return {'status': 'Success', 'items': []} - response = ask_dify(api_key, plain_text) - answer_text = response.get("answer", "") - self.update_progress(80, 100, "Parsing response...") + # Use the robust client function + parsed_items = extract_action_items(plain_text) - parsed_items = [] - try: - # Find the JSON array within the response string - match = re.search(r'\[.*\]', answer_text, re.DOTALL) - if match: - json_str = match.group(0) - parsed_items = json.loads(json_str) - - # Ensure it's a list, otherwise reset to empty - if not isinstance(parsed_items, list): - parsed_items = [] - - except (json.JSONDecodeError, TypeError): - # If parsing fails, leave it as an empty list - parsed_items = [] - self.update_progress(100, 100, "Action item preview generated.") return {'status': 'Success', 'items': parsed_items} except Exception as e: + # Log the exception for better debugging + import logging + logging.error(f"Error in preview_action_items_task: {e}", exc_info=True) self.update_state( state='FAILURE', meta={'exc_type': type(e).__name__, 'exc_message': str(e)}