ICAS03 commited on
Commit
46f84a2
·
1 Parent(s): 28f11ce

- fix upload-drive & notion

Browse files
Files changed (4) hide show
  1. Project.py +2 -6
  2. event_handler.py +12 -0
  3. google_drive.py +200 -56
  4. notion.py +79 -14
Project.py CHANGED
@@ -81,17 +81,13 @@ class Project:
81
  'generated_plan_test_components': lambda self: self.generated_plan_test_components,
82
  'generated_page_dev_components': lambda self: self.generated_page_dev_components,
83
  'generated_engage_dev_components': lambda self: self.generated_engage_dev_components,
84
- 'reformatted_page_dev_components': lambda self: self.reformatted_dev_components,
85
- 'reformatted_engage_dev_components': lambda self: self.reformatted_dev_components,
86
- 'reformatted_hybrid_dev_components': lambda self: self.reformatted_dev_components,
87
  'generated_intents_csv': lambda self: self.generated_intents_csv,
88
  'generated_plan_test_mandays': lambda self: self.generated_plan_test_mandays,
89
- 'reformatted_dev_components': lambda self: self.reformatted_dev_components,
90
  'generated_dev_mandays': lambda self: self.generated_dev_mandays,
91
- 'mvp_components': lambda self: self.mvp_components,
92
  'generated_mvp_prd': lambda self: self.generated_mvp_prd,
93
  'combined_cost_summary': lambda self: self.combined_cost_summary,
94
- 'generated_page_BD_SOW': lambda self: self.generated_BD_SOW,
95
  'generated_Tech_SOW': lambda self: self.generated_Tech_SOW,
96
  'identified_planning_testing_components': lambda self: self.identified_planning_testing_components,
97
  'identified_development_components': lambda self: self.identified_development_components,
 
81
  'generated_plan_test_components': lambda self: self.generated_plan_test_components,
82
  'generated_page_dev_components': lambda self: self.generated_page_dev_components,
83
  'generated_engage_dev_components': lambda self: self.generated_engage_dev_components,
84
+ 'reformatted_dev_components': lambda self: self.reformatted_dev_components,
 
 
85
  'generated_intents_csv': lambda self: self.generated_intents_csv,
86
  'generated_plan_test_mandays': lambda self: self.generated_plan_test_mandays,
 
87
  'generated_dev_mandays': lambda self: self.generated_dev_mandays,
 
88
  'generated_mvp_prd': lambda self: self.generated_mvp_prd,
89
  'combined_cost_summary': lambda self: self.combined_cost_summary,
90
+ 'generated_BD_SOW': lambda self: self.generated_BD_SOW,
91
  'generated_Tech_SOW': lambda self: self.generated_Tech_SOW,
92
  'identified_planning_testing_components': lambda self: self.identified_planning_testing_components,
93
  'identified_development_components': lambda self: self.identified_development_components,
event_handler.py CHANGED
@@ -134,6 +134,18 @@ def setup_all_handlers(step_buttons, all_components, progress_update, quotation_
134
  outputs=[quotation_cost],
135
  )
136
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  except Exception as e:
138
  print(f"Error setting up handlers: {str(e)}")
139
  return None
 
134
  outputs=[quotation_cost],
135
  )
136
 
137
+ upload_drive_btn.click(
138
+ fn=upload_to_gdrive,
139
+ inputs=[project_name],
140
+ outputs=[progress_update]
141
+ )
142
+
143
+ upload_notion_btn.click(
144
+ fn=upload_to_notion,
145
+ inputs=[project_name],
146
+ outputs=[progress_update]
147
+ )
148
+
149
  except Exception as e:
150
  print(f"Error setting up handlers: {str(e)}")
151
  return None
google_drive.py CHANGED
@@ -9,6 +9,10 @@ from google.oauth2 import service_account
9
  from googleapiclient.errors import HttpError
10
  from docx import Document
11
  import re
 
 
 
 
12
 
13
  # Path to your Service Account key file
14
  SERVICE_ACCOUNT_FILE = 'gdrive_service_account.json'
@@ -131,61 +135,6 @@ def process_table(doc, table_rows):
131
  run = paragraph.add_run(cell_content)
132
  run.bold = is_bold
133
 
134
- def convert_md_to_docx(md_content):
135
- """
136
- Convert Markdown content to a DOCX document using python-docx.
137
- Handles headings, lists, tables, and text formatting.
138
- """
139
- doc = Document()
140
- lines = md_content.split('\n')
141
- in_table = False
142
- table_rows = []
143
- line_index = 0
144
-
145
- while line_index < len(lines):
146
- line = lines[line_index]
147
- stripped_line = line.strip()
148
-
149
- # Handle tables
150
- if stripped_line.startswith('|') and stripped_line.endswith('|'):
151
- if not in_table:
152
- in_table = True
153
- table_rows = [] # Reset table rows when starting a new table
154
-
155
- table_rows.append(stripped_line)
156
- line_index += 1
157
- continue
158
-
159
- # Handle end of table
160
- if in_table:
161
- # Process the table when we hit non-table content
162
- if table_rows:
163
- process_table(doc, table_rows)
164
- table_rows = []
165
- in_table = False
166
-
167
- # Only add paragraph if we're not at an empty line
168
- if not stripped_line:
169
- doc.add_paragraph()
170
-
171
- # Handle non-table content
172
- if stripped_line:
173
- process_non_table_line(doc, stripped_line)
174
- elif not in_table: # Only add empty paragraph if we're not in a table
175
- doc.add_paragraph()
176
-
177
- line_index += 1
178
-
179
- # Handle any remaining table at the end of the document
180
- if in_table and table_rows:
181
- process_table(doc, table_rows)
182
-
183
- # Convert to bytes
184
- output = io.BytesIO()
185
- doc.save(output)
186
- output.seek(0)
187
- return output.getvalue()
188
-
189
  def process_non_table_line(doc, stripped_line):
190
  """
191
  Process a non-table line of markdown text, handling various formatting options.
@@ -255,6 +204,61 @@ def convert_df_to_text(df):
255
  table = f"| {header} |\n|{separator}|\n" + "\n".join(rows)
256
  return table
257
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
  def determine_mime_type(filename):
259
  """Determine MIME type based on file extension for Google Drive conversion."""
260
  print(f"Determining MIME type for {filename}...")
@@ -326,13 +330,31 @@ def upload_content(service, folder_id, filename, content):
326
  mimetype=mime_type,
327
  resumable=True
328
  )
329
- else:
330
  # For binary content (already bytes)
331
  media = MediaIoBaseUpload(
332
  io.BytesIO(content),
333
  mimetype=mime_type,
334
  resumable=True
335
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
 
337
  print(f"Prepared media for upload: {media}")
338
 
@@ -346,4 +368,126 @@ def upload_content(service, folder_id, filename, content):
346
  except Exception as e:
347
  print(f"An error occurred while uploading {filename}: {e}")
348
  raise e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  from googleapiclient.errors import HttpError
10
  from docx import Document
11
  import re
12
+ from datetime import datetime
13
+ import gradio as gr
14
+ from state import state
15
+ import json
16
 
17
  # Path to your Service Account key file
18
  SERVICE_ACCOUNT_FILE = 'gdrive_service_account.json'
 
135
  run = paragraph.add_run(cell_content)
136
  run.bold = is_bold
137
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  def process_non_table_line(doc, stripped_line):
139
  """
140
  Process a non-table line of markdown text, handling various formatting options.
 
204
  table = f"| {header} |\n|{separator}|\n" + "\n".join(rows)
205
  return table
206
 
207
+ def convert_md_to_docx(md_content):
208
+ """
209
+ Convert Markdown content to a DOCX document using python-docx.
210
+ Handles headings, lists, tables, and text formatting.
211
+ """
212
+ doc = Document()
213
+ lines = md_content.split('\n')
214
+ in_table = False
215
+ table_rows = []
216
+ line_index = 0
217
+
218
+ while line_index < len(lines):
219
+ line = lines[line_index]
220
+ stripped_line = line.strip()
221
+
222
+ # Handle tables
223
+ if stripped_line.startswith('|') and stripped_line.endswith('|'):
224
+ if not in_table:
225
+ in_table = True
226
+ table_rows = [] # Reset table rows when starting a new table
227
+
228
+ table_rows.append(stripped_line)
229
+ line_index += 1
230
+ continue
231
+
232
+ # Handle end of table
233
+ if in_table:
234
+ # Process the table when we hit non-table content
235
+ if table_rows:
236
+ process_table(doc, table_rows)
237
+ table_rows = []
238
+ in_table = False
239
+
240
+ # Only add paragraph if we're not at an empty line
241
+ if not stripped_line:
242
+ doc.add_paragraph()
243
+
244
+ # Handle non-table content
245
+ if stripped_line:
246
+ process_non_table_line(doc, stripped_line)
247
+ elif not in_table: # Only add empty paragraph if we're not in a table
248
+ doc.add_paragraph()
249
+
250
+ line_index += 1
251
+
252
+ # Handle any remaining table at the end of the document
253
+ if in_table and table_rows:
254
+ process_table(doc, table_rows)
255
+
256
+ # Convert to bytes
257
+ output = io.BytesIO()
258
+ doc.save(output)
259
+ output.seek(0)
260
+ return output.getvalue()
261
+
262
  def determine_mime_type(filename):
263
  """Determine MIME type based on file extension for Google Drive conversion."""
264
  print(f"Determining MIME type for {filename}...")
 
330
  mimetype=mime_type,
331
  resumable=True
332
  )
333
+ elif isinstance(content, bytes):
334
  # For binary content (already bytes)
335
  media = MediaIoBaseUpload(
336
  io.BytesIO(content),
337
  mimetype=mime_type,
338
  resumable=True
339
  )
340
+ elif isinstance(content, dict):
341
+ # Convert dict to JSON string
342
+ json_content = json.dumps(content, indent=2)
343
+ media = MediaIoBaseUpload(
344
+ io.BytesIO(json_content.encode('utf-8')),
345
+ mimetype='application/json',
346
+ resumable=True
347
+ )
348
+ elif isinstance(content, list):
349
+ # Convert list to a string representation
350
+ list_content = "\n".join(str(item) for item in content)
351
+ media = MediaIoBaseUpload(
352
+ io.BytesIO(list_content.encode('utf-8')),
353
+ mimetype='text/plain',
354
+ resumable=True
355
+ )
356
+ else:
357
+ raise ValueError("Unsupported content type for upload.")
358
 
359
  print(f"Prepared media for upload: {media}")
360
 
 
368
  except Exception as e:
369
  print(f"An error occurred while uploading {filename}: {e}")
370
  raise e
371
+
372
+ def upload_to_gdrive(project_name, progress=gr.Progress()):
373
+ print("Starting upload to Google Drive...")
374
+ service = authenticate_drive_service()
375
+
376
+ project = state.quotation_project
377
+ if project is None:
378
+ print("Error: quotation_project is not set")
379
+
380
+ parent_folder_id = DRIVE_FOLDER_ID
381
+ if not parent_folder_id:
382
+ return "Drive folder ID is not set."
383
+
384
+ if not project_name:
385
+ project_name = "Final Quotation"
386
+
387
+ folder_metadata = {
388
+ 'name': f'{project_name}_{datetime.now().strftime("%y%m%d_%H%M%S")}',
389
+ 'mimeType': 'application/vnd.google-apps.folder',
390
+ 'parents': [parent_folder_id]
391
+ }
392
+
393
+ subfolder = service.files().create(body=folder_metadata, fields='id').execute()
394
+ subfolder_id = subfolder.get('id')
395
+ print(f"Created subfolder with ID: {subfolder_id}")
396
+ progress(0.1, "Created subfolder and preparing files to upload.")
397
+
398
+ try:
399
+ # Define the attributes to upload with appropriate file extensions
400
+ attributes_to_upload = {
401
+ "generated_prd": "PRD.md",
402
+ "generated_plan_test_components": "plan_test_components.md",
403
+ "generated_page_dev_components": "page_dev_components.md",
404
+ "generated_engage_dev_components": "engage_dev_components.md",
405
+ "generated_intent_list": "intent_list.md",
406
+ "reformatted_dev_components": "reformatted_dev_components.md",
407
+ "generated_mvp_prd": "MVP_prd.md",
408
+ "combined_cost_summary": "cost_summary.md",
409
+ "generated_BD_SOW": "BD_SOW.md",
410
+ "generated_Tech_SOW": "Tech_SOW.md"
411
+ }
412
+
413
+ # Upload each attribute using the upload_content function
414
+ for attr, filename in attributes_to_upload.items():
415
+ try:
416
+ content = getattr(project, attr, None)
417
+ print(f"Uploading {attr} with filename {filename}...")
418
+ if content:
419
+ # Convert list of dicts to DataFrame if necessary
420
+ if isinstance(content, list) and all(isinstance(i, dict) for i in content):
421
+ content = pd.DataFrame(content)
422
+ print(f"Content for {filename}: {content}")
423
+ upload_content(service, subfolder_id, filename, content)
424
+ progress(0.1, f"Uploaded {filename}")
425
+ else:
426
+ print(f"No content found for {attr}")
427
+ except Exception as e:
428
+ print(f"Failed to upload {filename}: {e}")
429
+
430
+ # Handle the mandays csv results
431
+ try:
432
+ if project.mandays_results:
433
+ for result in project.mandays_results:
434
+ function_name = result['function_name']
435
+ df = pd.DataFrame(result['result'])
436
+ if not df.empty:
437
+ csv_content = df.to_csv(index=False)
438
+ upload_content(service, subfolder_id, f"{function_name}.csv", csv_content)
439
+ progress(0.1, f"Uploaded {function_name}.csv")
440
+
441
+ if project.mvp_mandays_results:
442
+ for result in project.mvp_mandays_results:
443
+ function_name = result['function_name']
444
+ result_data = result['result']
445
+
446
+ # Check if result_data is a dictionary
447
+ if isinstance(result_data, dict):
448
+ for section_name, records in result_data.items():
449
+ if isinstance(records, list):
450
+ df = pd.DataFrame(records)
451
+ if not df.empty:
452
+ csv_content = df.to_csv(index=False)
453
+
454
+ # Upload the CSV content to Google Drive
455
+ upload_content(service, subfolder_id, f"{function_name}_{section_name}.csv", csv_content)
456
+ progress(0.1, f"Uploaded {function_name}_{section_name}.csv")
457
+ else:
458
+ print(f"Unexpected data format for {section_name} in {function_name}.")
459
+ else:
460
+ print(f"Unexpected result data format for {function_name}.")
461
+
462
+ except Exception as e:
463
+ print(f"Failed to upload mandays results: {e}")
464
+
465
+ folder_url = f"https://drive.google.com/drive/folders/{subfolder_id}"
466
+ progress(1.0, "Upload complete")
467
+ return f"All files uploaded successfully. Folder URL: {folder_url}"
468
+
469
+ except Exception as e:
470
+ print(f"An error occurred: {e}")
471
+ return f"Failed to upload files. Error: {e}"
472
 
473
+ def upload_combined_content(service, subfolder_id, combined_cost_summary, generated_plan_test_components, reformatted_dev_components, generated_intent_list):
474
+ """
475
+ Combine various content sections into a single Markdown document and upload it.
476
+ """
477
+ # Combine the content into a single Markdown string
478
+ combined_content = f"""
479
+ # Final Cost Summary
480
+ {combined_cost_summary}
481
+
482
+ # Final Planning and Testing Component
483
+ {generated_plan_test_components}
484
+
485
+ # Final Development Component
486
+ {reformatted_dev_components}
487
+
488
+ # Final Intent List
489
+ {generated_intent_list}
490
+ """
491
+
492
+ # Upload the combined content as a Markdown file
493
+ upload_content(service, subfolder_id, "quotation_document.md", combined_content)
notion.py CHANGED
@@ -3,6 +3,8 @@ import re
3
  from notion_client import Client
4
  from datetime import datetime
5
  from dotenv import load_dotenv
 
 
6
 
7
  load_dotenv()
8
 
@@ -198,8 +200,10 @@ def upload_blocks_to_notion(notion, page_id, blocks, chunk_size=95):
198
  notion.blocks.children.append(page_id, children=chunk)
199
  print(f"Uploaded blocks {i+1} to {i+len(chunk)}")
200
 
201
- def upload_to_notion(project_name, generation_result):
202
  """Upload generation results to Notion as subpages."""
 
 
203
  try:
204
  notion = Client(auth=os.getenv("NOTION_TOKEN"))
205
  parent_page_id = os.getenv("NOTION_PAGE_ID")
@@ -216,33 +220,94 @@ def upload_to_notion(project_name, generation_result):
216
  parent_page_id = parent_page["id"]
217
  print(f"Created parent Notion page with ID: {parent_page_id}")
218
 
219
- # Create subpages for each item
220
- for item in generation_result:
221
- if not (isinstance(item, (list, tuple)) and len(item) == 2):
222
- print(f"Skipping invalid item: {item}")
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  continue
224
-
225
- key, content = item
226
- if not isinstance(key, str):
227
- print(f"Skipping item with non-string key: {key}")
228
  continue
229
-
230
- print(f"Creating subpage for item '{key}'")
231
  try:
232
  # Create subpage
233
  subpage = notion.pages.create(
234
  parent={"page_id": parent_page_id},
235
- properties={"title": [{"type": "text", "text": {"content": key}}]}
236
  )
237
 
238
  # Process content
 
 
 
 
 
 
 
 
 
239
  blocks = process_mixed_content(content) or markdown_to_notion_blocks(content)
240
  if blocks:
241
  upload_blocks_to_notion(notion, subpage["id"], blocks)
242
  else:
243
- print(f"No blocks generated for subpage '{key}'")
244
  except Exception as e:
245
- print(f"Error processing item '{key}': {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
 
247
  return f"Successfully uploaded to Notion page: {page_title}. Link: https://www.notion.so/{parent_page_id}"
248
 
 
3
  from notion_client import Client
4
  from datetime import datetime
5
  from dotenv import load_dotenv
6
+ from state import state
7
+ import pandas as pd
8
 
9
  load_dotenv()
10
 
 
200
  notion.blocks.children.append(page_id, children=chunk)
201
  print(f"Uploaded blocks {i+1} to {i+len(chunk)}")
202
 
203
+ def upload_to_notion(project_name):
204
  """Upload generation results to Notion as subpages."""
205
+
206
+ project = state.quotation_project
207
  try:
208
  notion = Client(auth=os.getenv("NOTION_TOKEN"))
209
  parent_page_id = os.getenv("NOTION_PAGE_ID")
 
220
  parent_page_id = parent_page["id"]
221
  print(f"Created parent Notion page with ID: {parent_page_id}")
222
 
223
+ # Define the attributes to upload
224
+ attributes_to_upload = [
225
+ "generated_prd",
226
+ "generated_plan_test_components",
227
+ "generated_page_dev_components",
228
+ "generated_engage_dev_components",
229
+ "generated_intent_list",
230
+ "reformatted_dev_components",
231
+ "generated_mvp_prd",
232
+ "combined_cost_summary",
233
+ "generated_BD_SOW",
234
+ "generated_Tech_SOW"
235
+ ]
236
+
237
+ # Iterate over selected attributes of the project
238
+ for attr_name in attributes_to_upload:
239
+ if not hasattr(project, attr_name):
240
  continue
241
+
242
+ content = getattr(project, attr_name)
243
+ if content is None:
 
244
  continue
245
+
246
+ print(f"Creating subpage for attribute '{attr_name}'")
247
  try:
248
  # Create subpage
249
  subpage = notion.pages.create(
250
  parent={"page_id": parent_page_id},
251
+ properties={"title": [{"type": "text", "text": {"content": attr_name}}]}
252
  )
253
 
254
  # Process content
255
+ if isinstance(content, pd.DataFrame):
256
+ content = content.to_markdown()
257
+ elif isinstance(content, list) and all(isinstance(i, dict) for i in content):
258
+ # Convert list of dictionaries to DataFrame
259
+ content = pd.DataFrame(content).to_markdown()
260
+ elif isinstance(content, list):
261
+ # Convert list to a string representation
262
+ content = "\n".join(str(item) for item in content)
263
+
264
  blocks = process_mixed_content(content) or markdown_to_notion_blocks(content)
265
  if blocks:
266
  upload_blocks_to_notion(notion, subpage["id"], blocks)
267
  else:
268
+ print(f"No blocks generated for subpage '{attr_name}'")
269
  except Exception as e:
270
+ print(f"Error processing attribute '{attr_name}': {e}")
271
+
272
+ # Upload mandays results
273
+ try:
274
+ if project.mandays_results:
275
+ for result in project.mandays_results:
276
+ function_name = result['function_name']
277
+ df = pd.DataFrame(result['result'])
278
+ if not df.empty:
279
+ markdown_content = df.to_markdown(index=False)
280
+ blocks = markdown_to_notion_blocks(markdown_content)
281
+ subpage = notion.pages.create(
282
+ parent={"page_id": parent_page_id},
283
+ properties={"title": [{"type": "text", "text": {"content": f"{function_name}_mandays"}}]}
284
+ )
285
+ upload_blocks_to_notion(notion, subpage["id"], blocks)
286
+ print(f"Uploaded {function_name}_mandays to Notion")
287
+
288
+ if project.mvp_mandays_results:
289
+ for result in project.mvp_mandays_results:
290
+ function_name = result['function_name']
291
+ result_data = result['result']
292
+
293
+ df = pd.DataFrame(result_data)
294
+ if isinstance(result_data, dict):
295
+ for section_name, records in result_data.items():
296
+ if isinstance(records, list):
297
+ df = pd.DataFrame(records)
298
+ if not df.empty:
299
+ markdown_content = df.to_markdown(index=False) # Convert DataFrame to markdown
300
+ blocks = markdown_to_notion_blocks(markdown_content)
301
+
302
+ subpage = notion.pages.create(
303
+ parent={"page_id": parent_page_id},
304
+ properties={"title": [{"type": "text", "text": {"content": f"{function_name}_mvp_mandays"}}]}
305
+ )
306
+ upload_blocks_to_notion(notion, subpage["id"], blocks)
307
+ print(f"Uploaded {function_name}_mvp_mandays to Notion")
308
+
309
+ except Exception as e:
310
+ print(f"Failed to upload mandays results to Notion: {e}")
311
 
312
  return f"Successfully uploaded to Notion page: {page_title}. Link: https://www.notion.so/{parent_page_id}"
313