Marthee commited on
Commit
e2ef6e1
·
verified ·
1 Parent(s): c856399

Update InitialMarkups.py

Browse files
Files changed (1) hide show
  1. InitialMarkups.py +320 -306
InitialMarkups.py CHANGED
@@ -649,283 +649,200 @@ def same_start_word(s1, s2):
649
  return False
650
 
651
 
652
- def extract_section_under_header(pdf_path):
653
- top_margin = 70
654
- bottom_margin = 50
655
- headertoContinue1 = False
656
- headertoContinue2=False
657
-
658
- parsed_url = urlparse(pdf_path)
659
- filename = os.path.basename(parsed_url.path)
660
- filename = unquote(filename) # decode URL-encoded characters
661
 
662
- # Optimized URL handling
663
- if pdf_path and ('http' in pdf_path or 'dropbox' in pdf_path):
664
- pdf_path = pdf_path.replace('dl=0', 'dl=1')
665
 
666
- # Cache frequently used values
667
- response = requests.get(pdf_path)
668
- pdf_content = BytesIO(response.content)
669
- if not pdf_content:
670
- raise ValueError("No valid PDF content found.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
671
 
672
- doc = fitz.open(stream=pdf_content, filetype="pdf")
673
- docHighlights = fitz.open(stream=pdf_content, filetype="pdf")
674
- most_common_font_size, most_common_color, most_common_font = get_regular_font_size_and_color(doc)
 
 
675
 
676
- # Precompute regex patterns
677
- dot_pattern = re.compile(r'\.{3,}')
678
- url_pattern = re.compile(r'https?://\S+|www\.\S+')
679
 
680
- def get_toc_page_numbers(doc, max_pages_to_check=15):
681
- toc_pages = []
682
- for page_num in range(min(len(doc), max_pages_to_check)):
683
- page = doc.load_page(page_num)
684
- blocks = page.get_text("dict")["blocks"]
685
 
686
- dot_line_count = 0
687
- for block in blocks:
688
- for line in block.get("lines", []):
689
- line_text = get_spaced_text_from_spans(line["spans"]).strip()
690
- if dot_pattern.search(line_text):
691
- dot_line_count += 1
692
 
693
- if dot_line_count >= 3:
694
- toc_pages.append(page_num)
 
 
 
 
695
 
696
- return list(range(0, toc_pages[-1] +1)) if toc_pages else toc_pages
 
697
 
698
- toc_pages = get_toc_page_numbers(doc)
699
 
700
- headers, top_3_font_sizes, smallest_font_size, headersSpans = extract_headers(
701
- doc, toc_pages, most_common_font_size, most_common_color, most_common_font, top_margin, bottom_margin
702
- )
703
 
704
- hierarchy = build_header_hierarchy(doc, toc_pages, most_common_font_size, most_common_color, most_common_font)
705
- listofHeaderstoMarkup = get_leaf_headers_with_paths(hierarchy)
706
-
707
- # Precompute all children headers once
708
- allchildrenheaders = [normalize_text(item['text']) for item, p in listofHeaderstoMarkup]
709
- allchildrenheaders_set = set(allchildrenheaders) # For faster lookups
710
 
711
- df = pd.DataFrame(columns=["NBSLink","Subject","Page","Author","Creation Date","Layer",'Code', 'head above 1', "head above 2"])
712
- dictionaryNBS={}
713
- data_list_JSON = []
 
 
 
714
 
715
- if len(top_3_font_sizes)==3:
716
- mainHeaderFontSize, subHeaderFontSize, subsubheaderFontSize = top_3_font_sizes
717
- elif len(top_3_font_sizes)==2:
718
- mainHeaderFontSize= top_3_font_sizes[0]
719
- subHeaderFontSize= top_3_font_sizes[1]
720
- subsubheaderFontSize= top_3_font_sizes[1]
721
 
722
-
 
 
 
 
 
723
 
724
- # Preload all pages to avoid repeated loading
725
- # pages = [doc.load_page(page_num) for page_num in range(len(doc)) if page_num not in toc_pages]
726
 
727
- for heading_to_searchDict, paths in listofHeaderstoMarkup:
728
- heading_to_search = heading_to_searchDict['text']
729
- heading_to_searchPageNum = heading_to_searchDict['page']
730
 
731
- # Initialize variables
732
- headertoContinue1 = False
733
- headertoContinue2 = False
734
- matched_header_line = None
735
- done = False
736
- collecting = False
737
- collected_lines = []
738
- page_highlights = {}
739
- current_bbox = {}
740
- last_y1s = {}
741
- mainHeader = ''
742
- subHeader = ''
743
- matched_header_line_norm = heading_to_search
744
- break_collecting = False
745
- heading_norm = normalize_text(heading_to_search)
746
- paths_norm = [normalize_text(p) for p in paths[0]] if paths and paths[0] else []
747
 
748
- for page_num in range(heading_to_searchPageNum,len(doc)):
749
- if page_num in toc_pages:
750
- continue
751
- if break_collecting:
752
- break
753
- page=doc[page_num]
754
- page_height = page.rect.height
755
- blocks = page.get_text("dict")["blocks"]
 
 
 
 
 
 
 
 
756
 
757
- for block in blocks:
 
 
758
  if break_collecting:
759
  break
 
 
 
760
 
761
- lines = block.get("lines", [])
762
- i = 0
763
- while i < len(lines):
764
  if break_collecting:
765
  break
766
 
767
- spans = lines[i].get("spans", [])
768
- if not spans:
769
- i += 1
770
- continue
771
-
772
- y0 = spans[0]["bbox"][1]
773
- y1 = spans[0]["bbox"][3]
774
- if y0 < top_margin or y1 > (page_height - bottom_margin):
775
- i += 1
776
- continue
777
-
778
- line_text = get_spaced_text_from_spans(spans).lower()
779
- line_text_norm = normalize_text(line_text)
780
-
781
- # Combine with next line if available
782
- if i + 1 < len(lines):
783
- next_spans = lines[i + 1].get("spans", [])
784
- next_line_text = get_spaced_text_from_spans(next_spans).lower()
785
- combined_line_norm = normalize_text(line_text + " " + next_line_text)
786
- else:
787
- combined_line_norm = line_text_norm
788
-
789
- # Check if we should continue processing
790
- if combined_line_norm and combined_line_norm in paths[0]:
791
-
792
- headertoContinue1 = combined_line_norm
793
- if combined_line_norm and combined_line_norm in paths[-2]:
794
-
795
- headertoContinue2 = combined_line_norm
796
- if 'installation' in paths[-2].lower() or 'execution' in paths[-2].lower() or 'miscellaneous items' in paths[-2].lower() :
797
- stringtowrite='Not to be billed'
798
- else:
799
- stringtowrite='To be billed'
800
- # Optimized header matching
801
- existsfull = (
802
- ( combined_line_norm in allchildrenheaders_set or
803
- combined_line_norm in allchildrenheaders ) and heading_to_search in combined_line_norm
804
- )
805
-
806
- # New word-based matching
807
- current_line_words = set(combined_line_norm.split())
808
- heading_words = set(heading_norm.split())
809
- all_words_match = current_line_words.issubset(heading_words) and len(current_line_words) > 0
810
-
811
- substring_match = (
812
- heading_norm in combined_line_norm or
813
- combined_line_norm in heading_norm or
814
- all_words_match # Include the new word-based matching
815
- )
816
- # substring_match = (
817
- # heading_norm in combined_line_norm or
818
- # combined_line_norm in heading_norm
819
- # )
820
-
821
- if (substring_match and existsfull and not collecting and
822
- len(combined_line_norm) > 0 ):#and (headertoContinue1 or headertoContinue2) ):
823
-
824
- # Check header conditions more efficiently
825
- header_spans = [
826
- span for span in spans
827
- if (is_header(span, most_common_font_size, most_common_color, most_common_font)
828
- # and span['size'] >= subsubheaderFontSize
829
- and span['size'] < mainHeaderFontSize)
830
- ]
831
- if header_spans:
832
- collecting = True
833
- matched_header_font_size = max(span["size"] for span in header_spans)
834
-
835
- collected_lines.append(line_text)
836
- valid_spans = [span for span in spans if span.get("bbox")]
837
-
838
- if valid_spans:
839
- x0s = [span["bbox"][0] for span in valid_spans]
840
- x1s = [span["bbox"][2] for span in valid_spans]
841
- y0s = [span["bbox"][1] for span in valid_spans]
842
- y1s = [span["bbox"][3] for span in valid_spans]
843
-
844
- header_bbox = [min(x0s), min(y0s), max(x1s), max(y1s)]
845
-
846
- if page_num in current_bbox:
847
- cb = current_bbox[page_num]
848
- current_bbox[page_num] = [
849
- min(cb[0], header_bbox[0]),
850
- min(cb[1], header_bbox[1]),
851
- max(cb[2], header_bbox[2]),
852
- max(cb[3], header_bbox[3])
853
- ]
854
- else:
855
- current_bbox[page_num] = header_bbox
856
- last_y1s[page_num] = header_bbox[3]
857
- x0, y0, x1, y1 = header_bbox
858
-
859
- zoom = 200
860
- left = int(x0)
861
- top = int(y0)
862
- zoom_str = f"{zoom},{left},{top}"
863
- pageNumberFound = page_num + 1
864
-
865
- # Build the query parameters
866
- params = {
867
- 'pdfLink': pdf_path, # Your PDF link
868
- 'keyword': heading_to_search, # Your keyword (could be a string or list)
869
- }
870
-
871
- # URL encode each parameter
872
- encoded_params = {key: urllib.parse.quote(value, safe='') for key, value in params.items()}
873
-
874
- # Construct the final encoded link
875
- encoded_link = '&'.join([f"{key}={value}" for key, value in encoded_params.items()])
876
-
877
- # Correctly construct the final URL with page and zoom
878
- final_url = f"{baselink}{encoded_link}#page={str(pageNumberFound)}&zoom={zoom_str}"
879
 
880
- # Get current date and time
881
- now = datetime.now()
 
 
882
 
883
- # Format the output
884
- formatted_time = now.strftime("%d/%m/%Y %I:%M:%S %p")
885
- # Optionally, add the URL to a DataFrame
 
 
886
 
 
 
887
 
888
- data_entry = {
889
- "NBSLink": final_url,
890
- "Subject": heading_to_search,
891
- "Page": str(pageNumberFound),
892
- "Author": "ADR",
893
- "Creation Date": formatted_time,
894
- "Layer": "Initial",
895
- "Code": stringtowrite,
896
- "head above 1": paths[-2],
897
- "head above 2": paths[0],
898
- "MC Connnection": 'Go to ' + paths[0].strip().split()[0] +'/'+ heading_to_search.strip().split()[0] + ' in '+ filename
899
- }
900
- data_list_JSON.append(data_entry)
901
 
902
- # Convert list to JSON
903
- json_output = json.dumps(data_list_JSON, indent=4)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
904
 
905
- i += 2
906
- continue
907
- else:
908
- if (substring_match and not collecting and
909
- len(combined_line_norm) > 0): # and (headertoContinue1 or headertoContinue2) ):
910
 
911
- # Calculate word match percentage
912
- word_match_percent = words_match_ratio(heading_norm, combined_line_norm) * 100
 
 
 
 
 
 
 
913
 
914
- # Check if at least 70% of header words exist in this line
915
- meets_word_threshold = word_match_percent >= 100
916
 
917
- # Check header conditions (including word threshold)
918
  header_spans = [
919
  span for span in spans
920
  if (is_header(span, most_common_font_size, most_common_color, most_common_font)
921
  # and span['size'] >= subsubheaderFontSize
922
  and span['size'] < mainHeaderFontSize)
923
  ]
924
-
925
- if header_spans and (meets_word_threshold or same_start_word(heading_to_search, combined_line_norm) ):
926
  collecting = True
927
  matched_header_font_size = max(span["size"] for span in header_spans)
928
-
929
  collected_lines.append(line_text)
930
  valid_spans = [span for span in spans if span.get("bbox")]
931
 
@@ -947,16 +864,16 @@ def extract_section_under_header(pdf_path):
947
  ]
948
  else:
949
  current_bbox[page_num] = header_bbox
950
-
951
  last_y1s[page_num] = header_bbox[3]
952
  x0, y0, x1, y1 = header_bbox
 
953
  zoom = 200
954
  left = int(x0)
955
  top = int(y0)
956
  zoom_str = f"{zoom},{left},{top}"
957
  pageNumberFound = page_num + 1
958
 
959
- # Build the query parameters
960
  params = {
961
  'pdfLink': pdf_path, # Your PDF link
962
  'keyword': heading_to_search, # Your keyword (could be a string or list)
@@ -994,94 +911,191 @@ def extract_section_under_header(pdf_path):
994
  data_list_JSON.append(data_entry)
995
 
996
  # Convert list to JSON
997
- json_output = json.dumps(data_list_JSON, indent=4)
998
 
999
-
1000
  i += 2
1001
  continue
1002
- if collecting:
1003
- norm_line = normalize_text(line_text)
1004
-
1005
- # Optimized URL check
1006
- if url_pattern.match(norm_line):
1007
- line_is_header = False
1008
  else:
1009
- line_is_header = any(is_header(span, most_common_font_size, most_common_color, most_common_font) for span in spans)
 
1010
 
1011
- if line_is_header:
1012
- header_font_size = max(span["size"] for span in spans)
1013
- is_probably_real_header = (
1014
- header_font_size >= matched_header_font_size and
1015
- is_header(spans[0], most_common_font_size, most_common_color, most_common_font) and
1016
- len(line_text.strip()) > 2
1017
- )
1018
 
1019
- if (norm_line != matched_header_line_norm and
1020
- norm_line != heading_norm and
1021
- is_probably_real_header):
1022
- if line_text not in heading_norm:
1023
- collecting = False
1024
- done = True
1025
- headertoContinue1 = False
1026
- headertoContinue2=False
1027
- for page_num, bbox in current_bbox.items():
1028
- bbox[3] = last_y1s.get(page_num, bbox[3])
1029
- page_highlights[page_num] = bbox
1030
- highlight_boxes(docHighlights, page_highlights,stringtowrite)
1031
 
1032
- break_collecting = True
1033
- break
 
 
 
 
 
1034
 
1035
- if break_collecting:
1036
- break
 
 
 
 
1037
 
1038
- collected_lines.append(line_text)
1039
- valid_spans = [span for span in spans if span.get("bbox")]
1040
- if valid_spans:
1041
- x0s = [span["bbox"][0] for span in valid_spans]
1042
- x1s = [span["bbox"][2] for span in valid_spans]
1043
- y0s = [span["bbox"][1] for span in valid_spans]
1044
- y1s = [span["bbox"][3] for span in valid_spans]
1045
 
1046
- line_bbox = [min(x0s), min(y0s), max(x1s), max(y1s)]
1047
 
1048
- if page_num in current_bbox:
1049
- cb = current_bbox[page_num]
1050
- current_bbox[page_num] = [
1051
- min(cb[0], line_bbox[0]),
1052
- min(cb[1], line_bbox[1]),
1053
- max(cb[2], line_bbox[2]),
1054
- max(cb[3], line_bbox[3])
1055
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1056
  else:
1057
- current_bbox[page_num] = line_bbox
1058
 
1059
- last_y1s[page_num] = line_bbox[3]
1060
- i += 1
 
 
 
 
 
1061
 
1062
- if not done:
1063
- for page_num, bbox in current_bbox.items():
1064
- bbox[3] = last_y1s.get(page_num, bbox[3])
1065
- page_highlights[page_num] = bbox
1066
- if 'installation' in paths[-2].lower() or 'execution' in paths[-2].lower() or 'miscellaneous items' in paths[-2].lower() :
1067
- stringtowrite='Not to be billed'
1068
- else:
1069
- stringtowrite='To be billed'
1070
- highlight_boxes(docHighlights, page_highlights,stringtowrite)
 
 
 
 
 
 
1071
 
1072
- # docHighlights.save("highlighted_output.pdf", garbage=4, deflate=True)
1073
- dbxTeam = tsadropboxretrieval.ADR_Access_DropboxTeam('user')
1074
- metadata = dbxTeam.sharing_get_shared_link_metadata(pdf_path)
1075
- dbPath = '/TSA JOBS/ADR Test/FIND/'
1076
- pdf_bytes = BytesIO()
1077
- docHighlights.save(pdf_bytes)
1078
- pdflink = tsadropboxretrieval.uploadanyFile(doc=docHighlights, path=dbPath, pdfname=filename)
1079
- json_output=changepdflinks(json_output,pdflink)
1080
- return pdf_bytes.getvalue(), docHighlights , json_output
 
 
 
1081
 
 
 
 
 
 
 
 
 
 
 
1082
 
 
 
1083
 
 
 
 
 
 
 
 
 
 
1084
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1085
 
1086
  ########################################################################################################################################################
1087
  ########################################################################################################################################################
 
649
  return False
650
 
651
 
 
 
 
 
 
 
 
 
 
652
 
 
 
 
653
 
654
+ def extract_section_under_header(multiplePDF_Paths):
655
+ filenames=[]
656
+ keywords = {'installation', 'execution', 'miscellaneous items', 'workmanship', 'testing', 'labeling'}
657
+ top_margin = 70
658
+ bottom_margin = 50
659
+ arrayofPDFS=multiplePDF_Paths.split(',')
660
+ print(multiplePDF_Paths)
661
+ print(arrayofPDFS)
662
+ docarray=[]
663
+ jsons=[]
664
+ df = pd.DataFrame(columns=["PDF Name","NBSLink","Subject","Page","Author","Creation Date","Layer",'Code', 'head above 1', "head above 2","BodyText"])
665
+ for pdf_path in arrayofPDFS:
666
+ headertoContinue1 = False
667
+ headertoContinue2=False
668
+ Alltexttobebilled=''
669
+ parsed_url = urlparse(pdf_path)
670
+ filename = os.path.basename(parsed_url.path)
671
+ filename = unquote(filename) # decode URL-encoded characters
672
+ filenames.append(filename)
673
+ # Optimized URL handling
674
+ if pdf_path and ('http' in pdf_path or 'dropbox' in pdf_path):
675
+ pdf_path = pdf_path.replace('dl=0', 'dl=1')
676
 
677
+ # Cache frequently used values
678
+ response = requests.get(pdf_path)
679
+ pdf_content = BytesIO(response.content)
680
+ if not pdf_content:
681
+ raise ValueError("No valid PDF content found.")
682
 
683
+ doc = fitz.open(stream=pdf_content, filetype="pdf")
684
+ docHighlights = fitz.open(stream=pdf_content, filetype="pdf")
685
+ most_common_font_size, most_common_color, most_common_font = get_regular_font_size_and_color(doc)
686
 
687
+ # Precompute regex patterns
688
+ dot_pattern = re.compile(r'\.{3,}')
689
+ url_pattern = re.compile(r'https?://\S+|www\.\S+')
 
 
690
 
691
+ def get_toc_page_numbers(doc, max_pages_to_check=15):
692
+ toc_pages = []
693
+ for page_num in range(min(len(doc), max_pages_to_check)):
694
+ page = doc.load_page(page_num)
695
+ blocks = page.get_text("dict")["blocks"]
 
696
 
697
+ dot_line_count = 0
698
+ for block in blocks:
699
+ for line in block.get("lines", []):
700
+ line_text = get_spaced_text_from_spans(line["spans"]).strip()
701
+ if dot_pattern.search(line_text):
702
+ dot_line_count += 1
703
 
704
+ if dot_line_count >= 1:
705
+ toc_pages.append(page_num)
706
 
707
+ return list(range(0, toc_pages[-1] +1)) if toc_pages else toc_pages
708
 
709
+ toc_pages = get_toc_page_numbers(doc)
 
 
710
 
711
+ headers, top_3_font_sizes, smallest_font_size, headersSpans = extract_headers(
712
+ doc, toc_pages, most_common_font_size, most_common_color, most_common_font, top_margin, bottom_margin
713
+ )
 
 
 
714
 
715
+ hierarchy = build_header_hierarchy(doc, toc_pages, most_common_font_size, most_common_color, most_common_font)
716
+ listofHeaderstoMarkup = get_leaf_headers_with_paths(hierarchy)
717
+
718
+ # Precompute all children headers once
719
+ allchildrenheaders = [normalize_text(item['text']) for item, p in listofHeaderstoMarkup]
720
+ allchildrenheaders_set = set(allchildrenheaders) # For faster lookups
721
 
722
+ df = pd.DataFrame(columns=["NBSLink","Subject","Page","Author","Creation Date","Layer",'Code', 'head above 1', "head above 2"])
723
+ dictionaryNBS={}
724
+ data_list_JSON = []
 
 
 
725
 
726
+ if len(top_3_font_sizes)==3:
727
+ mainHeaderFontSize, subHeaderFontSize, subsubheaderFontSize = top_3_font_sizes
728
+ elif len(top_3_font_sizes)==2:
729
+ mainHeaderFontSize= top_3_font_sizes[0]
730
+ subHeaderFontSize= top_3_font_sizes[1]
731
+ subsubheaderFontSize= top_3_font_sizes[1]
732
 
733
+
 
734
 
735
+ # Preload all pages to avoid repeated loading
736
+ # pages = [doc.load_page(page_num) for page_num in range(len(doc)) if page_num not in toc_pages]
 
737
 
738
+ for heading_to_searchDict, paths in listofHeaderstoMarkup:
739
+ heading_to_search = heading_to_searchDict['text']
740
+ heading_to_searchPageNum = heading_to_searchDict['page']
 
 
 
 
 
 
 
 
 
 
 
 
 
741
 
742
+ # Initialize variables
743
+ headertoContinue1 = False
744
+ headertoContinue2 = False
745
+ matched_header_line = None
746
+ done = False
747
+ collecting = False
748
+ collected_lines = []
749
+ page_highlights = {}
750
+ current_bbox = {}
751
+ last_y1s = {}
752
+ mainHeader = ''
753
+ subHeader = ''
754
+ matched_header_line_norm = heading_to_search
755
+ break_collecting = False
756
+ heading_norm = normalize_text(heading_to_search)
757
+ paths_norm = [normalize_text(p) for p in paths[0]] if paths and paths[0] else []
758
 
759
+ for page_num in range(heading_to_searchPageNum,len(doc)):
760
+ if page_num in toc_pages:
761
+ continue
762
  if break_collecting:
763
  break
764
+ page=doc[page_num]
765
+ page_height = page.rect.height
766
+ blocks = page.get_text("dict")["blocks"]
767
 
768
+ for block in blocks:
 
 
769
  if break_collecting:
770
  break
771
 
772
+ lines = block.get("lines", [])
773
+ i = 0
774
+ while i < len(lines):
775
+ if break_collecting:
776
+ break
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
777
 
778
+ spans = lines[i].get("spans", [])
779
+ if not spans:
780
+ i += 1
781
+ continue
782
 
783
+ y0 = spans[0]["bbox"][1]
784
+ y1 = spans[0]["bbox"][3]
785
+ if y0 < top_margin or y1 > (page_height - bottom_margin):
786
+ i += 1
787
+ continue
788
 
789
+ line_text = get_spaced_text_from_spans(spans).lower()
790
+ line_text_norm = normalize_text(line_text)
791
 
792
+ # Combine with next line if available
793
+ if i + 1 < len(lines):
794
+ next_spans = lines[i + 1].get("spans", [])
795
+ next_line_text = get_spaced_text_from_spans(next_spans).lower()
796
+ combined_line_norm = normalize_text(line_text + " " + next_line_text)
797
+ else:
798
+ combined_line_norm = line_text_norm
 
 
 
 
 
 
799
 
800
+ # Check if we should continue processing
801
+ if combined_line_norm and combined_line_norm in paths[0]:
802
+
803
+ headertoContinue1 = combined_line_norm
804
+ if combined_line_norm and combined_line_norm in paths[-2]:
805
+
806
+ headertoContinue2 = combined_line_norm
807
+ if 'installation' in paths[-2].lower() or 'execution' in paths[-2].lower() or 'miscellaneous items' in paths[-2].lower() :
808
+ stringtowrite='Not to be billed'
809
+ else:
810
+ stringtowrite='To be billed'
811
+ # Optimized header matching
812
+ existsfull = (
813
+ ( combined_line_norm in allchildrenheaders_set or
814
+ combined_line_norm in allchildrenheaders ) and heading_to_search in combined_line_norm
815
+ )
816
 
817
+ # New word-based matching
818
+ current_line_words = set(combined_line_norm.split())
819
+ heading_words = set(heading_norm.split())
820
+ all_words_match = current_line_words.issubset(heading_words) and len(current_line_words) > 0
 
821
 
822
+ substring_match = (
823
+ heading_norm in combined_line_norm or
824
+ combined_line_norm in heading_norm or
825
+ all_words_match # Include the new word-based matching
826
+ )
827
+ # substring_match = (
828
+ # heading_norm in combined_line_norm or
829
+ # combined_line_norm in heading_norm
830
+ # )
831
 
832
+ if (substring_match and existsfull and not collecting and
833
+ len(combined_line_norm) > 0 ):#and (headertoContinue1 or headertoContinue2) ):
834
 
835
+ # Check header conditions more efficiently
836
  header_spans = [
837
  span for span in spans
838
  if (is_header(span, most_common_font_size, most_common_color, most_common_font)
839
  # and span['size'] >= subsubheaderFontSize
840
  and span['size'] < mainHeaderFontSize)
841
  ]
842
+ if header_spans:
 
843
  collecting = True
844
  matched_header_font_size = max(span["size"] for span in header_spans)
845
+
846
  collected_lines.append(line_text)
847
  valid_spans = [span for span in spans if span.get("bbox")]
848
 
 
864
  ]
865
  else:
866
  current_bbox[page_num] = header_bbox
 
867
  last_y1s[page_num] = header_bbox[3]
868
  x0, y0, x1, y1 = header_bbox
869
+
870
  zoom = 200
871
  left = int(x0)
872
  top = int(y0)
873
  zoom_str = f"{zoom},{left},{top}"
874
  pageNumberFound = page_num + 1
875
 
876
+ # Build the query parameters
877
  params = {
878
  'pdfLink': pdf_path, # Your PDF link
879
  'keyword': heading_to_search, # Your keyword (could be a string or list)
 
911
  data_list_JSON.append(data_entry)
912
 
913
  # Convert list to JSON
914
+ # json_output = json.dumps(data_list_JSON, indent=4)
915
 
 
916
  i += 2
917
  continue
 
 
 
 
 
 
918
  else:
919
+ if (substring_match and not collecting and
920
+ len(combined_line_norm) > 0): # and (headertoContinue1 or headertoContinue2) ):
921
 
922
+ # Calculate word match percentage
923
+ word_match_percent = words_match_ratio(heading_norm, combined_line_norm) * 100
 
 
 
 
 
924
 
925
+ # Check if at least 70% of header words exist in this line
926
+ meets_word_threshold = word_match_percent >= 100
 
 
 
 
 
 
 
 
 
 
927
 
928
+ # Check header conditions (including word threshold)
929
+ header_spans = [
930
+ span for span in spans
931
+ if (is_header(span, most_common_font_size, most_common_color, most_common_font)
932
+ # and span['size'] >= subsubheaderFontSize
933
+ and span['size'] < mainHeaderFontSize)
934
+ ]
935
 
936
+ if header_spans and (meets_word_threshold or same_start_word(heading_to_search, combined_line_norm) ):
937
+ collecting = True
938
+ matched_header_font_size = max(span["size"] for span in header_spans)
939
+
940
+ collected_lines.append(line_text)
941
+ valid_spans = [span for span in spans if span.get("bbox")]
942
 
943
+ if valid_spans:
944
+ x0s = [span["bbox"][0] for span in valid_spans]
945
+ x1s = [span["bbox"][2] for span in valid_spans]
946
+ y0s = [span["bbox"][1] for span in valid_spans]
947
+ y1s = [span["bbox"][3] for span in valid_spans]
 
 
948
 
949
+ header_bbox = [min(x0s), min(y0s), max(x1s), max(y1s)]
950
 
951
+ if page_num in current_bbox:
952
+ cb = current_bbox[page_num]
953
+ current_bbox[page_num] = [
954
+ min(cb[0], header_bbox[0]),
955
+ min(cb[1], header_bbox[1]),
956
+ max(cb[2], header_bbox[2]),
957
+ max(cb[3], header_bbox[3])
958
+ ]
959
+ else:
960
+ current_bbox[page_num] = header_bbox
961
+
962
+ last_y1s[page_num] = header_bbox[3]
963
+ x0, y0, x1, y1 = header_bbox
964
+ zoom = 200
965
+ left = int(x0)
966
+ top = int(y0)
967
+ zoom_str = f"{zoom},{left},{top}"
968
+ pageNumberFound = page_num + 1
969
+
970
+ # Build the query parameters
971
+ params = {
972
+ 'pdfLink': pdf_path, # Your PDF link
973
+ 'keyword': heading_to_search, # Your keyword (could be a string or list)
974
+ }
975
+
976
+ # URL encode each parameter
977
+ encoded_params = {key: urllib.parse.quote(value, safe='') for key, value in params.items()}
978
+
979
+ # Construct the final encoded link
980
+ encoded_link = '&'.join([f"{key}={value}" for key, value in encoded_params.items()])
981
+
982
+ # Correctly construct the final URL with page and zoom
983
+ final_url = f"{baselink}{encoded_link}#page={str(pageNumberFound)}&zoom={zoom_str}"
984
+
985
+ # Get current date and time
986
+ now = datetime.now()
987
+
988
+ # Format the output
989
+ formatted_time = now.strftime("%d/%m/%Y %I:%M:%S %p")
990
+ # Optionally, add the URL to a DataFrame
991
+
992
+
993
+ data_entry = {
994
+ "NBSLink": final_url,
995
+ "Subject": heading_to_search,
996
+ "Page": str(pageNumberFound),
997
+ "Author": "ADR",
998
+ "Creation Date": formatted_time,
999
+ "Layer": "Initial",
1000
+ "Code": stringtowrite,
1001
+ "head above 1": paths[-2],
1002
+ "head above 2": paths[0],
1003
+ "MC Connnection": 'Go to ' + paths[0].strip().split()[0] +'/'+ heading_to_search.strip().split()[0] + ' in '+ filename
1004
+ }
1005
+ data_list_JSON.append(data_entry)
1006
+
1007
+ # Convert list to JSON
1008
+ # json_output = json.dumps(data_list_JSON, indent=4)
1009
+
1010
+
1011
+ i += 2
1012
+ continue
1013
+ if collecting:
1014
+ norm_line = normalize_text(line_text)
1015
+
1016
+ # Optimized URL check
1017
+ if url_pattern.match(norm_line):
1018
+ line_is_header = False
1019
  else:
1020
+ line_is_header = any(is_header(span, most_common_font_size, most_common_color, most_common_font) for span in spans)
1021
 
1022
+ if line_is_header:
1023
+ header_font_size = max(span["size"] for span in spans)
1024
+ is_probably_real_header = (
1025
+ header_font_size >= matched_header_font_size and
1026
+ is_header(spans[0], most_common_font_size, most_common_color, most_common_font) and
1027
+ len(line_text.strip()) > 2
1028
+ )
1029
 
1030
+ if (norm_line != matched_header_line_norm and
1031
+ norm_line != heading_norm and
1032
+ is_probably_real_header):
1033
+ if line_text not in heading_norm:
1034
+ collecting = False
1035
+ done = True
1036
+ headertoContinue1 = False
1037
+ headertoContinue2=False
1038
+ for page_num, bbox in current_bbox.items():
1039
+ bbox[3] = last_y1s.get(page_num, bbox[3])
1040
+ page_highlights[page_num] = bbox
1041
+ highlight_boxes(docHighlights, page_highlights,stringtowrite)
1042
+
1043
+ break_collecting = True
1044
+ break
1045
 
1046
+ if break_collecting:
1047
+ break
1048
+
1049
+ collected_lines.append(line_text)
1050
+ valid_spans = [span for span in spans if span.get("bbox")]
1051
+ if valid_spans:
1052
+ x0s = [span["bbox"][0] for span in valid_spans]
1053
+ x1s = [span["bbox"][2] for span in valid_spans]
1054
+ y0s = [span["bbox"][1] for span in valid_spans]
1055
+ y1s = [span["bbox"][3] for span in valid_spans]
1056
+
1057
+ line_bbox = [min(x0s), min(y0s), max(x1s), max(y1s)]
1058
 
1059
+ if page_num in current_bbox:
1060
+ cb = current_bbox[page_num]
1061
+ current_bbox[page_num] = [
1062
+ min(cb[0], line_bbox[0]),
1063
+ min(cb[1], line_bbox[1]),
1064
+ max(cb[2], line_bbox[2]),
1065
+ max(cb[3], line_bbox[3])
1066
+ ]
1067
+ else:
1068
+ current_bbox[page_num] = line_bbox
1069
 
1070
+ last_y1s[page_num] = line_bbox[3]
1071
+ i += 1
1072
 
1073
+ if not done:
1074
+ for page_num, bbox in current_bbox.items():
1075
+ bbox[3] = last_y1s.get(page_num, bbox[3])
1076
+ page_highlights[page_num] = bbox
1077
+ if 'installation' in paths[-2].lower() or 'execution' in paths[-2].lower() or 'miscellaneous items' in paths[-2].lower() :
1078
+ stringtowrite='Not to be billed'
1079
+ else:
1080
+ stringtowrite='To be billed'
1081
+ highlight_boxes(docHighlights, page_highlights,stringtowrite)
1082
 
1083
+ docarray.append(docHighlights)
1084
+ jsons.append(data_list_JSON)
1085
+ dbxTeam = tsadropboxretrieval.ADR_Access_DropboxTeam('user')
1086
+ dbPath = '/TSA JOBS/ADR Test/FIND/'
1087
+ jsonCombined=[]
1088
+ for i in range(len(arrayofPDFS)):
1089
+ singlepdf=arrayofPDFS[i]
1090
+
1091
+ metadata = dbxTeam.sharing_get_shared_link_metadata(singlepdf)
1092
+ pdf_bytes = BytesIO()
1093
+ docHighlights.save(pdf_bytes)
1094
+ pdflink = tsadropboxretrieval.uploadanyFile(doc=docarray[i], path=dbPath, pdfname=filenames[i])
1095
+ json_output1=changepdflinks(jsons[i],pdflink)
1096
+ jsonCombined.extend(json_output1)
1097
+ combined_json_str = json.dumps(jsonCombined, indent=1)
1098
+ return pdf_bytes.getvalue(), docHighlights , combined_json_str
1099
 
1100
  ########################################################################################################################################################
1101
  ########################################################################################################################################################