Marthee commited on
Commit
5e73a2e
·
verified ·
1 Parent(s): 6b130e4

Update InitialMarkups.py

Browse files
Files changed (1) hide show
  1. InitialMarkups.py +5 -816
InitialMarkups.py CHANGED
@@ -1043,8 +1043,10 @@ def extract_section_under_header(pdf_path):
1043
  ########################################################################################################################################################
1044
 
1045
 
 
1046
  def extract_section_under_header_tobebilledOnly(pdf_path):
1047
  Alltexttobebilled=''
 
1048
  top_margin = 70
1049
  bottom_margin = 50
1050
  headertoContinue1 = False
@@ -1192,6 +1194,8 @@ def extract_section_under_header_tobebilledOnly(pdf_path):
1192
  stringtowrite='Not to be billed'
1193
  else:
1194
  stringtowrite='To be billed'
 
 
1195
  # Optimized header matching
1196
  existsfull = (
1197
  ( combined_line_norm in allchildrenheaders_set or
@@ -1468,823 +1472,8 @@ def extract_section_under_header_tobebilledOnly(pdf_path):
1468
 
1469
  pdf_bytes = BytesIO()
1470
  docHighlights.save(pdf_bytes)
1471
- return pdf_bytes.getvalue(), docHighlights , json_output , Alltexttobebilled
1472
-
1473
-
1474
-
1475
-
1476
-
1477
-
1478
- ########################################################################################################################################################
1479
- ########################################################################################################################################################
1480
-
1481
- def extract_section_under_headerRawan(pdf_path,headingjson,pagenum=0,incomingheader=0):
1482
- Alltexttobebilled=''
1483
- top_margin = 70
1484
- bottom_margin = 50
1485
- # Optimized URL handling
1486
- if pdf_path and ('http' in pdf_path or 'dropbox' in pdf_path):
1487
- pdf_path = pdf_path.replace('dl=0', 'dl=1')
1488
-
1489
- # Cache frequently used values
1490
- response = requests.get(pdf_path)
1491
- pdf_content = BytesIO(response.content)
1492
- if not pdf_content:
1493
- raise ValueError("No valid PDF content found.")
1494
-
1495
- doc = fitz.open(stream=pdf_content, filetype="pdf")
1496
- docHighlights = fitz.open(stream=pdf_content, filetype="pdf")
1497
- most_common_font_size, most_common_color, most_common_font = get_regular_font_size_and_color(doc)
1498
-
1499
- # Precompute regex patterns
1500
- dot_pattern = re.compile(r'\.{3,}')
1501
- url_pattern = re.compile(r'https?://\S+|www\.\S+')
1502
-
1503
- def get_toc_page_numbers(doc, max_pages_to_check=15):
1504
- toc_pages = []
1505
- for page_num in range(min(len(doc), max_pages_to_check)):
1506
- page = doc.load_page(page_num)
1507
- blocks = page.get_text("dict")["blocks"]
1508
-
1509
- dot_line_count = 0
1510
- for block in blocks:
1511
- for line in block.get("lines", []):
1512
- line_text = get_spaced_text_from_spans(line["spans"]).strip()
1513
- if dot_pattern.search(line_text):
1514
- dot_line_count += 1
1515
-
1516
- if dot_line_count >= 3:
1517
- toc_pages.append(page_num)
1518
-
1519
- return list(range(0, toc_pages[-1] +1)) if toc_pages else toc_pages
1520
-
1521
- toc_pages = get_toc_page_numbers(doc)
1522
-
1523
- headers, top_3_font_sizes, smallest_font_size, headersSpans = extract_headers(
1524
- doc, toc_pages, most_common_font_size, most_common_color, most_common_font, top_margin, bottom_margin
1525
- )
1526
-
1527
- listofheadingsfromrawan=[]
1528
- if type(headingjson) == str:
1529
- listofheadingsfromrawan.append(headingjson)
1530
- headingjson=[headingjson]
1531
- else:
1532
- for item in headingjson:
1533
- listofheadingsfromrawan.append(normalize_text(item['Subject']))
1534
- # Precompute all children headers once
1535
- allchildrenheaders = listofheadingsfromrawan
1536
- allchildrenheaders_set = set(allchildrenheaders) # For faster lookups
1537
-
1538
- df = pd.DataFrame(columns=["NBSLink","Subject","Page","Author","Creation Date","Layer",'Code', 'head above 1', "head above 2"])
1539
- data_list_JSON = []
1540
-
1541
- if len(top_3_font_sizes)==3:
1542
- mainHeaderFontSize, subHeaderFontSize, subsubheaderFontSize = top_3_font_sizes
1543
- elif len(top_3_font_sizes)==2:
1544
- mainHeaderFontSize= top_3_font_sizes[0]
1545
- subHeaderFontSize= top_3_font_sizes[1]
1546
- subsubheaderFontSize= top_3_font_sizes[1]
1547
-
1548
-
1549
- # Preload all pages to avoid repeated loading
1550
- # pages = [doc.load_page(page_num) for page_num in range(len(doc)) if page_num not in toc_pages]
1551
- newjsonList=[]
1552
- for heading_to_searchDict in headingjson:
1553
- if type(heading_to_searchDict) == str:
1554
- heading_to_search = heading_to_searchDict
1555
- heading_to_searchPageNum = pagenum
1556
- else:
1557
- heading_to_search = heading_to_searchDict['Subject']
1558
- heading_to_searchPageNum = int(heading_to_searchDict['Page'])-1
1559
- incomingheader = heading_to_searchDict['head above 1']
1560
-
1561
- done = False
1562
- collecting = False
1563
- collected_lines = []
1564
- page_highlights = {}
1565
- current_bbox = {}
1566
- last_y1s = {}
1567
- mainHeader = ''
1568
- subHeader = ''
1569
- matched_header_line_norm = heading_to_search
1570
- break_collecting = False
1571
- heading_norm = normalize_text(heading_to_search)
1572
-
1573
- for page_num in range(heading_to_searchPageNum,len(doc)):
1574
- if page_num in toc_pages:
1575
- continue
1576
- if break_collecting:
1577
- break
1578
- page=doc[page_num]
1579
- page_height = page.rect.height
1580
- blocks = page.get_text("dict")["blocks"]
1581
-
1582
- for block in blocks:
1583
- if break_collecting:
1584
- break
1585
-
1586
- lines = block.get("lines", [])
1587
- i = 0
1588
- while i < len(lines):
1589
- if break_collecting:
1590
- break
1591
-
1592
- spans = lines[i].get("spans", [])
1593
- if not spans:
1594
- i += 1
1595
- continue
1596
-
1597
- y0 = spans[0]["bbox"][1]
1598
- y1 = spans[0]["bbox"][3]
1599
- if y0 < top_margin or y1 > (page_height - bottom_margin):
1600
- i += 1
1601
- continue
1602
-
1603
- line_text = get_spaced_text_from_spans(spans).lower()
1604
- line_text_norm = normalize_text(line_text)
1605
-
1606
- # Combine with next line if available
1607
- if i + 1 < len(lines):
1608
- next_spans = lines[i + 1].get("spans", [])
1609
- next_line_text = get_spaced_text_from_spans(next_spans).lower()
1610
- combined_line_norm = normalize_text(line_text + " " + next_line_text)
1611
- else:
1612
- combined_line_norm = line_text_norm
1613
- # Optimized header matching
1614
- existsfull = (
1615
- ( combined_line_norm in allchildrenheaders_set or
1616
- combined_line_norm in allchildrenheaders ) and heading_to_search in combined_line_norm
1617
- )
1618
-
1619
- # New word-based matching
1620
- current_line_words = set(combined_line_norm.split())
1621
- heading_words = set(heading_norm.split())
1622
- all_words_match = current_line_words.issubset(heading_words) and len(current_line_words) > 0
1623
-
1624
- substring_match = (
1625
- heading_norm in combined_line_norm or
1626
- combined_line_norm in heading_norm or
1627
- all_words_match # Include the new word-based matching
1628
- )
1629
-
1630
- if (substring_match and existsfull and not collecting and
1631
- len(combined_line_norm) > 0 ):#and (headertoContinue1 or headertoContinue2) ):
1632
-
1633
- # Check header conditions more efficiently
1634
- header_spans = [
1635
- span for span in spans
1636
- if (is_header(span, most_common_font_size, most_common_color, most_common_font)
1637
- # and span['size'] >= subsubheaderFontSize
1638
- and span['size'] < mainHeaderFontSize)
1639
- ]
1640
- if header_spans:
1641
- Alltexttobebilled+= ' '+ combined_line_norm
1642
- collecting = True
1643
- matched_header_font_size = max(span["size"] for span in header_spans)
1644
-
1645
- collected_lines.append(line_text)
1646
- valid_spans = [span for span in spans if span.get("bbox")]
1647
-
1648
- if valid_spans:
1649
- x0s = [span["bbox"][0] for span in valid_spans]
1650
- x1s = [span["bbox"][2] for span in valid_spans]
1651
- y0s = [span["bbox"][1] for span in valid_spans]
1652
- y1s = [span["bbox"][3] for span in valid_spans]
1653
-
1654
- header_bbox = [min(x0s), min(y0s), max(x1s), max(y1s)]
1655
-
1656
- if page_num in current_bbox:
1657
- cb = current_bbox[page_num]
1658
- current_bbox[page_num] = [
1659
- min(cb[0], header_bbox[0]),
1660
- min(cb[1], header_bbox[1]),
1661
- max(cb[2], header_bbox[2]),
1662
- max(cb[3], header_bbox[3])
1663
- ]
1664
- else:
1665
- current_bbox[page_num] = header_bbox
1666
- last_y1s[page_num] = header_bbox[3]
1667
- x0, y0, x1, y1 = header_bbox
1668
-
1669
- zoom = 200
1670
- left = int(x0)
1671
- top = int(y0)
1672
- zoom_str = f"{zoom},{left},{top}"
1673
- pageNumberFound = page_num + 1
1674
-
1675
- # Build the query parameters
1676
- params = {
1677
- 'pdfLink': pdf_path, # Your PDF link
1678
- 'keyword': heading_to_search, # Your keyword (could be a string or list)
1679
- }
1680
-
1681
- # URL encode each parameter
1682
- encoded_params = {key: urllib.parse.quote(value, safe='') for key, value in params.items()}
1683
-
1684
- # Construct the final encoded link
1685
- encoded_link = '&'.join([f"{key}={value}" for key, value in encoded_params.items()])
1686
-
1687
- # Correctly construct the final URL with page and zoom
1688
- final_url = f"{newlink}{encoded_link}#page={str(pageNumberFound)}&zoom={zoom_str}"
1689
-
1690
- # Get current date and time
1691
- now = datetime.now()
1692
-
1693
- # Format the output
1694
- formatted_time = now.strftime("%d/%m/%Y %I:%M:%S %p")
1695
- # Optionally, add the URL to a DataFrame
1696
- new_url= final_url
1697
- if type(heading_to_searchDict) != str:
1698
- heading_to_searchDict['NBSLink']=new_url
1699
- newjsonList.append(heading_to_searchDict)
1700
- i += 2
1701
- continue
1702
- else:
1703
- if (substring_match and not collecting and
1704
- len(combined_line_norm) > 0): # and (headertoContinue1 or headertoContinue2) ):
1705
-
1706
- # Calculate word match percentage
1707
- word_match_percent = words_match_ratio(heading_norm, combined_line_norm) * 100
1708
-
1709
- # Check if at least 70% of header words exist in this line
1710
- meets_word_threshold = word_match_percent >= 100
1711
-
1712
- # Check header conditions (including word threshold)
1713
- header_spans = [
1714
- span for span in spans
1715
- if (is_header(span, most_common_font_size, most_common_color, most_common_font)
1716
- # and span['size'] >= subsubheaderFontSize
1717
- and span['size'] < mainHeaderFontSize)
1718
- ]
1719
-
1720
- if header_spans and (meets_word_threshold or same_start_word(heading_to_search, combined_line_norm) ):
1721
- collecting = True
1722
- Alltexttobebilled+= ' '+ combined_line_norm
1723
- matched_header_font_size = max(span["size"] for span in header_spans)
1724
-
1725
- collected_lines.append(line_text)
1726
- valid_spans = [span for span in spans if span.get("bbox")]
1727
-
1728
- if valid_spans:
1729
- x0s = [span["bbox"][0] for span in valid_spans]
1730
- x1s = [span["bbox"][2] for span in valid_spans]
1731
- y0s = [span["bbox"][1] for span in valid_spans]
1732
- y1s = [span["bbox"][3] for span in valid_spans]
1733
 
1734
- header_bbox = [min(x0s), min(y0s), max(x1s), max(y1s)]
1735
 
1736
- if page_num in current_bbox:
1737
- cb = current_bbox[page_num]
1738
- current_bbox[page_num] = [
1739
- min(cb[0], header_bbox[0]),
1740
- min(cb[1], header_bbox[1]),
1741
- max(cb[2], header_bbox[2]),
1742
- max(cb[3], header_bbox[3])
1743
- ]
1744
- else:
1745
- current_bbox[page_num] = header_bbox
1746
 
1747
- last_y1s[page_num] = header_bbox[3]
1748
- x0, y0, x1, y1 = header_bbox
1749
- zoom = 200
1750
- left = int(x0)
1751
- top = int(y0)
1752
- zoom_str = f"{zoom},{left},{top}"
1753
- pageNumberFound = page_num + 1
1754
-
1755
- # Build the query parameters
1756
- params = {
1757
- 'pdfLink': pdf_path, # Your PDF link
1758
- 'keyword': heading_to_search, # Your keyword (could be a string or list)
1759
- }
1760
-
1761
- # URL encode each parameter
1762
- encoded_params = {key: urllib.parse.quote(value, safe='') for key, value in params.items()}
1763
-
1764
- # Construct the final encoded link
1765
- encoded_link = '&'.join([f"{key}={value}" for key, value in encoded_params.items()])
1766
-
1767
- # Correctly construct the final URL with page and zoom
1768
- final_url = f"{newlink}{encoded_link}#page={str(pageNumberFound)}&zoom={zoom_str}"
1769
- new_url= final_url
1770
- if type(heading_to_searchDict) != str:
1771
- heading_to_searchDict['NBSLink']=new_url
1772
- newjsonList.append(heading_to_searchDict)
1773
- i += 2
1774
- continue
1775
- if collecting:
1776
- norm_line = normalize_text(line_text)
1777
-
1778
- # Optimized URL check
1779
- if url_pattern.match(norm_line):
1780
- line_is_header = False
1781
- else:
1782
- line_is_header = any(is_header(span, most_common_font_size, most_common_color, most_common_font) for span in spans)
1783
-
1784
- if line_is_header:
1785
- header_font_size = max(span["size"] for span in spans)
1786
- is_probably_real_header = (
1787
- header_font_size >= matched_header_font_size and
1788
- is_header(spans[0], most_common_font_size, most_common_color, most_common_font) and
1789
- len(line_text.strip()) > 2
1790
- )
1791
-
1792
- if (norm_line != matched_header_line_norm and
1793
- norm_line != heading_norm and
1794
- is_probably_real_header):
1795
- if line_text not in heading_norm:
1796
- collecting = False
1797
- done = True
1798
- headertoContinue1 = False
1799
- headertoContinue2=False
1800
- for page_num, bbox in current_bbox.items():
1801
- bbox[3] = last_y1s.get(page_num, bbox[3])
1802
- page_highlights[page_num] = bbox
1803
-
1804
- if 'installation' in incomingheader or 'execution' in incomingheader or 'miscellaneous items' in incomingheader :
1805
- stringtowrite='Not to be billed'
1806
- else:
1807
- stringtowrite='To be billed'
1808
- highlight_boxes(docHighlights, page_highlights,stringtowrite)
1809
-
1810
- break_collecting = True
1811
- break
1812
-
1813
- if break_collecting:
1814
- break
1815
-
1816
- collected_lines.append(line_text)
1817
- valid_spans = [span for span in spans if span.get("bbox")]
1818
- if valid_spans:
1819
- x0s = [span["bbox"][0] for span in valid_spans]
1820
- x1s = [span["bbox"][2] for span in valid_spans]
1821
- y0s = [span["bbox"][1] for span in valid_spans]
1822
- y1s = [span["bbox"][3] for span in valid_spans]
1823
-
1824
- line_bbox = [min(x0s), min(y0s), max(x1s), max(y1s)]
1825
-
1826
- if page_num in current_bbox:
1827
- cb = current_bbox[page_num]
1828
- current_bbox[page_num] = [
1829
- min(cb[0], line_bbox[0]),
1830
- min(cb[1], line_bbox[1]),
1831
- max(cb[2], line_bbox[2]),
1832
- max(cb[3], line_bbox[3])
1833
- ]
1834
- else:
1835
- current_bbox[page_num] = line_bbox
1836
-
1837
- last_y1s[page_num] = line_bbox[3]
1838
- i += 1
1839
-
1840
- if not done:
1841
- for page_num, bbox in current_bbox.items():
1842
- bbox[3] = last_y1s.get(page_num, bbox[3])
1843
- page_highlights[page_num] = bbox
1844
- if 'installation' in incomingheader or 'execution' in incomingheader or 'miscellaneous items' in incomingheader :
1845
- stringtowrite='Not to be billed'
1846
- else:
1847
- stringtowrite='To be billed'
1848
- highlight_boxes(docHighlights, page_highlights,stringtowrite)
1849
-
1850
- # docHighlights.save("highlighted_output.pdf", garbage=4, deflate=True)
1851
-
1852
- pdf_bytes = BytesIO()
1853
- docHighlights.save(pdf_bytes)
1854
- return pdf_bytes.getvalue(), docHighlights , newjsonList, Alltexttobebilled
1855
-
1856
-
1857
-
1858
-
1859
- ########################################################################################################################################################
1860
- ########################################################################################################################################################
1861
- def extract_section_under_header_FullDoc_WithoutNotBilled(pdf_path):
1862
- Alltexttobebilled=''
1863
- top_margin = 70
1864
- bottom_margin = 50
1865
- headertoContinue1 = False
1866
- headertoContinue2=False
1867
-
1868
- parsed_url = urlparse(pdf_path)
1869
- filename = os.path.basename(parsed_url.path)
1870
- filename = unquote(filename) # decode URL-encoded characters
1871
-
1872
- # Optimized URL handling
1873
- if pdf_path and ('http' in pdf_path or 'dropbox' in pdf_path):
1874
- pdf_path = pdf_path.replace('dl=0', 'dl=1')
1875
-
1876
- # Cache frequently used values
1877
- response = requests.get(pdf_path)
1878
- pdf_content = BytesIO(response.content)
1879
- if not pdf_content:
1880
- raise ValueError("No valid PDF content found.")
1881
-
1882
- doc = fitz.open(stream=pdf_content, filetype="pdf")
1883
- docHighlights = fitz.open(stream=pdf_content, filetype="pdf")
1884
- most_common_font_size, most_common_color, most_common_font = get_regular_font_size_and_color(doc)
1885
-
1886
- # Precompute regex patterns
1887
- dot_pattern = re.compile(r'\.{3,}')
1888
- url_pattern = re.compile(r'https?://\S+|www\.\S+')
1889
-
1890
- def get_toc_page_numbers(doc, max_pages_to_check=15):
1891
- toc_pages = []
1892
- for page_num in range(min(len(doc), max_pages_to_check)):
1893
- page = doc.load_page(page_num)
1894
- blocks = page.get_text("dict")["blocks"]
1895
-
1896
- dot_line_count = 0
1897
- for block in blocks:
1898
- for line in block.get("lines", []):
1899
- line_text = get_spaced_text_from_spans(line["spans"]).strip()
1900
- if dot_pattern.search(line_text):
1901
- dot_line_count += 1
1902
-
1903
- if dot_line_count >= 3:
1904
- toc_pages.append(page_num)
1905
-
1906
- return list(range(0, toc_pages[-1] +1)) if toc_pages else toc_pages
1907
-
1908
- toc_pages = get_toc_page_numbers(doc)
1909
-
1910
- headers, top_3_font_sizes, smallest_font_size, headersSpans = extract_headers(
1911
- doc, toc_pages, most_common_font_size, most_common_color, most_common_font, top_margin, bottom_margin
1912
- )
1913
-
1914
- hierarchy = build_header_hierarchy(doc, toc_pages, most_common_font_size, most_common_color, most_common_font)
1915
- listofHeaderstoMarkup = get_leaf_headers_with_paths(hierarchy)
1916
-
1917
- # Precompute all children headers once
1918
- allchildrenheaders = [normalize_text(item['text']) for item, p in listofHeaderstoMarkup]
1919
- allchildrenheaders_set = set(allchildrenheaders) # For faster lookups
1920
-
1921
- df = pd.DataFrame(columns=["NBSLink","Subject","Page","Author","Creation Date","Layer",'Code', 'head above 1', "head above 2"])
1922
- dictionaryNBS={}
1923
- data_list_JSON = []
1924
-
1925
- if len(top_3_font_sizes)==3:
1926
- mainHeaderFontSize, subHeaderFontSize, subsubheaderFontSize = top_3_font_sizes
1927
- elif len(top_3_font_sizes)==2:
1928
- mainHeaderFontSize= top_3_font_sizes[0]
1929
- subHeaderFontSize= top_3_font_sizes[1]
1930
- subsubheaderFontSize= top_3_font_sizes[1]
1931
-
1932
-
1933
-
1934
- # Preload all pages to avoid repeated loading
1935
- # pages = [doc.load_page(page_num) for page_num in range(len(doc)) if page_num not in toc_pages]
1936
-
1937
- for heading_to_searchDict, paths in listofHeaderstoMarkup:
1938
-
1939
- heading_to_search = heading_to_searchDict['text']
1940
- heading_to_searchPageNum = heading_to_searchDict['page']
1941
-
1942
- # Initialize variables
1943
- headertoContinue1 = False
1944
- headertoContinue2 = False
1945
- matched_header_line = None
1946
- done = False
1947
- collecting = False
1948
- collected_lines = []
1949
- page_highlights = {}
1950
- current_bbox = {}
1951
- last_y1s = {}
1952
- mainHeader = ''
1953
- subHeader = ''
1954
- matched_header_line_norm = heading_to_search
1955
- break_collecting = False
1956
- heading_norm = normalize_text(heading_to_search)
1957
- paths_norm = [normalize_text(p) for p in paths[0]] if paths and paths[0] else []
1958
-
1959
- for page_num in range(0,len(doc)):
1960
- if page_num in toc_pages:
1961
- continue
1962
- if break_collecting:
1963
- break
1964
- page=doc[page_num]
1965
- page_height = page.rect.height
1966
- blocks = page.get_text("dict")["blocks"]
1967
-
1968
- for block in blocks:
1969
- if break_collecting:
1970
- break
1971
-
1972
- lines = block.get("lines", [])
1973
- i = 0
1974
- while i < len(lines):
1975
- if break_collecting:
1976
- break
1977
-
1978
- spans = lines[i].get("spans", [])
1979
- if not spans:
1980
- i += 1
1981
- continue
1982
-
1983
- y0 = spans[0]["bbox"][1]
1984
- y1 = spans[0]["bbox"][3]
1985
- if y0 < top_margin or y1 > (page_height - bottom_margin):
1986
- i += 1
1987
- continue
1988
-
1989
- line_text = get_spaced_text_from_spans(spans).lower()
1990
- line_text_norm = normalize_text(line_text)
1991
-
1992
- # Combine with next line if available
1993
- if i + 1 < len(lines):
1994
- next_spans = lines[i + 1].get("spans", [])
1995
- next_line_text = get_spaced_text_from_spans(next_spans).lower()
1996
- combined_line_norm = normalize_text(line_text + " " + next_line_text)
1997
- else:
1998
- combined_line_norm = line_text_norm
1999
-
2000
- # Check if we should continue processing
2001
- if combined_line_norm and combined_line_norm in paths[0]:
2002
-
2003
- headertoContinue1 = combined_line_norm
2004
- if combined_line_norm and combined_line_norm in paths[-2]:
2005
- headertoContinue2 = combined_line_norm
2006
- if 'installation' in paths[-2].lower() or 'execution' in paths[-2].lower() or 'miscellaneous items' in paths[-2].lower() :
2007
- stringtowrite='Not to be billed'
2008
- else:
2009
- stringtowrite='To be billed'
2010
- # Optimized header matching
2011
- if stringtowrite=='Not to be billed':
2012
- Alltexttobebilled+= combined_line_norm #################################################
2013
- existsfull = (
2014
- ( combined_line_norm in allchildrenheaders_set or
2015
- combined_line_norm in allchildrenheaders ) and heading_to_search in combined_line_norm
2016
- )
2017
-
2018
- # New word-based matching
2019
- current_line_words = set(combined_line_norm.split())
2020
- heading_words = set(heading_norm.split())
2021
- all_words_match = current_line_words.issubset(heading_words) and len(current_line_words) > 0
2022
-
2023
- substring_match = (
2024
- heading_norm in combined_line_norm or
2025
- combined_line_norm in heading_norm or
2026
- all_words_match # Include the new word-based matching
2027
- )
2028
- # substring_match = (
2029
- # heading_norm in combined_line_norm or
2030
- # combined_line_norm in heading_norm
2031
- # )
2032
-
2033
- if (substring_match and existsfull and not collecting and
2034
- len(combined_line_norm) > 0 ):#and (headertoContinue1 or headertoContinue2) ):
2035
-
2036
- # Check header conditions more efficiently
2037
- header_spans = [
2038
- span for span in spans
2039
- if (is_header(span, most_common_font_size, most_common_color, most_common_font)
2040
- # and span['size'] >= subsubheaderFontSize
2041
- and span['size'] < mainHeaderFontSize)
2042
- ]
2043
- if header_spans and stringtowrite.startswith('To'):
2044
- collecting = True
2045
- matched_header_font_size = max(span["size"] for span in header_spans)
2046
- Alltexttobebilled+= ' '+ combined_line_norm
2047
- collected_lines.append(line_text)
2048
- valid_spans = [span for span in spans if span.get("bbox")]
2049
-
2050
- if valid_spans:
2051
- x0s = [span["bbox"][0] for span in valid_spans]
2052
- x1s = [span["bbox"][2] for span in valid_spans]
2053
- y0s = [span["bbox"][1] for span in valid_spans]
2054
- y1s = [span["bbox"][3] for span in valid_spans]
2055
-
2056
- header_bbox = [min(x0s), min(y0s), max(x1s), max(y1s)]
2057
-
2058
- if page_num in current_bbox:
2059
- cb = current_bbox[page_num]
2060
- current_bbox[page_num] = [
2061
- min(cb[0], header_bbox[0]),
2062
- min(cb[1], header_bbox[1]),
2063
- max(cb[2], header_bbox[2]),
2064
- max(cb[3], header_bbox[3])
2065
- ]
2066
- else:
2067
- current_bbox[page_num] = header_bbox
2068
- last_y1s[page_num] = header_bbox[3]
2069
- x0, y0, x1, y1 = header_bbox
2070
-
2071
- zoom = 200
2072
- left = int(x0)
2073
- top = int(y0)
2074
- zoom_str = f"{zoom},{left},{top}"
2075
- pageNumberFound = page_num + 1
2076
-
2077
- # Build the query parameters
2078
- params = {
2079
- 'pdfLink': pdf_path, # Your PDF link
2080
- 'keyword': heading_to_search, # Your keyword (could be a string or list)
2081
- }
2082
-
2083
- # URL encode each parameter
2084
- encoded_params = {key: urllib.parse.quote(value, safe='') for key, value in params.items()}
2085
-
2086
- # Construct the final encoded link
2087
- encoded_link = '&'.join([f"{key}={value}" for key, value in encoded_params.items()])
2088
-
2089
- # Correctly construct the final URL with page and zoom
2090
- final_url = f"{baselink}{encoded_link}#page={str(pageNumberFound)}&zoom={zoom_str}"
2091
-
2092
- # Get current date and time
2093
- now = datetime.now()
2094
-
2095
- # Format the output
2096
- formatted_time = now.strftime("%d/%m/%Y %I:%M:%S %p")
2097
- # Optionally, add the URL to a DataFrame
2098
-
2099
-
2100
- data_entry = {
2101
- "NBSLink": final_url,
2102
- "Subject": heading_to_search,
2103
- "Page": str(pageNumberFound),
2104
- "Author": "ADR",
2105
- "Creation Date": formatted_time,
2106
- "Layer": "Initial",
2107
- "Code": stringtowrite,
2108
- "head above 1": paths[-2],
2109
- "head above 2": paths[0],
2110
- "MC Connnection": 'Go to ' + paths[0].strip().split()[0] +'/'+ heading_to_search.strip().split()[0] + ' in '+ filename
2111
- }
2112
- data_list_JSON.append(data_entry)
2113
-
2114
- # Convert list to JSON
2115
- json_output = json.dumps(data_list_JSON, indent=4)
2116
-
2117
- i += 2
2118
- continue
2119
- else:
2120
- if (substring_match and not collecting and
2121
- len(combined_line_norm) > 0): # and (headertoContinue1 or headertoContinue2) ):
2122
-
2123
- # Calculate word match percentage
2124
- word_match_percent = words_match_ratio(heading_norm, combined_line_norm) * 100
2125
-
2126
- # Check if at least 70% of header words exist in this line
2127
- meets_word_threshold = word_match_percent >= 100
2128
-
2129
- # Check header conditions (including word threshold)
2130
- header_spans = [
2131
- span for span in spans
2132
- if (is_header(span, most_common_font_size, most_common_color, most_common_font)
2133
- # and span['size'] >= subsubheaderFontSize
2134
- and span['size'] < mainHeaderFontSize)
2135
- ]
2136
-
2137
- if header_spans and (meets_word_threshold or same_start_word(heading_to_search, combined_line_norm) ) and stringtowrite.startswith('To'):
2138
- collecting = True
2139
- matched_header_font_size = max(span["size"] for span in header_spans)
2140
- Alltexttobebilled+= ' '+ combined_line_norm
2141
- collected_lines.append(line_text)
2142
- valid_spans = [span for span in spans if span.get("bbox")]
2143
-
2144
- if valid_spans:
2145
- x0s = [span["bbox"][0] for span in valid_spans]
2146
- x1s = [span["bbox"][2] for span in valid_spans]
2147
- y0s = [span["bbox"][1] for span in valid_spans]
2148
- y1s = [span["bbox"][3] for span in valid_spans]
2149
-
2150
- header_bbox = [min(x0s), min(y0s), max(x1s), max(y1s)]
2151
-
2152
- if page_num in current_bbox:
2153
- cb = current_bbox[page_num]
2154
- current_bbox[page_num] = [
2155
- min(cb[0], header_bbox[0]),
2156
- min(cb[1], header_bbox[1]),
2157
- max(cb[2], header_bbox[2]),
2158
- max(cb[3], header_bbox[3])
2159
- ]
2160
- else:
2161
- current_bbox[page_num] = header_bbox
2162
-
2163
- last_y1s[page_num] = header_bbox[3]
2164
- x0, y0, x1, y1 = header_bbox
2165
- zoom = 200
2166
- left = int(x0)
2167
- top = int(y0)
2168
- zoom_str = f"{zoom},{left},{top}"
2169
- pageNumberFound = page_num + 1
2170
-
2171
- # Build the query parameters
2172
- params = {
2173
- 'pdfLink': pdf_path, # Your PDF link
2174
- 'keyword': heading_to_search, # Your keyword (could be a string or list)
2175
- }
2176
-
2177
- # URL encode each parameter
2178
- encoded_params = {key: urllib.parse.quote(value, safe='') for key, value in params.items()}
2179
-
2180
- # Construct the final encoded link
2181
- encoded_link = '&'.join([f"{key}={value}" for key, value in encoded_params.items()])
2182
-
2183
- # Correctly construct the final URL with page and zoom
2184
- final_url = f"{baselink}{encoded_link}#page={str(pageNumberFound)}&zoom={zoom_str}"
2185
-
2186
- # Get current date and time
2187
- now = datetime.now()
2188
-
2189
- # Format the output
2190
- formatted_time = now.strftime("%d/%m/%Y %I:%M:%S %p")
2191
- # Optionally, add the URL to a DataFrame
2192
-
2193
-
2194
- data_entry = {
2195
- "NBSLink": final_url,
2196
- "Subject": heading_to_search,
2197
- "Page": str(pageNumberFound),
2198
- "Author": "ADR",
2199
- "Creation Date": formatted_time,
2200
- "Layer": "Initial",
2201
- "Code": stringtowrite,
2202
- "head above 1": paths[-2],
2203
- "head above 2": paths[0],
2204
- "MC Connnection": 'Go to ' + paths[0].strip().split()[0] +'/'+ heading_to_search.strip().split()[0] + ' in '+ filename
2205
- }
2206
- data_list_JSON.append(data_entry)
2207
-
2208
- # Convert list to JSON
2209
- json_output = json.dumps(data_list_JSON, indent=4)
2210
-
2211
-
2212
- i += 2
2213
- continue
2214
- if collecting:
2215
- norm_line = normalize_text(line_text)
2216
-
2217
- # Optimized URL check
2218
- if url_pattern.match(norm_line):
2219
- line_is_header = False
2220
- else:
2221
- line_is_header = any(is_header(span, most_common_font_size, most_common_color, most_common_font) for span in spans)
2222
-
2223
- if line_is_header:
2224
- header_font_size = max(span["size"] for span in spans)
2225
- is_probably_real_header = (
2226
- header_font_size >= matched_header_font_size and
2227
- is_header(spans[0], most_common_font_size, most_common_color, most_common_font) and
2228
- len(line_text.strip()) > 2
2229
- )
2230
-
2231
- if (norm_line != matched_header_line_norm and
2232
- norm_line != heading_norm and
2233
- is_probably_real_header):
2234
- if line_text not in heading_norm:
2235
- collecting = False
2236
- done = True
2237
- headertoContinue1 = False
2238
- headertoContinue2=False
2239
- for page_num, bbox in current_bbox.items():
2240
- bbox[3] = last_y1s.get(page_num, bbox[3])
2241
- page_highlights[page_num] = bbox
2242
- highlight_boxes(docHighlights, page_highlights,stringtowrite)
2243
-
2244
- break_collecting = True
2245
- break
2246
-
2247
- if break_collecting:
2248
- break
2249
-
2250
- collected_lines.append(line_text)
2251
- valid_spans = [span for span in spans if span.get("bbox")]
2252
- if valid_spans:
2253
- x0s = [span["bbox"][0] for span in valid_spans]
2254
- x1s = [span["bbox"][2] for span in valid_spans]
2255
- y0s = [span["bbox"][1] for span in valid_spans]
2256
- y1s = [span["bbox"][3] for span in valid_spans]
2257
-
2258
- line_bbox = [min(x0s), min(y0s), max(x1s), max(y1s)]
2259
-
2260
- if page_num in current_bbox:
2261
- cb = current_bbox[page_num]
2262
- current_bbox[page_num] = [
2263
- min(cb[0], line_bbox[0]),
2264
- min(cb[1], line_bbox[1]),
2265
- max(cb[2], line_bbox[2]),
2266
- max(cb[3], line_bbox[3])
2267
- ]
2268
- else:
2269
- current_bbox[page_num] = line_bbox
2270
-
2271
- last_y1s[page_num] = line_bbox[3]
2272
- i += 1
2273
-
2274
- if not done:
2275
- for page_num, bbox in current_bbox.items():
2276
- bbox[3] = last_y1s.get(page_num, bbox[3])
2277
- page_highlights[page_num] = bbox
2278
- if 'installation' in paths[-2].lower() or 'execution' in paths[-2].lower() or 'miscellaneous items' in paths[-2].lower() :
2279
- stringtowrite='Not to be billed'
2280
- else:
2281
- stringtowrite='To be billed'
2282
- highlight_boxes(docHighlights, page_highlights,stringtowrite)
2283
-
2284
- # docHighlights.save("highlighted_output.pdf", garbage=4, deflate=True)
2285
- print('Alltexttobebilled')
2286
- pdf_bytes = BytesIO()
2287
- docHighlights.save(pdf_bytes)
2288
- return pdf_bytes.getvalue(), docHighlights , json_output , Alltexttobebilled
2289
 
2290
-
 
1043
  ########################################################################################################################################################
1044
 
1045
 
1046
+
1047
  def extract_section_under_header_tobebilledOnly(pdf_path):
1048
  Alltexttobebilled=''
1049
+ alltextWithoutNotbilled=''
1050
  top_margin = 70
1051
  bottom_margin = 50
1052
  headertoContinue1 = False
 
1194
  stringtowrite='Not to be billed'
1195
  else:
1196
  stringtowrite='To be billed'
1197
+ if stringtowrite=='Not to be billed':
1198
+ alltextWithoutNotbilled+= combined_line_norm #################################################
1199
  # Optimized header matching
1200
  existsfull = (
1201
  ( combined_line_norm in allchildrenheaders_set or
 
1472
 
1473
  pdf_bytes = BytesIO()
1474
  docHighlights.save(pdf_bytes)
1475
+ return pdf_bytes.getvalue(), docHighlights , json_output , Alltexttobebilled , alltextWithoutNotbilled
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1476
 
 
1477
 
 
 
 
 
 
 
 
 
 
 
1478
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1479