diff options
Diffstat (limited to 'printIssues.py')
-rw-r--r-- | printIssues.py | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/printIssues.py b/printIssues.py new file mode 100644 index 0000000..e849567 --- /dev/null +++ b/printIssues.py @@ -0,0 +1,174 @@ +from urllib.parse import urlparse +from dataclasses import dataclass +import datetime +import tempfile +import requests +import logging +import pickle +import pdfkit +import jinja2 +import json +import cups +import os + +logging.basicConfig( + format = "%(levelname)s\t[%(asctime)s]\t%(message)s", + level = logging.INFO, + handlers=[ + logging.FileHandler("github_printer.log"), + logging.StreamHandler() + ]) + +@dataclass +class RenderedIssue(tempfile.TemporaryDirectory): + + gh_api_key: str + issue: dict + + # this really should be done by @dataclass imo... + def __post_init__(self): + super().__init__() + + def __enter__(self): + self.pdf_path = os.path.join(self.name, "out.pdf") + + with open(os.path.join(os.path.split(__file__)[0], "issue.html.j2"), "r") as f: + jinja_template = jinja2.Template(f.read()) + + gfm_html = gfm_to_html( + self.gh_api_key, self.issue["body"], get_context_from_html_url(self.issue["html_url"]) + ) + html = jinja_template.render(**self.issue, gfm_html = gfm_html) + + pdfkit.from_string(html, self.pdf_path, options = json.loads(os.environ["WKHTMLTOPDF_OPTS"])) + + return self.pdf_path + + def __exit__(self, exc, value, tb): + self.cleanup() + +# we're not inside docker- add the environment variables from the file +if not os.path.exists("/.dockerenv") and os.path.exists("githubPrinter.env"): + from dotenv import load_dotenv + load_dotenv("githubPrinter.env") + logging.info("Not being run in docker. Adding environment variables...") + +cups.setUser(os.environ["CUPS_USER"]) +cups.setPasswordCB(lambda a: os.environ["CUPS_PASSWD"]) +cups.setServer(os.environ["CUPS_HOST"]) +conn = cups.Connection() +logging.info( + "Successfully connected to CUPS server. The avaliable printers are %s" + % ", ".join(conn.getPrinters().keys()) +) +logging.info( + "The printer selected to print, '%s', to has details %s" % ( + os.environ["CUPS_PRINTER"], + json.dumps(conn.getPrinters()[os.environ["CUPS_PRINTER"]], indent=4) + ) +) + +def get_user_repos(gh_api_key: str, gh_user: str): + req = requests.get( + "https://api.github.com/users/%s/repos" % gh_user, + headers = { + "Authorization": "token %s" % gh_api_key, + "Accept": "application/vnd.github+json" + } + ) + if req.status_code == 200: + repos = req.json() + + return [get_suffix_from_issues_irl(r["issues_url"]) for r in repos] + + else: + logging.error("Request 'get_user_repos' '%s' failed with status code %d. Request returned %s" % ( + gh_user, req.status_code, req.text + )) + +def get_issues_for(gh_api_key: str, url_suffix: str, since: datetime.datetime) -> [dict]: + # logging.info("Searching for issues in %s..." % url_suffix) + req = requests.get( + "https://api.github.com%s" % url_suffix, + headers = { + "Authorization": "token %s" % gh_api_key, + "Accept": "application/vnd.github+json" + }, + params = { + "since": since.replace(microsecond = 0).isoformat() + } + ) + if req.status_code == 200: + return req.json() + else: + logging.error("Request 'get_issues_for' '%s' failed with status code %d. Request returned %s" % ( + url_suffix, req.status_code, req.text + )) + +def get_context_from_html_url(html_url: str) -> str: + return "/".join(urlparse(html_url).path.split("/")[1:3]) + +def get_suffix_from_issues_irl(issues_url: str) -> str: + return urlparse(issues_url).path.replace("{/number}", "") + +def gfm_to_html(gh_api_key: str, md_text: str, context: str): + req = requests.post( + "https://api.github.com/markdown", + headers = { + "Authorization": gh_api_key, + "Accept": "application/vnd.github+json" + }, + json = { + "mode": "gfm", + "context": context, + "text": md_text + } + ) + if req.status_code == 200: + return req.text + else: + logging.error("Request 'gfm_to_html' failed with status code %d. Request returned %s" % ( + req.status_code, req.text + )) + +def print_file(file_path: str, actually_print: bool = True): + if actually_print: + conn.printFile( + os.environ["CUPS_PRINTER"], file_path, + "GitHub printer job", json.loads(os.environ["CUPS_OPTS"]) + ) + logging.info("Sent file %s to the printer..." % file_path) + logging.info("The printer queue is now %s" % json.dumps(conn.getJobs(), indent = 4)) + +def main(): + if not os.path.exists(".last_checked_at"): + since = datetime.datetime(1970, 1, 1) + else: + with open(".last_checked_at", "rb") as f: + since = pickle.load(f) + + logging.info("Last checked at %s" % since.replace(microsecond=0).isoformat()) + the_next_since = datetime.datetime.now() + + repos = get_user_repos(os.environ["GITHUB_TOKEN"], os.environ["GITHUB_USER"]) + logging.info("Found %i repositories to search for issues in..." % len(repos)) + issues = [] + for repo in repos: + issues += get_issues_for(os.environ["GITHUB_TOKEN"], repo, since) + + logging.info("Found %i issues..." % len(issues)) + + for issue in issues: + logging.info("Going to try and render and print issue '%s #%d' in repo '%s'..." % (issue["title"], issue["number"], issue["html_url"])) + + with RenderedIssue(os.environ["GITHUB_TOKEN"], issue) as rendered_pdf: + print_file(rendered_pdf, False) + + with open(".last_checked_at", "wb") as f: + pickle.dump(the_next_since, f) + + logging.info("Run finished. Marked the last time checked as '%s'." % the_next_since.isoformat()) + + +if __name__ == "__main__": + main() |