Marthee commited on
Commit
a355392
·
verified ·
1 Parent(s): 78a602d

Update InitialMarkups.py

Browse files
Files changed (1) hide show
  1. InitialMarkups.py +465 -0
InitialMarkups.py CHANGED
@@ -2417,3 +2417,468 @@ def extract_section_under_header_tobebilled2marthe(multiplePDF_Paths):
2417
  return pdf_bytes.getvalue(), docHighlights , json_output, Alltexttobebilled , filenames
2418
 
2419
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2417
  return pdf_bytes.getvalue(), docHighlights , json_output, Alltexttobebilled , filenames
2418
 
2419
 
2420
+
2421
+ def extract_section_under_header_tobebilledMultiplePDFSmarthe(multiplePDF_Paths):
2422
+ # keywordstoSkip=["installation", "execution", "miscellaneous items", "workmanship", "testing", "labeling"]
2423
+ filenames=[]
2424
+ keywords = {'installation', 'execution', 'miscellaneous items', 'workmanship', 'testing', 'labeling'}
2425
+ top_margin = 70
2426
+ bottom_margin = 50
2427
+ arrayofPDFS=multiplePDF_Paths.split(',')
2428
+ print(multiplePDF_Paths)
2429
+ print(arrayofPDFS)
2430
+ df = pd.DataFrame(columns=["PDF Name","NBSLink","Subject","Page","Author","Creation Date","Layer",'Code', 'head above 1', "head above 2","BodyText"])
2431
+ for pdf_path in arrayofPDFS:
2432
+ headertoContinue1 = False
2433
+ headertoContinue2=False
2434
+ Alltexttobebilled=''
2435
+ parsed_url = urlparse(pdf_path)
2436
+ filename = os.path.basename(parsed_url.path)
2437
+ filename = unquote(filename) # decode URL-encoded characters
2438
+ filenames.append(filename)
2439
+ # Optimized URL handling
2440
+ if pdf_path and ('http' in pdf_path or 'dropbox' in pdf_path):
2441
+ pdf_path = pdf_path.replace('dl=0', 'dl=1')
2442
+
2443
+ # Cache frequently used values
2444
+ response = requests.get(pdf_path)
2445
+ pdf_content = BytesIO(response.content)
2446
+ if not pdf_content:
2447
+ raise ValueError("No valid PDF content found.")
2448
+
2449
+ doc = fitz.open(stream=pdf_content, filetype="pdf")
2450
+ docHighlights = fitz.open(stream=pdf_content, filetype="pdf")
2451
+ most_common_font_size, most_common_color, most_common_font = get_regular_font_size_and_color(doc)
2452
+
2453
+ # Precompute regex patterns
2454
+ dot_pattern = re.compile(r'\.{3,}')
2455
+ url_pattern = re.compile(r'https?://\S+|www\.\S+')
2456
+
2457
+ def get_toc_page_numbers(doc, max_pages_to_check=15):
2458
+ toc_pages = []
2459
+ for page_num in range(min(len(doc), max_pages_to_check)):
2460
+ page = doc.load_page(page_num)
2461
+ blocks = page.get_text("dict")["blocks"]
2462
+
2463
+ dot_line_count = 0
2464
+ for block in blocks:
2465
+ for line in block.get("lines", []):
2466
+ line_text = get_spaced_text_from_spans(line["spans"]).strip()
2467
+ if dot_pattern.search(line_text):
2468
+ dot_line_count += 1
2469
+
2470
+ if dot_line_count >= 3:
2471
+ toc_pages.append(page_num)
2472
+
2473
+ return list(range(0, toc_pages[-1] +1)) if toc_pages else toc_pages
2474
+
2475
+ toc_pages = get_toc_page_numbers(doc)
2476
+
2477
+ headers, top_3_font_sizes, smallest_font_size, headersSpans = extract_headers(
2478
+ doc, toc_pages, most_common_font_size, most_common_color, most_common_font, top_margin, bottom_margin
2479
+ )
2480
+
2481
+ hierarchy = build_header_hierarchy(doc, toc_pages, most_common_font_size, most_common_color, most_common_font)
2482
+ listofHeaderstoMarkup = get_leaf_headers_with_paths(hierarchy)
2483
+
2484
+ # Precompute all children headers once
2485
+ allchildrenheaders = [normalize_text(item['text']) for item, p in listofHeaderstoMarkup]
2486
+ allchildrenheaders_set = set(allchildrenheaders) # For faster lookups
2487
+
2488
+ # df = pd.DataFrame(columns=["NBSLink","Subject","Page","Author","Creation Date","Layer",'Code', 'head above 1', "head above 2","BodyText"])
2489
+ dictionaryNBS={}
2490
+ data_list_JSON = []
2491
+ currentgroupname=''
2492
+ if len(top_3_font_sizes)==3:
2493
+ mainHeaderFontSize, subHeaderFontSize, subsubheaderFontSize = top_3_font_sizes
2494
+ elif len(top_3_font_sizes)==2:
2495
+ mainHeaderFontSize= top_3_font_sizes[0]
2496
+ subHeaderFontSize= top_3_font_sizes[1]
2497
+ subsubheaderFontSize= top_3_font_sizes[1]
2498
+
2499
+
2500
+
2501
+ # Preload all pages to avoid repeated loading
2502
+ # pages = [doc.load_page(page_num) for page_num in range(len(doc)) if page_num not in toc_pages]
2503
+
2504
+ for heading_to_searchDict, paths in listofHeaderstoMarkup:
2505
+ heading_to_search = heading_to_searchDict['text']
2506
+ heading_to_searchPageNum = heading_to_searchDict['page']
2507
+
2508
+ # Initialize variables
2509
+ headertoContinue1 = False
2510
+ headertoContinue2 = False
2511
+ matched_header_line = None
2512
+ done = False
2513
+ collecting = False
2514
+ collected_lines = []
2515
+ page_highlights = {}
2516
+ current_bbox = {}
2517
+ last_y1s = {}
2518
+ mainHeader = ''
2519
+ subHeader = ''
2520
+ matched_header_line_norm = heading_to_search
2521
+ break_collecting = False
2522
+ heading_norm = normalize_text(heading_to_search)
2523
+ paths_norm = [normalize_text(p) for p in paths[0]] if paths and paths[0] else []
2524
+ for page_num in range(heading_to_searchPageNum,len(doc)):
2525
+ # print(heading_to_search)
2526
+ if paths[0].strip().lower() != currentgroupname.strip().lower():
2527
+ Alltexttobebilled+= paths[0] +'\n'
2528
+ currentgroupname=paths[0]
2529
+ # print(paths[0])
2530
+
2531
+
2532
+ if page_num in toc_pages:
2533
+ continue
2534
+ if break_collecting:
2535
+ break
2536
+ page=doc[page_num]
2537
+ page_height = page.rect.height
2538
+ blocks = page.get_text("dict")["blocks"]
2539
+
2540
+ for block in blocks:
2541
+ if break_collecting:
2542
+ break
2543
+
2544
+ lines = block.get("lines", [])
2545
+ i = 0
2546
+ while i < len(lines):
2547
+ if break_collecting:
2548
+ break
2549
+
2550
+ spans = lines[i].get("spans", [])
2551
+ if not spans:
2552
+ i += 1
2553
+ continue
2554
+
2555
+ y0 = spans[0]["bbox"][1]
2556
+ y1 = spans[0]["bbox"][3]
2557
+ if y0 < top_margin or y1 > (page_height - bottom_margin):
2558
+ i += 1
2559
+ continue
2560
+
2561
+ line_text = get_spaced_text_from_spans(spans).lower()
2562
+ line_text_norm = normalize_text(line_text)
2563
+
2564
+ # Combine with next line if available
2565
+ if i + 1 < len(lines):
2566
+ next_spans = lines[i + 1].get("spans", [])
2567
+ next_line_text = get_spaced_text_from_spans(next_spans).lower()
2568
+ combined_line_norm = normalize_text(line_text + " " + next_line_text)
2569
+ else:
2570
+ combined_line_norm = line_text_norm
2571
+
2572
+ # Check if we should continue processing
2573
+ if combined_line_norm and combined_line_norm in paths[0]:
2574
+
2575
+ headertoContinue1 = combined_line_norm
2576
+ if combined_line_norm and combined_line_norm in paths[-2]:
2577
+
2578
+ headertoContinue2 = combined_line_norm
2579
+ # if 'installation' in paths[-2].lower() or 'execution' in paths[-2].lower() or 'miscellaneous items' in paths[-2].lower() :
2580
+ last_path = paths[-2].lower()
2581
+ # if any(word in paths[-2].lower() for word in keywordstoSkip):
2582
+ # if 'installation' in paths[-2].lower() or 'execution' in paths[-2].lower() or 'miscellaneous items' in paths[-2].lower() or 'workmanship' in paths[-2].lower() or 'testing' in paths[-2].lower() or 'labeling' in paths[-2].lower():
2583
+ if any(keyword in last_path for keyword in keywords):
2584
+ stringtowrite='Not to be billed'
2585
+ else:
2586
+ stringtowrite='To be billed'
2587
+ if stringtowrite=='To be billed':
2588
+ # Alltexttobebilled+= combined_line_norm #################################################
2589
+ if matched_header_line_norm in combined_line_norm:
2590
+ Alltexttobebilled+='\n'
2591
+ Alltexttobebilled+= ' '+combined_line_norm
2592
+ # Optimized header matching
2593
+ existsfull = (
2594
+ ( combined_line_norm in allchildrenheaders_set or
2595
+ combined_line_norm in allchildrenheaders ) and heading_to_search in combined_line_norm
2596
+ )
2597
+
2598
+ # New word-based matching
2599
+ current_line_words = set(combined_line_norm.split())
2600
+ heading_words = set(heading_norm.split())
2601
+ all_words_match = current_line_words.issubset(heading_words) and len(current_line_words) > 0
2602
+
2603
+ substring_match = (
2604
+ heading_norm in combined_line_norm or
2605
+ combined_line_norm in heading_norm or
2606
+ all_words_match # Include the new word-based matching
2607
+ )
2608
+ # substring_match = (
2609
+ # heading_norm in combined_line_norm or
2610
+ # combined_line_norm in heading_norm
2611
+ # )
2612
+
2613
+ if (substring_match and existsfull and not collecting and
2614
+ len(combined_line_norm) > 0 ):#and (headertoContinue1 or headertoContinue2) ):
2615
+
2616
+ # Check header conditions more efficiently
2617
+ header_spans = [
2618
+ span for span in spans
2619
+ if (is_header(span, most_common_font_size, most_common_color, most_common_font)
2620
+ # and span['size'] >= subsubheaderFontSize
2621
+ and span['size'] < mainHeaderFontSize)
2622
+ ]
2623
+ if header_spans and stringtowrite.startswith('To'):
2624
+ collecting = True
2625
+ # if stringtowrite=='To be billed':
2626
+ # Alltexttobebilled+='\n'
2627
+ matched_header_font_size = max(span["size"] for span in header_spans)
2628
+
2629
+ # collected_lines.append(line_text)
2630
+ valid_spans = [span for span in spans if span.get("bbox")]
2631
+
2632
+ if valid_spans:
2633
+ x0s = [span["bbox"][0] for span in valid_spans]
2634
+ x1s = [span["bbox"][2] for span in valid_spans]
2635
+ y0s = [span["bbox"][1] for span in valid_spans]
2636
+ y1s = [span["bbox"][3] for span in valid_spans]
2637
+
2638
+ header_bbox = [min(x0s), min(y0s), max(x1s), max(y1s)]
2639
+
2640
+ if page_num in current_bbox:
2641
+ cb = current_bbox[page_num]
2642
+ current_bbox[page_num] = [
2643
+ min(cb[0], header_bbox[0]),
2644
+ min(cb[1], header_bbox[1]),
2645
+ max(cb[2], header_bbox[2]),
2646
+ max(cb[3], header_bbox[3])
2647
+ ]
2648
+ else:
2649
+ current_bbox[page_num] = header_bbox
2650
+ last_y1s[page_num] = header_bbox[3]
2651
+ x0, y0, x1, y1 = header_bbox
2652
+
2653
+ zoom = 200
2654
+ left = int(x0)
2655
+ top = int(y0)
2656
+ zoom_str = f"{zoom},{left},{top}"
2657
+ pageNumberFound = page_num + 1
2658
+
2659
+ # Build the query parameters
2660
+ params = {
2661
+ 'pdfLink': pdf_path, # Your PDF link
2662
+ 'keyword': heading_to_search, # Your keyword (could be a string or list)
2663
+ }
2664
+
2665
+ # URL encode each parameter
2666
+ encoded_params = {key: urllib.parse.quote(value, safe='') for key, value in params.items()}
2667
+
2668
+ # Construct the final encoded link
2669
+ encoded_link = '&'.join([f"{key}={value}" for key, value in encoded_params.items()])
2670
+
2671
+ # Correctly construct the final URL with page and zoom
2672
+ final_url = f"{baselink}{encoded_link}#page={str(pageNumberFound)}&zoom={zoom_str}"
2673
+
2674
+ # Get current date and time
2675
+ now = datetime.now()
2676
+
2677
+ # Format the output
2678
+ formatted_time = now.strftime("%d/%m/%Y %I:%M:%S %p")
2679
+ # Optionally, add the URL to a DataFrame
2680
+
2681
+
2682
+ data_entry = {
2683
+ "PDF Name":filename,
2684
+ "NBSLink": final_url,
2685
+ "Subject": heading_to_search,
2686
+ "Page": str(pageNumberFound),
2687
+ "Author": "ADR",
2688
+ "Creation Date": formatted_time,
2689
+ "Layer": "Initial",
2690
+ "Code": stringtowrite,
2691
+ "head above 1": paths[-2],
2692
+ "head above 2": paths[0],
2693
+ "BodyText":collected_lines,
2694
+ "MC Connnection": 'Go to ' + paths[0].strip().split()[0] +'/'+ heading_to_search.strip().split()[0] + ' in '+ filename
2695
+ }
2696
+ data_list_JSON.append(data_entry)
2697
+
2698
+ # Convert list to JSON
2699
+ json_output = json.dumps(data_list_JSON, indent=4)
2700
+
2701
+ i += 2
2702
+ continue
2703
+ else:
2704
+ if (substring_match and not collecting and
2705
+ len(combined_line_norm) > 0): # and (headertoContinue1 or headertoContinue2) ):
2706
+
2707
+ # Calculate word match percentage
2708
+ word_match_percent = words_match_ratio(heading_norm, combined_line_norm) * 100
2709
+
2710
+ # Check if at least 70% of header words exist in this line
2711
+ meets_word_threshold = word_match_percent >= 100
2712
+
2713
+ # Check header conditions (including word threshold)
2714
+ header_spans = [
2715
+ span for span in spans
2716
+ if (is_header(span, most_common_font_size, most_common_color, most_common_font)
2717
+ # and span['size'] >= subsubheaderFontSize
2718
+ and span['size'] < mainHeaderFontSize)
2719
+ ]
2720
+
2721
+ if header_spans and (meets_word_threshold or same_start_word(heading_to_search, combined_line_norm) ) and stringtowrite.startswith('To'):
2722
+ collecting = True
2723
+ if stringtowrite=='To be billed':
2724
+ Alltexttobebilled+='\n'
2725
+ # if stringtowrite=='To be billed':
2726
+ # Alltexttobebilled+= ' '+ combined_line_norm
2727
+ matched_header_font_size = max(span["size"] for span in header_spans)
2728
+
2729
+ collected_lines.append(line_text)
2730
+ valid_spans = [span for span in spans if span.get("bbox")]
2731
+
2732
+ if valid_spans:
2733
+ x0s = [span["bbox"][0] for span in valid_spans]
2734
+ x1s = [span["bbox"][2] for span in valid_spans]
2735
+ y0s = [span["bbox"][1] for span in valid_spans]
2736
+ y1s = [span["bbox"][3] for span in valid_spans]
2737
+
2738
+ header_bbox = [min(x0s), min(y0s), max(x1s), max(y1s)]
2739
+
2740
+ if page_num in current_bbox:
2741
+ cb = current_bbox[page_num]
2742
+ current_bbox[page_num] = [
2743
+ min(cb[0], header_bbox[0]),
2744
+ min(cb[1], header_bbox[1]),
2745
+ max(cb[2], header_bbox[2]),
2746
+ max(cb[3], header_bbox[3])
2747
+ ]
2748
+ else:
2749
+ current_bbox[page_num] = header_bbox
2750
+
2751
+ last_y1s[page_num] = header_bbox[3]
2752
+ x0, y0, x1, y1 = header_bbox
2753
+ zoom = 200
2754
+ left = int(x0)
2755
+ top = int(y0)
2756
+ zoom_str = f"{zoom},{left},{top}"
2757
+ pageNumberFound = page_num + 1
2758
+
2759
+ # Build the query parameters
2760
+ params = {
2761
+ 'pdfLink': pdf_path, # Your PDF link
2762
+ 'keyword': heading_to_search, # Your keyword (could be a string or list)
2763
+ }
2764
+
2765
+ # URL encode each parameter
2766
+ encoded_params = {key: urllib.parse.quote(value, safe='') for key, value in params.items()}
2767
+
2768
+ # Construct the final encoded link
2769
+ encoded_link = '&'.join([f"{key}={value}" for key, value in encoded_params.items()])
2770
+
2771
+ # Correctly construct the final URL with page and zoom
2772
+ final_url = f"{baselink}{encoded_link}#page={str(pageNumberFound)}&zoom={zoom_str}"
2773
+
2774
+ # Get current date and time
2775
+ now = datetime.now()
2776
+
2777
+ # Format the output
2778
+ formatted_time = now.strftime("%d/%m/%Y %I:%M:%S %p")
2779
+ # Optionally, add the URL to a DataFrame
2780
+
2781
+
2782
+ data_entry = {
2783
+ "PDF Name":filename,
2784
+ "NBSLink": final_url,
2785
+ "Subject": heading_to_search,
2786
+ "Page": str(pageNumberFound),
2787
+ "Author": "ADR",
2788
+ "Creation Date": formatted_time,
2789
+ "Layer": "Initial",
2790
+ "Code": stringtowrite,
2791
+ "head above 1": paths[-2],
2792
+ "head above 2": paths[0],
2793
+ "BodyText":collected_lines,
2794
+ "MC Connnection": 'Go to ' + paths[0].strip().split()[0] +'/'+ heading_to_search.strip().split()[0] + ' in '+ filename
2795
+ }
2796
+ data_list_JSON.append(data_entry)
2797
+
2798
+ # Convert list to JSON
2799
+ json_output = json.dumps(data_list_JSON, indent=4)
2800
+
2801
+
2802
+ i += 2
2803
+ continue
2804
+ if collecting:
2805
+ norm_line = normalize_text(line_text)
2806
+
2807
+ # Optimized URL check
2808
+ if url_pattern.match(norm_line):
2809
+ line_is_header = False
2810
+ else:
2811
+ line_is_header = any(is_header(span, most_common_font_size, most_common_color, most_common_font) for span in spans)
2812
+
2813
+ if line_is_header:
2814
+ header_font_size = max(span["size"] for span in spans)
2815
+ is_probably_real_header = (
2816
+ header_font_size >= matched_header_font_size and
2817
+ is_header(spans[0], most_common_font_size, most_common_color, most_common_font) and
2818
+ len(line_text.strip()) > 2
2819
+ )
2820
+
2821
+ if (norm_line != matched_header_line_norm and
2822
+ norm_line != heading_norm and
2823
+ is_probably_real_header):
2824
+ if line_text not in heading_norm:
2825
+ collecting = False
2826
+ done = True
2827
+ headertoContinue1 = False
2828
+ headertoContinue2=False
2829
+ for page_num, bbox in current_bbox.items():
2830
+ bbox[3] = last_y1s.get(page_num, bbox[3])
2831
+ page_highlights[page_num] = bbox
2832
+ highlight_boxes(docHighlights, page_highlights,stringtowrite)
2833
+
2834
+ break_collecting = True
2835
+ break
2836
+
2837
+ if break_collecting:
2838
+ break
2839
+
2840
+ collected_lines.append(line_text)
2841
+ valid_spans = [span for span in spans if span.get("bbox")]
2842
+ if valid_spans:
2843
+ x0s = [span["bbox"][0] for span in valid_spans]
2844
+ x1s = [span["bbox"][2] for span in valid_spans]
2845
+ y0s = [span["bbox"][1] for span in valid_spans]
2846
+ y1s = [span["bbox"][3] for span in valid_spans]
2847
+
2848
+ line_bbox = [min(x0s), min(y0s), max(x1s), max(y1s)]
2849
+
2850
+ if page_num in current_bbox:
2851
+ cb = current_bbox[page_num]
2852
+ current_bbox[page_num] = [
2853
+ min(cb[0], line_bbox[0]),
2854
+ min(cb[1], line_bbox[1]),
2855
+ max(cb[2], line_bbox[2]),
2856
+ max(cb[3], line_bbox[3])
2857
+ ]
2858
+ else:
2859
+ current_bbox[page_num] = line_bbox
2860
+
2861
+ last_y1s[page_num] = line_bbox[3]
2862
+ i += 1
2863
+
2864
+ if not done:
2865
+ for page_num, bbox in current_bbox.items():
2866
+ bbox[3] = last_y1s.get(page_num, bbox[3])
2867
+ page_highlights[page_num] = bbox
2868
+ if 'installation' in paths[-2].lower() or 'execution' in paths[-2].lower() or 'miscellaneous items' in paths[-2].lower() :
2869
+ stringtowrite='Not to be billed'
2870
+ else:
2871
+ stringtowrite='To be billed'
2872
+ highlight_boxes(docHighlights, page_highlights,stringtowrite)
2873
+
2874
+ # docHighlights.save("highlighted_output.pdf", garbage=4, deflate=True)
2875
+
2876
+ dbxTeam = tsadropboxretrieval.ADR_Access_DropboxTeam('user')
2877
+ metadata = dbxTeam.sharing_get_shared_link_metadata(pdf_path)
2878
+ dbPath = '/TSA JOBS/ADR Test/FIND/'
2879
+ pdf_bytes = BytesIO()
2880
+ docHighlights.save(pdf_bytes)
2881
+ pdflink = tsadropboxretrieval.uploadanyFile(doc=docHighlights, path=dbPath, pdfname=filename)
2882
+ json_output=changepdflinks(json_output,pdflink)
2883
+ return pdf_bytes.getvalue(), docHighlights , json_output, Alltexttobebilled , filenames
2884
+