aboutsummaryrefslogtreecommitdiffstats
path: root/printIssues.py
diff options
context:
space:
mode:
Diffstat (limited to 'printIssues.py')
-rw-r--r--printIssues.py174
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()