Cgs
Browse files
app.py
CHANGED
@@ -22,13 +22,15 @@ total_pages = 1
|
|
22 |
async def fetch_conversations(api_key: str, page: int = 1) -> Dict[str, Any]:
|
23 |
bee = Bee(api_key)
|
24 |
logging.info(f"Fetching conversations for user 'me', page {page}")
|
25 |
-
conversations = await bee.get_conversations("me", page=page)
|
26 |
return conversations
|
27 |
|
28 |
def format_end_time(end_time: str) -> str:
|
29 |
utc_time = datetime.fromisoformat(end_time.replace('Z', '+00:00'))
|
30 |
-
|
31 |
-
|
|
|
|
|
32 |
|
33 |
async def fetch_conversation(api_key: str, conversation_id: int) -> Dict[str, Any]:
|
34 |
bee = Bee(api_key)
|
@@ -45,7 +47,7 @@ def format_conversation(data: Dict[str, Any]) -> str:
|
|
45 |
try:
|
46 |
conversation = data.get("conversation", {})
|
47 |
logging.debug(f"Conversation keys: {conversation.keys()}")
|
48 |
-
formatted = f"# Conversation
|
49 |
# Format start_time and end_time
|
50 |
start_time = conversation.get('start_time')
|
51 |
end_time = conversation.get('end_time')
|
@@ -57,16 +59,16 @@ def format_conversation(data: Dict[str, Any]) -> str:
|
|
57 |
end_pacific = end_dt.astimezone(pacific_tz)
|
58 |
|
59 |
if start_pacific.date() == end_pacific.date():
|
60 |
-
formatted += f"
|
61 |
else:
|
62 |
-
formatted += f"**Start
|
63 |
-
formatted += f"**End
|
64 |
elif start_time:
|
65 |
start_time_formatted = format_end_time(start_time)
|
66 |
-
formatted += f"**Start
|
67 |
elif end_time:
|
68 |
end_time_formatted = format_end_time(end_time)
|
69 |
-
formatted += f"**End
|
70 |
|
71 |
# Display short_summary nicely
|
72 |
if 'short_summary' in conversation:
|
@@ -87,13 +89,26 @@ def format_conversation(data: Dict[str, Any]) -> str:
|
|
87 |
speaker = utterance.get('speaker')
|
88 |
text = utterance.get('text')
|
89 |
|
90 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
|
92 |
return formatted
|
93 |
except Exception as e:
|
94 |
logging.error(f"Error formatting conversation: {str(e)}")
|
95 |
return f"Error formatting conversation: {str(e)}\n\nRaw data: {conversation}"
|
96 |
|
|
|
|
|
|
|
|
|
|
|
|
|
97 |
async def list_conversations(api_key: str) -> Tuple[pd.DataFrame, str, int, int]:
|
98 |
global current_page, total_pages
|
99 |
conversations_data = await fetch_conversations(api_key, current_page)
|
@@ -102,12 +117,13 @@ async def list_conversations(api_key: str) -> Tuple[pd.DataFrame, str, int, int]
|
|
102 |
df = pd.DataFrame([
|
103 |
{
|
104 |
"ID": c['id'],
|
105 |
-
"
|
106 |
-
"Summary": c['short_summary'][1:
|
|
|
107 |
}
|
108 |
for c in conversations
|
109 |
])
|
110 |
-
df = df[["ID", "End Time", "Summary"]] # Reorder columns to ensure ID is first
|
111 |
info = f"Page {current_page} of {total_pages}"
|
112 |
return df, info, current_page, total_pages
|
113 |
|
@@ -192,7 +208,11 @@ with gr.Blocks() as demo:
|
|
192 |
with gr.Column(scale=1):
|
193 |
api_key = gr.Textbox(label="Enter your Bee API Key", type="password")
|
194 |
load_button = gr.Button("Load Conversations")
|
195 |
-
conversation_table = gr.Dataframe(
|
|
|
|
|
|
|
|
|
196 |
info_text = gr.Textbox(label="Info", interactive=False)
|
197 |
prev_page = gr.Button("Previous Page")
|
198 |
next_page = gr.Button("Next Page")
|
@@ -224,14 +244,36 @@ with gr.Blocks() as demo:
|
|
224 |
logging.info(f"SelectData event: index={evt.index}, value={evt.value}")
|
225 |
conversation_id = int(evt.value)
|
226 |
logging.info(f"Updating conversation with ID: {conversation_id}")
|
|
|
|
|
|
|
|
|
|
|
227 |
formatted_conversation = await display_conversation(api_key, conversation_id)
|
228 |
-
|
|
|
|
|
229 |
except Exception as e:
|
230 |
error_message = f"Error updating conversation: {str(e)}"
|
231 |
logging.error(error_message)
|
232 |
-
|
233 |
|
234 |
-
conversation_table.select(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
235 |
|
236 |
delete_button.click(
|
237 |
delete_selected_conversation,
|
|
|
22 |
async def fetch_conversations(api_key: str, page: int = 1) -> Dict[str, Any]:
|
23 |
bee = Bee(api_key)
|
24 |
logging.info(f"Fetching conversations for user 'me', page {page}")
|
25 |
+
conversations = await bee.get_conversations("me", page=page, limit=15)
|
26 |
return conversations
|
27 |
|
28 |
def format_end_time(end_time: str) -> str:
|
29 |
utc_time = datetime.fromisoformat(end_time.replace('Z', '+00:00'))
|
30 |
+
user_timezone = pytz.timezone('US/Pacific') # TODO: Replace with actual user timezone
|
31 |
+
local_time = utc_time.astimezone(user_timezone)
|
32 |
+
timezone_abbr = local_time.strftime('%Z')
|
33 |
+
return f"{local_time.strftime('%I:%M %p')} {timezone_abbr}"
|
34 |
|
35 |
async def fetch_conversation(api_key: str, conversation_id: int) -> Dict[str, Any]:
|
36 |
bee = Bee(api_key)
|
|
|
47 |
try:
|
48 |
conversation = data.get("conversation", {})
|
49 |
logging.debug(f"Conversation keys: {conversation.keys()}")
|
50 |
+
formatted = f"# Conversation [{conversation['id']}] "
|
51 |
# Format start_time and end_time
|
52 |
start_time = conversation.get('start_time')
|
53 |
end_time = conversation.get('end_time')
|
|
|
59 |
end_pacific = end_dt.astimezone(pacific_tz)
|
60 |
|
61 |
if start_pacific.date() == end_pacific.date():
|
62 |
+
formatted += f"{start_pacific.strftime('%I:%M %p')} - {end_pacific.strftime('%I:%M %p')} PT\n\n"
|
63 |
else:
|
64 |
+
formatted += f"\n\n**Start**: {start_pacific.strftime('%Y-%m-%d %I:%M %p')} PT\n"
|
65 |
+
formatted += f"**End**: {end_pacific.strftime('%Y-%m-%d %I:%M %p')} PT\n"
|
66 |
elif start_time:
|
67 |
start_time_formatted = format_end_time(start_time)
|
68 |
+
formatted += f"**Start**: {start_time_formatted}\n"
|
69 |
elif end_time:
|
70 |
end_time_formatted = format_end_time(end_time)
|
71 |
+
formatted += f"**End**: {end_time_formatted}\n"
|
72 |
|
73 |
# Display short_summary nicely
|
74 |
if 'short_summary' in conversation:
|
|
|
89 |
speaker = utterance.get('speaker')
|
90 |
text = utterance.get('text')
|
91 |
|
92 |
+
if last_timestamp is not None:
|
93 |
+
time_diff = datetime.fromisoformat(current_timestamp.replace('Z', '+00:00')) - datetime.fromisoformat(last_timestamp.replace('Z', '+00:00'))
|
94 |
+
if time_diff.total_seconds() > 300: # More than 5 minutes
|
95 |
+
local_time = datetime.fromisoformat(current_timestamp.replace('Z', '+00:00')).astimezone().strftime('%I:%M %p')
|
96 |
+
formatted += f"[{local_time}]\n\n"
|
97 |
+
|
98 |
+
formatted += f"Speaker **[{speaker}](https://kagi.com/search?q={current_timestamp})**: {text}\n\n"
|
99 |
+
last_timestamp = current_timestamp
|
100 |
|
101 |
return formatted
|
102 |
except Exception as e:
|
103 |
logging.error(f"Error formatting conversation: {str(e)}")
|
104 |
return f"Error formatting conversation: {str(e)}\n\nRaw data: {conversation}"
|
105 |
|
106 |
+
def format_duration(start_time: str, end_time: str) -> str:
|
107 |
+
start_dt = datetime.fromisoformat(start_time.replace('Z', '+00:00'))
|
108 |
+
end_dt = datetime.fromisoformat(end_time.replace('Z', '+00:00'))
|
109 |
+
duration = end_dt - start_dt
|
110 |
+
return f"{duration.total_seconds() // 3600:.0f}h {((duration.total_seconds() % 3600) // 60):.0f}m"
|
111 |
+
|
112 |
async def list_conversations(api_key: str) -> Tuple[pd.DataFrame, str, int, int]:
|
113 |
global current_page, total_pages
|
114 |
conversations_data = await fetch_conversations(api_key, current_page)
|
|
|
117 |
df = pd.DataFrame([
|
118 |
{
|
119 |
"ID": c['id'],
|
120 |
+
"Duration": format_duration(c['start_time'], c['end_time']) if c['start_time'] and c['end_time'] else "",
|
121 |
+
"Summary": ' '.join(c['short_summary'].split()[1:21]) + "..." if c['short_summary'] else "",
|
122 |
+
"End Time": format_end_time(c['end_time']) if c['end_time'] else "",
|
123 |
}
|
124 |
for c in conversations
|
125 |
])
|
126 |
+
df = df[["ID", "End Time", "Duration", "Summary"]] # Reorder columns to ensure ID is first
|
127 |
info = f"Page {current_page} of {total_pages}"
|
128 |
return df, info, current_page, total_pages
|
129 |
|
|
|
208 |
with gr.Column(scale=1):
|
209 |
api_key = gr.Textbox(label="Enter your Bee API Key", type="password")
|
210 |
load_button = gr.Button("Load Conversations")
|
211 |
+
conversation_table = gr.Dataframe(
|
212 |
+
label="Select a conversation (CLICK ON THE ID!!!)",
|
213 |
+
interactive=True,
|
214 |
+
row_count=10 # Adjust this number to approximate the desired height
|
215 |
+
)
|
216 |
info_text = gr.Textbox(label="Info", interactive=False)
|
217 |
prev_page = gr.Button("Previous Page")
|
218 |
next_page = gr.Button("Next Page")
|
|
|
244 |
logging.info(f"SelectData event: index={evt.index}, value={evt.value}")
|
245 |
conversation_id = int(evt.value)
|
246 |
logging.info(f"Updating conversation with ID: {conversation_id}")
|
247 |
+
|
248 |
+
# Return a loading message immediately
|
249 |
+
yield gr.update(value="Loading conversation details...", visible=True), gr.update(visible=False), None
|
250 |
+
|
251 |
+
# Fetch and format the conversation
|
252 |
formatted_conversation = await display_conversation(api_key, conversation_id)
|
253 |
+
|
254 |
+
# Return the formatted conversation and update the UI
|
255 |
+
yield formatted_conversation, gr.update(visible=True), conversation_id
|
256 |
except Exception as e:
|
257 |
error_message = f"Error updating conversation: {str(e)}"
|
258 |
logging.error(error_message)
|
259 |
+
yield error_message, gr.update(visible=False), None
|
260 |
|
261 |
+
conversation_table.select(
|
262 |
+
update_conversation,
|
263 |
+
inputs=[api_key],
|
264 |
+
outputs=[conversation_details, delete_button, selected_conversation_id],
|
265 |
+
_js="(api_key, evt) => [api_key, evt]", # This ensures the evt object is passed correctly
|
266 |
+
).then(
|
267 |
+
lambda: None, # This is a no-op function
|
268 |
+
None, # No inputs
|
269 |
+
None, # No outputs
|
270 |
+
_js="""
|
271 |
+
() => {
|
272 |
+
// Scroll to the conversation details
|
273 |
+
document.querySelector('#conversation_details').scrollIntoView({behavior: 'smooth'});
|
274 |
+
}
|
275 |
+
"""
|
276 |
+
)
|
277 |
|
278 |
delete_button.click(
|
279 |
delete_selected_conversation,
|