Marthee commited on
Commit
adb75eb
·
verified ·
1 Parent(s): 16c4771

Update InitialMarkups.py

Browse files
Files changed (1) hide show
  1. InitialMarkups.py +109 -129
InitialMarkups.py CHANGED
@@ -1933,68 +1933,63 @@ def extract_section_under_header_tobebilled2marthe(multiplePDF_Paths):
1933
  print(arrayofPDFS)
1934
  df = pd.DataFrame(columns=["PDF Name","NBSLink","Subject","Page","Author","Creation Date","Layer",'Code', 'head above 1', "head above 2","BodyText"])
1935
  for pdf_path in arrayofPDFS:
1936
- print(pdf_path)
1937
  headertoContinue1 = False
1938
  headertoContinue2=False
1939
  Alltexttobebilled=''
1940
-
1941
-
1942
-
1943
  parsed_url = urlparse(pdf_path)
1944
  filename = os.path.basename(parsed_url.path)
1945
  filename = unquote(filename) # decode URL-encoded characters
1946
-
1947
-
1948
  # Optimized URL handling
1949
  if pdf_path and ('http' in pdf_path or 'dropbox' in pdf_path):
1950
  pdf_path = pdf_path.replace('dl=0', 'dl=1')
1951
-
1952
  # Cache frequently used values
1953
  response = requests.get(pdf_path)
1954
  pdf_content = BytesIO(response.content)
1955
  if not pdf_content:
1956
  raise ValueError("No valid PDF content found.")
1957
-
1958
  doc = fitz.open(stream=pdf_content, filetype="pdf")
1959
  docHighlights = fitz.open(stream=pdf_content, filetype="pdf")
1960
  most_common_font_size, most_common_color, most_common_font = get_regular_font_size_and_color(doc)
1961
-
1962
  # Precompute regex patterns
1963
  dot_pattern = re.compile(r'\.{3,}')
1964
  url_pattern = re.compile(r'https?://\S+|www\.\S+')
1965
-
1966
  def get_toc_page_numbers(doc, max_pages_to_check=15):
1967
  toc_pages = []
1968
  for page_num in range(min(len(doc), max_pages_to_check)):
1969
  page = doc.load_page(page_num)
1970
  blocks = page.get_text("dict")["blocks"]
1971
-
1972
  dot_line_count = 0
1973
  for block in blocks:
1974
  for line in block.get("lines", []):
1975
  line_text = get_spaced_text_from_spans(line["spans"]).strip()
1976
  if dot_pattern.search(line_text):
1977
  dot_line_count += 1
1978
-
1979
  if dot_line_count >= 3:
1980
  toc_pages.append(page_num)
1981
-
1982
  return list(range(0, toc_pages[-1] +1)) if toc_pages else toc_pages
1983
-
1984
  toc_pages = get_toc_page_numbers(doc)
1985
-
1986
  headers, top_3_font_sizes, smallest_font_size, headersSpans = extract_headers(
1987
  doc, toc_pages, most_common_font_size, most_common_color, most_common_font, top_margin, bottom_margin
1988
  )
1989
-
1990
  hierarchy = build_header_hierarchy(doc, toc_pages, most_common_font_size, most_common_color, most_common_font)
1991
  listofHeaderstoMarkup = get_leaf_headers_with_paths(hierarchy)
1992
 
1993
  # Precompute all children headers once
1994
  allchildrenheaders = [normalize_text(item['text']) for item, p in listofHeaderstoMarkup]
1995
  allchildrenheaders_set = set(allchildrenheaders) # For faster lookups
1996
-
1997
-
1998
  dictionaryNBS={}
1999
  data_list_JSON = []
2000
  currentgroupname=''
@@ -2004,16 +1999,16 @@ def extract_section_under_header_tobebilled2marthe(multiplePDF_Paths):
2004
  mainHeaderFontSize= top_3_font_sizes[0]
2005
  subHeaderFontSize= top_3_font_sizes[1]
2006
  subsubheaderFontSize= top_3_font_sizes[1]
2007
-
2008
 
2009
-
2010
  # Preload all pages to avoid repeated loading
2011
  # pages = [doc.load_page(page_num) for page_num in range(len(doc)) if page_num not in toc_pages]
2012
-
2013
  for heading_to_searchDict, paths in listofHeaderstoMarkup:
2014
  heading_to_search = heading_to_searchDict['text']
2015
  heading_to_searchPageNum = heading_to_searchDict['page']
2016
-
2017
  # Initialize variables
2018
  headertoContinue1 = False
2019
  headertoContinue2 = False
@@ -2031,45 +2026,45 @@ def extract_section_under_header_tobebilled2marthe(multiplePDF_Paths):
2031
  heading_norm = normalize_text(heading_to_search)
2032
  paths_norm = [normalize_text(p) for p in paths[0]] if paths and paths[0] else []
2033
  for page_num in range(heading_to_searchPageNum,len(doc)):
2034
- # print(heading_to_search)
2035
  if paths[0].strip().lower() != currentgroupname.strip().lower():
2036
  Alltexttobebilled+= paths[0] +'\n'
2037
  currentgroupname=paths[0]
2038
- # print(paths[0])
2039
-
2040
 
2041
  if page_num in toc_pages:
2042
- continue
2043
  if break_collecting:
2044
  break
2045
  page=doc[page_num]
2046
  page_height = page.rect.height
2047
  blocks = page.get_text("dict")["blocks"]
2048
-
2049
  for block in blocks:
2050
  if break_collecting:
2051
  break
2052
-
2053
  lines = block.get("lines", [])
2054
  i = 0
2055
  while i < len(lines):
2056
  if break_collecting:
2057
  break
2058
-
2059
  spans = lines[i].get("spans", [])
2060
  if not spans:
2061
  i += 1
2062
  continue
2063
-
2064
  y0 = spans[0]["bbox"][1]
2065
  y1 = spans[0]["bbox"][3]
2066
  if y0 < top_margin or y1 > (page_height - bottom_margin):
2067
  i += 1
2068
  continue
2069
-
2070
  line_text = get_spaced_text_from_spans(spans).lower()
2071
  line_text_norm = normalize_text(line_text)
2072
-
2073
  # Combine with next line if available
2074
  if i + 1 < len(lines):
2075
  next_spans = lines[i + 1].get("spans", [])
@@ -2077,7 +2072,7 @@ def extract_section_under_header_tobebilled2marthe(multiplePDF_Paths):
2077
  combined_line_norm = normalize_text(line_text + " " + next_line_text)
2078
  else:
2079
  combined_line_norm = line_text_norm
2080
-
2081
  # Check if we should continue processing
2082
  if combined_line_norm and combined_line_norm in paths[0]:
2083
 
@@ -2086,24 +2081,14 @@ def extract_section_under_header_tobebilled2marthe(multiplePDF_Paths):
2086
 
2087
  headertoContinue2 = combined_line_norm
2088
  # if 'installation' in paths[-2].lower() or 'execution' in paths[-2].lower() or 'miscellaneous items' in paths[-2].lower() :
2089
- # last_path = paths[-2].lower()
2090
- flagstring=False
2091
- last_word = paths[-2].lower()
2092
-
2093
  # if any(word in paths[-2].lower() for word in keywordstoSkip):
2094
  # 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():
2095
- # if any(keyword in last_word for keyword in keywords):
2096
- # Precompile regex for faster repeated checks
2097
- pattern = re.compile(r'installation|execution|miscellaneous items|workmanship|testing|labeling', re.IGNORECASE)
2098
-
2099
- if pattern.search(last_word):
2100
- stringtowrite = 'Not to be billed'
2101
- flagstring = False
2102
  else:
2103
- stringtowrite = 'To be billed'
2104
- flagstring = True
2105
-
2106
- if flagstring:
2107
  # Alltexttobebilled+= combined_line_norm #################################################
2108
  if matched_header_line_norm in combined_line_norm:
2109
  Alltexttobebilled+='\n'
@@ -2113,12 +2098,12 @@ def extract_section_under_header_tobebilled2marthe(multiplePDF_Paths):
2113
  ( combined_line_norm in allchildrenheaders_set or
2114
  combined_line_norm in allchildrenheaders ) and heading_to_search in combined_line_norm
2115
  )
2116
-
2117
  # New word-based matching
2118
  current_line_words = set(combined_line_norm.split())
2119
  heading_words = set(heading_norm.split())
2120
  all_words_match = current_line_words.issubset(heading_words) and len(current_line_words) > 0
2121
-
2122
  substring_match = (
2123
  heading_norm in combined_line_norm or
2124
  combined_line_norm in heading_norm or
@@ -2128,10 +2113,10 @@ def extract_section_under_header_tobebilled2marthe(multiplePDF_Paths):
2128
  # heading_norm in combined_line_norm or
2129
  # combined_line_norm in heading_norm
2130
  # )
2131
-
2132
  if (substring_match and existsfull and not collecting and
2133
  len(combined_line_norm) > 0 ):#and (headertoContinue1 or headertoContinue2) ):
2134
-
2135
  # Check header conditions more efficiently
2136
  header_spans = [
2137
  span for span in spans
@@ -2139,23 +2124,23 @@ def extract_section_under_header_tobebilled2marthe(multiplePDF_Paths):
2139
  # and span['size'] >= subsubheaderFontSize
2140
  and span['size'] < mainHeaderFontSize)
2141
  ]
2142
- if header_spans and flagstring:
2143
  collecting = True
2144
  # if stringtowrite=='To be billed':
2145
  # Alltexttobebilled+='\n'
2146
  matched_header_font_size = max(span["size"] for span in header_spans)
2147
-
2148
  # collected_lines.append(line_text)
2149
  valid_spans = [span for span in spans if span.get("bbox")]
2150
-
2151
  if valid_spans:
2152
  x0s = [span["bbox"][0] for span in valid_spans]
2153
  x1s = [span["bbox"][2] for span in valid_spans]
2154
  y0s = [span["bbox"][1] for span in valid_spans]
2155
  y1s = [span["bbox"][3] for span in valid_spans]
2156
-
2157
  header_bbox = [min(x0s), min(y0s), max(x1s), max(y1s)]
2158
-
2159
  if page_num in current_bbox:
2160
  cb = current_bbox[page_num]
2161
  current_bbox[page_num] = [
@@ -2168,36 +2153,36 @@ def extract_section_under_header_tobebilled2marthe(multiplePDF_Paths):
2168
  current_bbox[page_num] = header_bbox
2169
  last_y1s[page_num] = header_bbox[3]
2170
  x0, y0, x1, y1 = header_bbox
2171
-
2172
  zoom = 200
2173
  left = int(x0)
2174
  top = int(y0)
2175
  zoom_str = f"{zoom},{left},{top}"
2176
  pageNumberFound = page_num + 1
2177
-
2178
- # Build the query parameters
2179
  params = {
2180
  'pdfLink': pdf_path, # Your PDF link
2181
  'keyword': heading_to_search, # Your keyword (could be a string or list)
2182
  }
2183
-
2184
  # URL encode each parameter
2185
  encoded_params = {key: urllib.parse.quote(value, safe='') for key, value in params.items()}
2186
-
2187
  # Construct the final encoded link
2188
  encoded_link = '&'.join([f"{key}={value}" for key, value in encoded_params.items()])
2189
-
2190
  # Correctly construct the final URL with page and zoom
2191
  final_url = f"{baselink}{encoded_link}#page={str(pageNumberFound)}&zoom={zoom_str}"
2192
-
2193
  # Get current date and time
2194
  now = datetime.now()
2195
-
2196
  # Format the output
2197
  formatted_time = now.strftime("%d/%m/%Y %I:%M:%S %p")
2198
  # Optionally, add the URL to a DataFrame
2199
-
2200
-
2201
  data_entry = {
2202
  "PDF Name":filename,
2203
  "NBSLink": final_url,
@@ -2213,22 +2198,22 @@ def extract_section_under_header_tobebilled2marthe(multiplePDF_Paths):
2213
  "MC Connnection": 'Go to ' + paths[0].strip().split()[0] +'/'+ heading_to_search.strip().split()[0] + ' in '+ filename
2214
  }
2215
  data_list_JSON.append(data_entry)
2216
-
2217
  # Convert list to JSON
2218
  json_output = json.dumps(data_list_JSON, indent=4)
2219
-
2220
  i += 2
2221
  continue
2222
  else:
2223
  if (substring_match and not collecting and
2224
  len(combined_line_norm) > 0): # and (headertoContinue1 or headertoContinue2) ):
2225
-
2226
  # Calculate word match percentage
2227
  word_match_percent = words_match_ratio(heading_norm, combined_line_norm) * 100
2228
-
2229
  # Check if at least 70% of header words exist in this line
2230
  meets_word_threshold = word_match_percent >= 100
2231
-
2232
  # Check header conditions (including word threshold)
2233
  header_spans = [
2234
  span for span in spans
@@ -2236,26 +2221,26 @@ def extract_section_under_header_tobebilled2marthe(multiplePDF_Paths):
2236
  # and span['size'] >= subsubheaderFontSize
2237
  and span['size'] < mainHeaderFontSize)
2238
  ]
2239
-
2240
- if header_spans and (meets_word_threshold or same_start_word(heading_to_search, combined_line_norm) ) and flagstring:
2241
  collecting = True
2242
- if flagstring:
2243
  Alltexttobebilled+='\n'
2244
  # if stringtowrite=='To be billed':
2245
  # Alltexttobebilled+= ' '+ combined_line_norm
2246
  matched_header_font_size = max(span["size"] for span in header_spans)
2247
-
2248
  collected_lines.append(line_text)
2249
  valid_spans = [span for span in spans if span.get("bbox")]
2250
-
2251
  if valid_spans:
2252
  x0s = [span["bbox"][0] for span in valid_spans]
2253
  x1s = [span["bbox"][2] for span in valid_spans]
2254
  y0s = [span["bbox"][1] for span in valid_spans]
2255
  y1s = [span["bbox"][3] for span in valid_spans]
2256
-
2257
  header_bbox = [min(x0s), min(y0s), max(x1s), max(y1s)]
2258
-
2259
  if page_num in current_bbox:
2260
  cb = current_bbox[page_num]
2261
  current_bbox[page_num] = [
@@ -2266,7 +2251,7 @@ def extract_section_under_header_tobebilled2marthe(multiplePDF_Paths):
2266
  ]
2267
  else:
2268
  current_bbox[page_num] = header_bbox
2269
-
2270
  last_y1s[page_num] = header_bbox[3]
2271
  x0, y0, x1, y1 = header_bbox
2272
  zoom = 200
@@ -2274,32 +2259,32 @@ def extract_section_under_header_tobebilled2marthe(multiplePDF_Paths):
2274
  top = int(y0)
2275
  zoom_str = f"{zoom},{left},{top}"
2276
  pageNumberFound = page_num + 1
2277
-
2278
- # Build the query parameters
2279
  params = {
2280
  'pdfLink': pdf_path, # Your PDF link
2281
  'keyword': heading_to_search, # Your keyword (could be a string or list)
2282
  }
2283
-
2284
  # URL encode each parameter
2285
  encoded_params = {key: urllib.parse.quote(value, safe='') for key, value in params.items()}
2286
-
2287
  # Construct the final encoded link
2288
  encoded_link = '&'.join([f"{key}={value}" for key, value in encoded_params.items()])
2289
-
2290
  # Correctly construct the final URL with page and zoom
2291
  final_url = f"{baselink}{encoded_link}#page={str(pageNumberFound)}&zoom={zoom_str}"
2292
-
2293
  # Get current date and time
2294
  now = datetime.now()
2295
-
2296
  # Format the output
2297
  formatted_time = now.strftime("%d/%m/%Y %I:%M:%S %p")
2298
  # Optionally, add the URL to a DataFrame
2299
-
2300
-
2301
  data_entry = {
2302
- "PDF Name": filename,
2303
  "NBSLink": final_url,
2304
  "Subject": heading_to_search,
2305
  "Page": str(pageNumberFound),
@@ -2313,22 +2298,22 @@ def extract_section_under_header_tobebilled2marthe(multiplePDF_Paths):
2313
  "MC Connnection": 'Go to ' + paths[0].strip().split()[0] +'/'+ heading_to_search.strip().split()[0] + ' in '+ filename
2314
  }
2315
  data_list_JSON.append(data_entry)
2316
-
2317
  # Convert list to JSON
2318
  json_output = json.dumps(data_list_JSON, indent=4)
2319
-
2320
 
2321
  i += 2
2322
  continue
2323
  if collecting:
2324
  norm_line = normalize_text(line_text)
2325
-
2326
  # Optimized URL check
2327
  if url_pattern.match(norm_line):
2328
  line_is_header = False
2329
  else:
2330
  line_is_header = any(is_header(span, most_common_font_size, most_common_color, most_common_font) for span in spans)
2331
-
2332
  if line_is_header:
2333
  header_font_size = max(span["size"] for span in spans)
2334
  is_probably_real_header = (
@@ -2336,26 +2321,26 @@ def extract_section_under_header_tobebilled2marthe(multiplePDF_Paths):
2336
  is_header(spans[0], most_common_font_size, most_common_color, most_common_font) and
2337
  len(line_text.strip()) > 2
2338
  )
2339
-
2340
  if (norm_line != matched_header_line_norm and
2341
  norm_line != heading_norm and
2342
  is_probably_real_header):
2343
  if line_text not in heading_norm:
2344
- collecting = False
2345
- done = True
2346
- headertoContinue1 = False
2347
- headertoContinue2=False
2348
- for page_num, bbox in current_bbox.items():
2349
- bbox[3] = last_y1s.get(page_num, bbox[3])
2350
- page_highlights[page_num] = bbox
2351
- highlight_boxes(docHighlights, page_highlights,stringtowrite)
2352
-
2353
- break_collecting = True
2354
- break
2355
-
2356
  if break_collecting:
2357
  break
2358
-
2359
  collected_lines.append(line_text)
2360
  valid_spans = [span for span in spans if span.get("bbox")]
2361
  if valid_spans:
@@ -2363,9 +2348,9 @@ def extract_section_under_header_tobebilled2marthe(multiplePDF_Paths):
2363
  x1s = [span["bbox"][2] for span in valid_spans]
2364
  y0s = [span["bbox"][1] for span in valid_spans]
2365
  y1s = [span["bbox"][3] for span in valid_spans]
2366
-
2367
  line_bbox = [min(x0s), min(y0s), max(x1s), max(y1s)]
2368
-
2369
  if page_num in current_bbox:
2370
  cb = current_bbox[page_num]
2371
  current_bbox[page_num] = [
@@ -2376,30 +2361,25 @@ def extract_section_under_header_tobebilled2marthe(multiplePDF_Paths):
2376
  ]
2377
  else:
2378
  current_bbox[page_num] = line_bbox
2379
-
2380
  last_y1s[page_num] = line_bbox[3]
2381
  i += 1
2382
-
2383
  if not done:
2384
  for page_num, bbox in current_bbox.items():
2385
  bbox[3] = last_y1s.get(page_num, bbox[3])
2386
  page_highlights[page_num] = bbox
2387
- if flagstring:
2388
- # if 'installation' in paths[-2].lower() or 'execution' in paths[-2].lower() or 'miscellaneous items' in paths[-2].lower() :
2389
- stringtowrite='To be billed'
2390
- else:
2391
  stringtowrite='Not to be billed'
 
 
2392
  highlight_boxes(docHighlights, page_highlights,stringtowrite)
2393
-
2394
- # docHighlights.save("highlighted_output.pdf", garbage=4, deflate=True)
2395
-
2396
- pdf_bytes = BytesIO()
2397
- docHighlights.save(pdf_bytes)
2398
-
2399
- return pdf_bytes.getvalue(), docHighlights , json_output, Alltexttobebilled , filename
2400
-
2401
-
2402
-
2403
-
2404
-
2405
-
 
1933
  print(arrayofPDFS)
1934
  df = pd.DataFrame(columns=["PDF Name","NBSLink","Subject","Page","Author","Creation Date","Layer",'Code', 'head above 1', "head above 2","BodyText"])
1935
  for pdf_path in arrayofPDFS:
 
1936
  headertoContinue1 = False
1937
  headertoContinue2=False
1938
  Alltexttobebilled=''
 
 
 
1939
  parsed_url = urlparse(pdf_path)
1940
  filename = os.path.basename(parsed_url.path)
1941
  filename = unquote(filename) # decode URL-encoded characters
1942
+
 
1943
  # Optimized URL handling
1944
  if pdf_path and ('http' in pdf_path or 'dropbox' in pdf_path):
1945
  pdf_path = pdf_path.replace('dl=0', 'dl=1')
1946
+
1947
  # Cache frequently used values
1948
  response = requests.get(pdf_path)
1949
  pdf_content = BytesIO(response.content)
1950
  if not pdf_content:
1951
  raise ValueError("No valid PDF content found.")
1952
+
1953
  doc = fitz.open(stream=pdf_content, filetype="pdf")
1954
  docHighlights = fitz.open(stream=pdf_content, filetype="pdf")
1955
  most_common_font_size, most_common_color, most_common_font = get_regular_font_size_and_color(doc)
1956
+
1957
  # Precompute regex patterns
1958
  dot_pattern = re.compile(r'\.{3,}')
1959
  url_pattern = re.compile(r'https?://\S+|www\.\S+')
1960
+
1961
  def get_toc_page_numbers(doc, max_pages_to_check=15):
1962
  toc_pages = []
1963
  for page_num in range(min(len(doc), max_pages_to_check)):
1964
  page = doc.load_page(page_num)
1965
  blocks = page.get_text("dict")["blocks"]
1966
+
1967
  dot_line_count = 0
1968
  for block in blocks:
1969
  for line in block.get("lines", []):
1970
  line_text = get_spaced_text_from_spans(line["spans"]).strip()
1971
  if dot_pattern.search(line_text):
1972
  dot_line_count += 1
1973
+
1974
  if dot_line_count >= 3:
1975
  toc_pages.append(page_num)
1976
+
1977
  return list(range(0, toc_pages[-1] +1)) if toc_pages else toc_pages
1978
+
1979
  toc_pages = get_toc_page_numbers(doc)
1980
+
1981
  headers, top_3_font_sizes, smallest_font_size, headersSpans = extract_headers(
1982
  doc, toc_pages, most_common_font_size, most_common_color, most_common_font, top_margin, bottom_margin
1983
  )
1984
+
1985
  hierarchy = build_header_hierarchy(doc, toc_pages, most_common_font_size, most_common_color, most_common_font)
1986
  listofHeaderstoMarkup = get_leaf_headers_with_paths(hierarchy)
1987
 
1988
  # Precompute all children headers once
1989
  allchildrenheaders = [normalize_text(item['text']) for item, p in listofHeaderstoMarkup]
1990
  allchildrenheaders_set = set(allchildrenheaders) # For faster lookups
1991
+
1992
+ df = pd.DataFrame(columns=["NBSLink","Subject","Page","Author","Creation Date","Layer",'Code', 'head above 1', "head above 2","BodyText"])
1993
  dictionaryNBS={}
1994
  data_list_JSON = []
1995
  currentgroupname=''
 
1999
  mainHeaderFontSize= top_3_font_sizes[0]
2000
  subHeaderFontSize= top_3_font_sizes[1]
2001
  subsubheaderFontSize= top_3_font_sizes[1]
2002
+
2003
 
2004
+
2005
  # Preload all pages to avoid repeated loading
2006
  # pages = [doc.load_page(page_num) for page_num in range(len(doc)) if page_num not in toc_pages]
2007
+
2008
  for heading_to_searchDict, paths in listofHeaderstoMarkup:
2009
  heading_to_search = heading_to_searchDict['text']
2010
  heading_to_searchPageNum = heading_to_searchDict['page']
2011
+
2012
  # Initialize variables
2013
  headertoContinue1 = False
2014
  headertoContinue2 = False
 
2026
  heading_norm = normalize_text(heading_to_search)
2027
  paths_norm = [normalize_text(p) for p in paths[0]] if paths and paths[0] else []
2028
  for page_num in range(heading_to_searchPageNum,len(doc)):
2029
+ print(heading_to_search)
2030
  if paths[0].strip().lower() != currentgroupname.strip().lower():
2031
  Alltexttobebilled+= paths[0] +'\n'
2032
  currentgroupname=paths[0]
2033
+ print(paths[0])
2034
+
2035
 
2036
  if page_num in toc_pages:
2037
+ continue
2038
  if break_collecting:
2039
  break
2040
  page=doc[page_num]
2041
  page_height = page.rect.height
2042
  blocks = page.get_text("dict")["blocks"]
2043
+
2044
  for block in blocks:
2045
  if break_collecting:
2046
  break
2047
+
2048
  lines = block.get("lines", [])
2049
  i = 0
2050
  while i < len(lines):
2051
  if break_collecting:
2052
  break
2053
+
2054
  spans = lines[i].get("spans", [])
2055
  if not spans:
2056
  i += 1
2057
  continue
2058
+
2059
  y0 = spans[0]["bbox"][1]
2060
  y1 = spans[0]["bbox"][3]
2061
  if y0 < top_margin or y1 > (page_height - bottom_margin):
2062
  i += 1
2063
  continue
2064
+
2065
  line_text = get_spaced_text_from_spans(spans).lower()
2066
  line_text_norm = normalize_text(line_text)
2067
+
2068
  # Combine with next line if available
2069
  if i + 1 < len(lines):
2070
  next_spans = lines[i + 1].get("spans", [])
 
2072
  combined_line_norm = normalize_text(line_text + " " + next_line_text)
2073
  else:
2074
  combined_line_norm = line_text_norm
2075
+
2076
  # Check if we should continue processing
2077
  if combined_line_norm and combined_line_norm in paths[0]:
2078
 
 
2081
 
2082
  headertoContinue2 = combined_line_norm
2083
  # if 'installation' in paths[-2].lower() or 'execution' in paths[-2].lower() or 'miscellaneous items' in paths[-2].lower() :
2084
+ last_path = paths[-2].lower()
 
 
 
2085
  # if any(word in paths[-2].lower() for word in keywordstoSkip):
2086
  # 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():
2087
+ if any(keyword in last_path for keyword in keywords):
2088
+ stringtowrite='Not to be billed'
 
 
 
 
 
2089
  else:
2090
+ stringtowrite='To be billed'
2091
+ if stringtowrite=='To be billed':
 
 
2092
  # Alltexttobebilled+= combined_line_norm #################################################
2093
  if matched_header_line_norm in combined_line_norm:
2094
  Alltexttobebilled+='\n'
 
2098
  ( combined_line_norm in allchildrenheaders_set or
2099
  combined_line_norm in allchildrenheaders ) and heading_to_search in combined_line_norm
2100
  )
2101
+
2102
  # New word-based matching
2103
  current_line_words = set(combined_line_norm.split())
2104
  heading_words = set(heading_norm.split())
2105
  all_words_match = current_line_words.issubset(heading_words) and len(current_line_words) > 0
2106
+
2107
  substring_match = (
2108
  heading_norm in combined_line_norm or
2109
  combined_line_norm in heading_norm or
 
2113
  # heading_norm in combined_line_norm or
2114
  # combined_line_norm in heading_norm
2115
  # )
2116
+
2117
  if (substring_match and existsfull and not collecting and
2118
  len(combined_line_norm) > 0 ):#and (headertoContinue1 or headertoContinue2) ):
2119
+
2120
  # Check header conditions more efficiently
2121
  header_spans = [
2122
  span for span in spans
 
2124
  # and span['size'] >= subsubheaderFontSize
2125
  and span['size'] < mainHeaderFontSize)
2126
  ]
2127
+ if header_spans and stringtowrite.startswith('To'):
2128
  collecting = True
2129
  # if stringtowrite=='To be billed':
2130
  # Alltexttobebilled+='\n'
2131
  matched_header_font_size = max(span["size"] for span in header_spans)
2132
+
2133
  # collected_lines.append(line_text)
2134
  valid_spans = [span for span in spans if span.get("bbox")]
2135
+
2136
  if valid_spans:
2137
  x0s = [span["bbox"][0] for span in valid_spans]
2138
  x1s = [span["bbox"][2] for span in valid_spans]
2139
  y0s = [span["bbox"][1] for span in valid_spans]
2140
  y1s = [span["bbox"][3] for span in valid_spans]
2141
+
2142
  header_bbox = [min(x0s), min(y0s), max(x1s), max(y1s)]
2143
+
2144
  if page_num in current_bbox:
2145
  cb = current_bbox[page_num]
2146
  current_bbox[page_num] = [
 
2153
  current_bbox[page_num] = header_bbox
2154
  last_y1s[page_num] = header_bbox[3]
2155
  x0, y0, x1, y1 = header_bbox
2156
+
2157
  zoom = 200
2158
  left = int(x0)
2159
  top = int(y0)
2160
  zoom_str = f"{zoom},{left},{top}"
2161
  pageNumberFound = page_num + 1
2162
+
2163
+ # Build the query parameters
2164
  params = {
2165
  'pdfLink': pdf_path, # Your PDF link
2166
  'keyword': heading_to_search, # Your keyword (could be a string or list)
2167
  }
2168
+
2169
  # URL encode each parameter
2170
  encoded_params = {key: urllib.parse.quote(value, safe='') for key, value in params.items()}
2171
+
2172
  # Construct the final encoded link
2173
  encoded_link = '&'.join([f"{key}={value}" for key, value in encoded_params.items()])
2174
+
2175
  # Correctly construct the final URL with page and zoom
2176
  final_url = f"{baselink}{encoded_link}#page={str(pageNumberFound)}&zoom={zoom_str}"
2177
+
2178
  # Get current date and time
2179
  now = datetime.now()
2180
+
2181
  # Format the output
2182
  formatted_time = now.strftime("%d/%m/%Y %I:%M:%S %p")
2183
  # Optionally, add the URL to a DataFrame
2184
+
2185
+
2186
  data_entry = {
2187
  "PDF Name":filename,
2188
  "NBSLink": final_url,
 
2198
  "MC Connnection": 'Go to ' + paths[0].strip().split()[0] +'/'+ heading_to_search.strip().split()[0] + ' in '+ filename
2199
  }
2200
  data_list_JSON.append(data_entry)
2201
+
2202
  # Convert list to JSON
2203
  json_output = json.dumps(data_list_JSON, indent=4)
2204
+
2205
  i += 2
2206
  continue
2207
  else:
2208
  if (substring_match and not collecting and
2209
  len(combined_line_norm) > 0): # and (headertoContinue1 or headertoContinue2) ):
2210
+
2211
  # Calculate word match percentage
2212
  word_match_percent = words_match_ratio(heading_norm, combined_line_norm) * 100
2213
+
2214
  # Check if at least 70% of header words exist in this line
2215
  meets_word_threshold = word_match_percent >= 100
2216
+
2217
  # Check header conditions (including word threshold)
2218
  header_spans = [
2219
  span for span in spans
 
2221
  # and span['size'] >= subsubheaderFontSize
2222
  and span['size'] < mainHeaderFontSize)
2223
  ]
2224
+
2225
+ if header_spans and (meets_word_threshold or same_start_word(heading_to_search, combined_line_norm) ) and stringtowrite.startswith('To'):
2226
  collecting = True
2227
+ if stringtowrite=='To be billed':
2228
  Alltexttobebilled+='\n'
2229
  # if stringtowrite=='To be billed':
2230
  # Alltexttobebilled+= ' '+ combined_line_norm
2231
  matched_header_font_size = max(span["size"] for span in header_spans)
2232
+
2233
  collected_lines.append(line_text)
2234
  valid_spans = [span for span in spans if span.get("bbox")]
2235
+
2236
  if valid_spans:
2237
  x0s = [span["bbox"][0] for span in valid_spans]
2238
  x1s = [span["bbox"][2] for span in valid_spans]
2239
  y0s = [span["bbox"][1] for span in valid_spans]
2240
  y1s = [span["bbox"][3] for span in valid_spans]
2241
+
2242
  header_bbox = [min(x0s), min(y0s), max(x1s), max(y1s)]
2243
+
2244
  if page_num in current_bbox:
2245
  cb = current_bbox[page_num]
2246
  current_bbox[page_num] = [
 
2251
  ]
2252
  else:
2253
  current_bbox[page_num] = header_bbox
2254
+
2255
  last_y1s[page_num] = header_bbox[3]
2256
  x0, y0, x1, y1 = header_bbox
2257
  zoom = 200
 
2259
  top = int(y0)
2260
  zoom_str = f"{zoom},{left},{top}"
2261
  pageNumberFound = page_num + 1
2262
+
2263
+ # Build the query parameters
2264
  params = {
2265
  'pdfLink': pdf_path, # Your PDF link
2266
  'keyword': heading_to_search, # Your keyword (could be a string or list)
2267
  }
2268
+
2269
  # URL encode each parameter
2270
  encoded_params = {key: urllib.parse.quote(value, safe='') for key, value in params.items()}
2271
+
2272
  # Construct the final encoded link
2273
  encoded_link = '&'.join([f"{key}={value}" for key, value in encoded_params.items()])
2274
+
2275
  # Correctly construct the final URL with page and zoom
2276
  final_url = f"{baselink}{encoded_link}#page={str(pageNumberFound)}&zoom={zoom_str}"
2277
+
2278
  # Get current date and time
2279
  now = datetime.now()
2280
+
2281
  # Format the output
2282
  formatted_time = now.strftime("%d/%m/%Y %I:%M:%S %p")
2283
  # Optionally, add the URL to a DataFrame
2284
+
2285
+
2286
  data_entry = {
2287
+ "PDF Name":filename,
2288
  "NBSLink": final_url,
2289
  "Subject": heading_to_search,
2290
  "Page": str(pageNumberFound),
 
2298
  "MC Connnection": 'Go to ' + paths[0].strip().split()[0] +'/'+ heading_to_search.strip().split()[0] + ' in '+ filename
2299
  }
2300
  data_list_JSON.append(data_entry)
2301
+
2302
  # Convert list to JSON
2303
  json_output = json.dumps(data_list_JSON, indent=4)
2304
+
2305
 
2306
  i += 2
2307
  continue
2308
  if collecting:
2309
  norm_line = normalize_text(line_text)
2310
+
2311
  # Optimized URL check
2312
  if url_pattern.match(norm_line):
2313
  line_is_header = False
2314
  else:
2315
  line_is_header = any(is_header(span, most_common_font_size, most_common_color, most_common_font) for span in spans)
2316
+
2317
  if line_is_header:
2318
  header_font_size = max(span["size"] for span in spans)
2319
  is_probably_real_header = (
 
2321
  is_header(spans[0], most_common_font_size, most_common_color, most_common_font) and
2322
  len(line_text.strip()) > 2
2323
  )
2324
+
2325
  if (norm_line != matched_header_line_norm and
2326
  norm_line != heading_norm and
2327
  is_probably_real_header):
2328
  if line_text not in heading_norm:
2329
+ collecting = False
2330
+ done = True
2331
+ headertoContinue1 = False
2332
+ headertoContinue2=False
2333
+ for page_num, bbox in current_bbox.items():
2334
+ bbox[3] = last_y1s.get(page_num, bbox[3])
2335
+ page_highlights[page_num] = bbox
2336
+ highlight_boxes(docHighlights, page_highlights,stringtowrite)
2337
+
2338
+ break_collecting = True
2339
+ break
2340
+
2341
  if break_collecting:
2342
  break
2343
+
2344
  collected_lines.append(line_text)
2345
  valid_spans = [span for span in spans if span.get("bbox")]
2346
  if valid_spans:
 
2348
  x1s = [span["bbox"][2] for span in valid_spans]
2349
  y0s = [span["bbox"][1] for span in valid_spans]
2350
  y1s = [span["bbox"][3] for span in valid_spans]
2351
+
2352
  line_bbox = [min(x0s), min(y0s), max(x1s), max(y1s)]
2353
+
2354
  if page_num in current_bbox:
2355
  cb = current_bbox[page_num]
2356
  current_bbox[page_num] = [
 
2361
  ]
2362
  else:
2363
  current_bbox[page_num] = line_bbox
2364
+
2365
  last_y1s[page_num] = line_bbox[3]
2366
  i += 1
2367
+
2368
  if not done:
2369
  for page_num, bbox in current_bbox.items():
2370
  bbox[3] = last_y1s.get(page_num, bbox[3])
2371
  page_highlights[page_num] = bbox
2372
+ if 'installation' in paths[-2].lower() or 'execution' in paths[-2].lower() or 'miscellaneous items' in paths[-2].lower() :
 
 
 
2373
  stringtowrite='Not to be billed'
2374
+ else:
2375
+ stringtowrite='To be billed'
2376
  highlight_boxes(docHighlights, page_highlights,stringtowrite)
2377
+
2378
+ # docHighlights.save("highlighted_output.pdf", garbage=4, deflate=True)
2379
+
2380
+ pdf_bytes = BytesIO()
2381
+ docHighlights.save(pdf_bytes)
2382
+
2383
+ return pdf_bytes.getvalue(), docHighlights , json_output, Alltexttobebilled , filename
2384
+
2385
+