swyx commited on
Commit
227ae2d
1 Parent(s): bc2848b
Files changed (1) hide show
  1. app.py +59 -17
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
- pacific_time = utc_time.astimezone(pytz.timezone('US/Pacific'))
31
- return pacific_time.strftime("%Y-%m-%d %I:%M:%S %p PT")
 
 
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 Details {conversation['id']}\n\n"
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"**Time**: {start_pacific.strftime('%I:%M %p')} - {end_pacific.strftime('%I:%M %p')} PT\n"
61
  else:
62
- formatted += f"**Start Time**: {start_pacific.strftime('%Y-%m-%d %I:%M %p')} PT\n"
63
- formatted += f"**End Time**: {end_pacific.strftime('%Y-%m-%d %I:%M %p')} PT\n"
64
  elif start_time:
65
  start_time_formatted = format_end_time(start_time)
66
- formatted += f"**Start Time**: {start_time_formatted}\n"
67
  elif end_time:
68
  end_time_formatted = format_end_time(end_time)
69
- formatted += f"**End Time**: {end_time_formatted}\n"
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
- formatted += f"Speaker **[{speaker}]({current_timestamp})**: {text}\n\n"
 
 
 
 
 
 
 
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
- "End Time": format_end_time(c['end_time']),
106
- "Summary": c['short_summary'][1:50] + "..."
 
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(label="Select a conversation (CLICK ON THE ID)", interactive=True)
 
 
 
 
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
- return formatted_conversation, gr.update(visible=True), conversation_id
 
 
229
  except Exception as e:
230
  error_message = f"Error updating conversation: {str(e)}"
231
  logging.error(error_message)
232
- return error_message, gr.update(visible=False), None
233
 
234
- conversation_table.select(update_conversation, inputs=[api_key], outputs=[conversation_details, delete_button, selected_conversation_id])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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,