from urllib.parse import urlparse
import webbrowser
import database
import argparse
import getpass
import app
import sys
import re
import os
# DISCLAIMER
# There is almost certainly a python package to
# do this better. I wanted to do it myself as a challenge.
# TODO:
# - Add table formatting
HEADER_INCREMENTER = 1
IMAGE_TYPES = [".png", ".jpg"]
def get_thought_from_id(db, id_):
category_name, title, dt, markdown = db.get_thought(id_)
return category_name, title, dt, parse_text(markdown)
def parse_file(path):
with open(path, "r") as f:
unformatted = f.read()
return parse_text(unformatted)
def parse_text(unformatted):
formatted = parse_headers(unformatted)
formatted = parse_asteriscs(formatted)
formatted = parse_links(formatted)
formatted = parse_code(formatted)
formatted = parse_lists(formatted)
formatted = add_linebreaks(formatted)
return formatted
def parse_headers(test_str):
regex = r"^#{1,5}\s\w.*$"
matches = re.finditer(regex, test_str, re.MULTILINE)
offset = 0
for match in matches:
# work out if its h2, h3 etc. from the number of #s
headerNo = len(match.group().split(" ")[0]) + HEADER_INCREMENTER
replacement = "%s" % (headerNo, " ".join(match.group().split(" ")[1:]), headerNo)
#don't use .replace() in the unlikely case the the regex hit appears in a block
test_str = test_str[:match.start()+offset] + replacement + test_str[match.end()+offset:]
#replacing the hits fucks up the indexes, accommodate for this
offset += (len(replacement) - (match.end() - match.start()))
return test_str
def parse_asteriscs(test_str):
regex = r"(?%s" % (match.group()[3:-3])
elif match.group().startswith("**"):
replacement = "%s" % (match.group()[2:-2])
else:
replacement = "%s" % (match.group()[1:-1])
test_str = test_str[:match.start()+offset] + replacement + test_str[match.end()+offset:]
offset += (len(replacement) - (match.end() - match.start()))
return test_str
def parse_links(test_str):
regex = r"(?" % (label, url)
else:
replacement = "%s" % (url, label)
test_str = test_str[:match.start()+offset] + replacement + test_str[match.end()+offset:]
offset += (len(replacement) - (match.end() - match.start()))
return test_str
def parse_code(test_str):
regex = r"(?%s" % match.group()[1:-1]
test_str = test_str[:match.start()+offset] + replacement + test_str[match.end()+offset:]
offset += (len(replacement) - (match.end() - match.start()))
out = ""
inBlock = 0
for line in test_str.split("\n"):
if line == "```":
if inBlock % 2 == 0:
out += "
\n"
else:
out += "
\n"
inBlock += 1
else:
out += line + "\n"
return out
def parse_lists(test_str):
regex = r"^[1-9][.)] .*$|- .*$"
matches = re.finditer(regex, test_str, re.MULTILINE)
offset = 0
theFirstOne = True
for match in matches:
if theFirstOne:
if match.group()[0].isdigit():
listType = "ol"
cutoff = 3
else:
listType = "ul"
cutoff = 2
replacement = "<%s>\n%s" % (listType, match.group()[cutoff:])
theFirstOne = False
else:
if re.match(regex, [i for i in test_str[match.end()+offset:].split("\n") if i != ''][0]) is None:
theFirstOne = True
replacement = "%s\n%s>" % (match.group()[cutoff:], listType)
else:
replacement = "%s" % match.group()[cutoff:]
test_str = test_str[:match.start()+offset] + replacement + test_str[match.end()+offset:]
offset += (len(replacement) - (match.end() - match.start()))
return test_str
def add_linebreaks(test_str):
return re.sub(r"^$", "
", test_str, 0, re.MULTILINE)
def preview_markdown(path, title, category):
def startBrowser():
webbrowser.get("firefox").open("http://localhost:5000/preview")
del os.environ["PREVIEW"]
del os.environ["PREVIEW_TITLE"]
del os.environ["CATEGORY"]
os.environ["PREVIEW"] = parse_file(path)
os.environ["PREVIEW_TITLE"] = title
os.environ["CATEGORY"] = category
import threading
threading.Timer(1.25, startBrowser ).start()
app.app.run(host = "0.0.0.0", debug = True)
def main():
p = argparse.ArgumentParser()
subparse = p.add_subparsers(help = "sub-command help")
save_parser = subparse.add_parser("save", help = "Add a markdown file to the database")
preview_parser = subparse.add_parser("preview", help = "Preview a markdown render")
echo_parser = subparse.add_parser("echo", help = "Print markdown render to stdout")
update_parser = subparse.add_parser("update", help = "Replace a markdown file")
export_parser = subparse.add_parser("export", help = "Export a database markdown file to disk")
for s in [save_parser, preview_parser, echo_parser, update_parser]:
s.add_argument(
"-m", "--markdown",
help = "Path to a markdown file",
type = str,
required = True
)
for s in [save_parser, preview_parser]:
s.add_argument(
"-t", "--title",
help = "Article title",
type = str,
required = True
)
s.add_argument(
"-c", "--category",
help = "Article category",
type = str,
required = True
)
for s in [save_parser, update_parser, export_parser]:
s.add_argument(
"-u", "--username",
help = "Username to use for the database",
type = str,
required = True
)
for s in [export_parser, update_parser]:
s.add_argument(
"-i", "--id",
help = "Article's id",
type = int,
required = True
)
export_parser.add_argument(
"-o", "--out",
help = "Path to write the markdown file to",
type = str,
required = True
)
args = vars(p.parse_args())
if "username" in args.keys():
args["password"] = getpass.getpass("Enter password for %s@%s: " % (args["username"], app.CONFIG["mysql"]["host"]))
try:
verb = sys.argv[1]
except IndexError:
print("No verb specified... Nothing to do... Exiting...")
exit()
if verb in ["save", "export", "update"]:
with database.Database(
safeLogin = False,
user = args["username"],
passwd = args["password"]
) as db:
if verb == "save":
if db.add_category(args["category"]):
print("Added category...")
with open(args["markdown"], "r") as f:
db.add_thought(args["category"], args["title"], f.read())
print("Added thought...")
elif verb == "export":
with open(args["out"], "w") as f:
f.writelines(db.get_thought(args["id"])[-1])
print("Written to %s" % args["out"])
elif verb == "update":
with open(args["markdown"], "r") as f:
db.update_thought_markdown(args["id"], f.read())
if verb == "preview":
preview_markdown(args["markdown"], args["title"], args["category"])
elif verb == "echo":
print(parse_file(args["markdown"]))
if __name__ == "__main__":
main()