#!/usr/bin/env python3
#
# Copyright (C) 2023 Jonathan A. Poritz
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
import argparse
from bs4 import BeautifulSoup
import os
import re
import sys
import time
import code
import warnings
if not sys.warnoptions:
warnings.simplefilter("ignore")
parser = argparse.ArgumentParser(description='Finds the links from an html file such as the "tidy_book.html" produced by OOprep.py, putting links plus preceding and following context into an HTML file')
parser.add_argument("inputfile", help="File containing html from which to extract outline information.")
parser.add_argument("-l", "--logfile", help='Filename for logfile to which will be appended detailed progress information; default is "link_finder.log".', default="link_finder.log", type=argparse.FileType('a'))
parser.add_argument('-v', '--verbose', help="print on console all information also going in to the logfile", action='store_true')
parser.add_argument("-o", "--output", help='Name to use as base of output file (before the ".html"). If absent, will be "links_from_" prepended to input file name (after any ".html", if present, is removed).', default="")
parser.add_argument('-n', '--numbered_chapters', help='chapters have numbers, which are used in building the outline numbering', action='store_true')
parser.add_argument('-c', '--context', help='in the HTML file with all figures, include a paragraph before and after the figure, to ', action='store_true')
args = parser.parse_args()
verbose = args.verbose
inputfile = args.inputfile
in_fh = open(inputfile,'r')
if inputfile[-5:]==".html":
infi_base=inputfile[:-5]
else:
infi_base=inputfile
output = args.output
args.logfile.write("------------------------------------\n")
def log_and_print(s):
t=time.strftime('%H:%M:%S')+" "+s
args.logfile.write(t+"\n")
if verbose:
print(t)
when_work = "On "+time.strftime('%d/%m/%Y')
log_and_print(when_work+", doing ")
what_work = ' '.join(sys.argv)+" in directory "+os.getcwd()
log_and_print(what_work)
if output:
out_fn_base = output
else:
[dir,fn] = os.path.split(infi_base)
if dir:
dir += "/"
out_fn_base = dir+'links_from_'+fn
html_out = open(out_fn_base+".html","w")
html_out.write(
f'''
Links
Links
''')
whole_book = in_fh.read()
book_lines = whole_book.split("\n")
locations = []
chap_no = 0
sec_no = 0
ssec_no = 0
sssec_no = 0
ssssec_no = 0
nesting_warnings = 0
this_location = ""
lines2print=[]
links=0
link_lines=0
html_lines=0
def warn(s):
log_and_print(f'WARNING: on line {line_no}, with content\n{l}\n{s}')
for line_no in range(len(book_lines)):
l = book_lines[line_no]
if l[:4]=="":
lines2print.append(line_no)
html_out.write(l+"\n")
if l[-5:]!="
":
warn("expecting entire line bracketed in ..
")
if args.numbered_chapters:
num = l.split()[1]
if num[-1] == ":":
num = num[:-1]
chap_no = int(num)
else:
chap_no += 1
sec_no = 0
ssec_no = 0
sssec_no = 0
ssssec_no = 0
this_location=f"Chapter {chap_no}"
locations.append(this_location)
log_and_print(f"found location {this_location}")
continue
if l[:4]=="":
lines2print.append(line_no)
html_out.write(l+"\n")
if l[-5:]!="
":
warn("expecting entire line bracketed in ..
")
if chap_no == 0:
nesting_warnings += 1
warn("Section without enclosing chapter")
sec_no += 1
ssec_no = 0
sssec_no = 0
ssssec_no = 0
this_location = f"{chap_no}.{sec_no}"
locations.append(this_location)
log_and_print(f"found location {this_location}")
continue
if l[:4]=="":
lines2print.append(line_no)
html_out.write(l+"\n")
if l[-5:]!="
":
warn("expecting entire line bracketed in ..
")
if sec_no == 0:
nesting_warnings += 1
if chap_no == 0:
warn("Subsection without enclosing chapter or section")
else:
warn("Subsection without enclosing section")
ssec_no += 1
sssec_no = 0
ssssec_no = 0
this_location = f"{chap_no}.{sec_no}.{ssec_no}"
locations.append(this_location)
log_and_print(f"found location {this_location}")
continue
if l[:4]=="":
lines2print.append(line_no)
html_out.write(l+"\n")
if l[-5:]!="
":
warn("expecting entire line bracketed in ..
")
if ssec_no ==0:
nesting_warnings += 1
if sec_no == 0:
if chap_no == 0:
warn("Subsubsection without enclosing chapter, section, or subsection")
else:
warn("Subsubsection without enclosing section or subsection")
else:
warn("Subsubsection without enclosing subsection")
sssec_no += 1
ssssec_no = 0
this_location = f"{chap_no}.{sec_no}.{ssec_no}.{sssec_no}"
locations.append(this_location)
log_and_print(f"found location {this_location}")
continue
if l[:4]=="":
lines2print.append(line_no)
html_out.write(l+"\n")
if l[-5:]!="
":
warn("expecting entire line bracketed in ..
")
if sssec_no == 0:
nesting_warnings += 1
if ssec_no == 0:
if sec_no == 0:
if chap_no == 0:
warn("Subsubsubsection without enclosing chapter, section, subsection, or subsubsection")
else:
warn("Subsubsubsection without enclosing section, subsection, or subsubsection")
else:
warn("Subsubsubsection without enclosing subsection or subsubsection")
else:
warn("Subsubsubsection without enclosing subsubsection")
ssssec_no += 1
this_location = f"{chap_no}.{sec_no}.{ssec_no}.{sssec_no}.{ssssec_no}"
locations.append(this_location)
log_and_print(f"found location {this_location}")
continue
locations.append(this_location)
soup=BeautifulSoup(l)
soup_a_list = soup.find_all("a")
if soup_a_list:
links += len(soup_a_list)
link_lines += 1
last_line_no = line_no-1
if args.context:
if not last_line_no in lines2print:
if book_lines[last_line_no] == "":
table_start=line_no-2
lines2print.append(table_start)
while book_lines[table_start][:6] != "":
list_start=line_no-2
lines2print.append(list_start)
while book_lines[list_start][:3] != "":
list_start=line_no-2
lines2print.append(list_start)
while book_lines[list_start] != "":
list_start -= 1
lines2print.append(list_start)
if not list_start:
raise ValueError(f"ordered list near line {line_no} seems to have no start!")
html_out.writelines(listlin+"\n" for listlin in book_lines[list_start:line_no])
lines2print.append(last_line_no)
html_out.write(book_lines[last_line_no]+"\n")
if l[:4] == "- ":
# go back and forward to get the entire list
0
elif book_lines[last_line_no] == "
":
# go back and forward to get the entire table
table_start=line_no-2
else:
lines2print.append(line_no)
html_out.write(l+"\n")
next_line_no = line_no-1
if book_lines[next_line_no][:6] == "":
table_end += 1
lines2print.append(table_end)
inner_soup = BeautifulSoup(book_lines[table_end])
inner_soup_a_list = inner_soup.find_all("a")
if inner_soup_a_list:
links += len(inner_soup_a_list)
link_lines += 1
if table_end>len(book_lines):
raise ValueError(f"table near line {line_no} seems to have no end!")
html_out.write('":
list_end += 1
lines2print.append(list_end)
inner_soup = BeautifulSoup(book_lines[list_end])
inner_soup_a_list = inner_soup.find_all("a")
if inner_soup_a_list:
links += len(inner_soup_a_list)
link_lines += 1
if list_end>len(book_lines):
raise ValueError(f"ordered list near line {line_no} seems to have no end!")
html_out.writelines(listlin+"\n" for listlin in book_lines[next_line_no:list_end+1])
elif book_lines[next_line_no] == "":
list_end=next_line_no+1
lines2print.append(list_end)
while book_lines[list_end] != " ":
list_end += 1
lines2print.append(list_end)
inner_soup = BeautifulSoup(book_lines[list_end])
inner_soup_a_list = inner_soup.find_all("a")
if inner_soup_a_list:
links += len(inner_soup_a_list)
link_lines += 1
if list_end>len(book_lines):
raise ValueError(f"ordered list near line {line_no} seems to have no end!")
html_out.writelines(listlin+"\n" for listlin in book_lines[next_line_no:list_end+1])
lines2print.append(last_line_no)
html_out.write(book_lines[last_line_no]+"\n")
else:
lines2print.append(line_no)
html_out.write(l+"\n")
if nesting_warnings:
if nesting_warnings >1:
log_and_print(f"Exiting: there were {nesting_warnings} improperly nested section warnings")
if nesting_warnings == 1:
log_and_print(f"Exiting: there was an improperly nested section warning")
quit()
html_out.write(' \n \n')
html_out.write('Please note that the material on this page is not by Jonathan Poritz but is instead by various Open Oregon Educational Resources authors, and those authors (or their employers) are the rightsholders and have chosen the copyright status of their work. Excerpts are posted here with the permission of Open Oregon Educaitonal Resources and are only for OOER internal use. \n')
html_out.write(f"{when_work} at {time.strftime('%H:%M:%S')} did {what_work} \n")
html_out.write(f"processed {len(book_lines)} lines of HTML \n")
html_out.write(f"found {links} links on {link_lines} lines \n")
html_out.write(f"wrote {len(set(lines2print))} lines of HTML output (not including header and footer) \n")
html_out.write("\n\n")
html_out.close()
log_and_print(f"processed {len(book_lines)} lines of HTML")
log_and_print(f"found {links} links on {link_lines} lines")
log_and_print(f"wrote {len(set(lines2print))} lines of HTML output (not including header and footer)")
#code.interact(local=locals())
|