# -*- coding: utf-8 -*-
from __future__ import print_function
import sys
import re
import itertools
import email.utils
import os.path
import time
import codecs
from datetime import datetime
# -----------------------------------------------------------------------------
# Python 2/3 hacks
# -----------------------------------------------------------------------------
PY3 = sys.version_info[0] == 3
if PY3:
import urllib
import urllib.request
def urlparse_foo(link):
return urllib.parse.parse_qs(urllib.parse.urlparse(link).query)['v'][0]
else:
import urllib
import urlparse
def urlparse_foo(link):
return urlparse.parse_qs(urlparse.urlparse(link).query)['v'][0]
# -----------------------------------------------------------------------------
# config "system"
# -----------------------------------------------------------------------------
conf = {
"default_lang": "en",
"base_url": "https://www.xythobuz.de",
"birthday": datetime(1994, 1, 22, 0, 0),
"blog_years_back": 6,
}
def get_conf(name):
return conf[name]
# -----------------------------------------------------------------------------
# local vars for compatibility
# -----------------------------------------------------------------------------
DEFAULT_LANG = get_conf("default_lang")
BASE_URL = get_conf("base_url")
# -----------------------------------------------------------------------------
# birthday calculation
# -----------------------------------------------------------------------------
from datetime import timedelta
from calendar import isleap
size_of_day = 1. / 366.
size_of_second = size_of_day / (24. * 60. * 60.)
def date_as_float(dt):
days_from_jan1 = dt - datetime(dt.year, 1, 1)
if not isleap(dt.year) and days_from_jan1.days >= 31+28:
days_from_jan1 += timedelta(1)
return dt.year + days_from_jan1.days * size_of_day + days_from_jan1.seconds * size_of_second
def difference_in_years(start_date, end_date):
return int(date_as_float(end_date) - date_as_float(start_date))
def own_age():
return difference_in_years(get_conf("birthday"), datetime.now())
# -----------------------------------------------------------------------------
# sub page helper macro
# -----------------------------------------------------------------------------
def backToParent():
# check for special parent cases
posts = []
if page.get("show_in_quadcopters", "false") == "true":
posts = [p for p in pages if p.url == "quadcopters.html"]
# if not, check for actual parent
if len(posts) == 0:
url = page.get("parent", "") + ".html"
posts = [p for p in pages if p.url == url]
# print if any parent link found
if len(posts) > 0:
p = posts[0]
print('[...back to ' + p.title + ' overview](' + p.url + ')')
# -----------------------------------------------------------------------------
# table helper macro
# -----------------------------------------------------------------------------
def tableHelper(style, header, content):
print("
")
if (header != None) and (len(header) == len(style)):
print("
")
for h in header:
print("
" + h + "
")
print("
")
for ci in range(0, len(content)):
if len(content[ci]) != len(style):
# invalid call of table helper!
continue
print("
")
for i in range(0, len(style)):
s = style[i]
td_style = ""
if "monospaced" in s:
td_style += " font-family: monospace;"
if "align-last-right" in s:
if ci == (len(content) - 1):
td_style += " text-align: right;"
else:
if "align-center" in s:
td_style += " text-align: center;"
elif "align-right" in s:
td_style += " text-align: right;"
elif "align-center" in s:
td_style += " text-align: center;"
td_args = ""
if td_style != "":
td_args = " style=\"" + td_style + "\""
print("
")
if isinstance(content[ci][i], tuple):
text, link = content[ci][i]
print("" + text + "")
else:
text = content[ci][i]
print(text)
print("
")
print("
")
print("
")
# -----------------------------------------------------------------------------
# menu helper macro
# -----------------------------------------------------------------------------
def githubCommitBadge(p, showInline = False):
ret = ""
if p.get("github", "") != "":
link = p.get("git", p.github)
linkParts = p.github.split("/")
if len(linkParts) >= 5:
ret += ""
return ret
def printMenuItem(p, yearsAsHeading = False, showDateSpan = False, showOnlyStartDate = False, nicelyFormatFullDate = False, lastyear = "0", lang = "", showLastCommit = True, hide_description = False, updates_as_heading = False):
title = p.title
if lang != "":
if p.get("title_" + lang, "") != "":
title = p.get("title_" + lang, "")
if title == "Blog":
title = p.post
if updates_as_heading:
year = p.get("update", p.get("date", ""))[0:4]
else:
year = p.get("date", "")[0:4]
if year != lastyear:
lastyear = year
if yearsAsHeading:
print("
" + str(year) + "
")
dateto = ""
if p.get("date", "" != ""):
year = p.get("date", "")[0:4]
if showOnlyStartDate:
dateto = " (%s)" % (year)
if p.get("update", "") != "" and p.get("update", "")[0:4] != year:
if showDateSpan:
dateto = " (%s - %s)" % (year, p.get("update", "")[0:4])
if nicelyFormatFullDate:
dateto = " - " + datetime.strptime(p.get("update", p.date), "%Y-%m-%d").strftime("%B %d, %Y")
print("
")
print("" + title + "" + dateto)
if hide_description == False:
if p.get("description", "") != "":
description = p.get("description", "")
if lang != "":
if p.get("description_" + lang, "") != "":
description = p.get("description_" + lang, "")
print(" " + description + "")
if showLastCommit:
link = githubCommitBadge(p)
if len(link) > 0:
print(" " + link)
print("
")
return lastyear
def printRecentMenu(count = 5):
posts = [p for p in pages if "date" in p and p.lang == "en"]
posts.sort(key=lambda p: p.get("update", p.get("date")), reverse=True)
if count > 0:
posts = posts[0:count]
print("
")
lastyear = "0"
for p in posts:
lastyear = printMenuItem(p, count == 0, False, False, True, lastyear, "", False, False, True)
print("
")
def printBlogMenu(year_min=None, year_max=None):
posts = [p for p in pages if "post" in p and p.lang == "en"]
posts.sort(key=lambda p: p.get("date", "9999-01-01"), reverse=True)
if year_min != None:
posts = [p for p in posts if int(p.get("date", "9999-01-01")[0:4]) >= int(year_min)]
if year_max != None:
posts = [p for p in posts if int(p.get("date", "9999-01-01")[0:4]) <= int(year_max)]
print("
")
lastyear = "0"
for p in posts:
lastyear = printMenuItem(p, True, False, False, True, lastyear)
print("
")
def printProjectsMenu():
# prints all pages with parent 'projects' or 'stuff'.
# first the ones without date, sorted by position.
# this first section includes sub-headings for children
# then afterwards those with date, split by year.
# also supports blog posts with parent.
enpages = [p for p in pages if p.lang == "en"]
# select pages without date
dpages = [p for p in enpages if p.get("date", "") == ""]
# only those that have a parent in ['projects', 'stuff']
mpages = [p for p in dpages if any(x in p.get("parent", "") for x in [ 'projects', 'stuff' ])]
# sort by position
mpages.sort(key=lambda p: [int(p.get("position", "999"))])
print("
")
# print all pages
for p in mpages:
printMenuItem(p)
# print subpages for these top-level items
subpages = [sub for sub in enpages if sub.get("parent", "none") == p.get("child-id", "unknown")]
if len(subpages) > 0:
print("
")
for sp in subpages:
printMenuItem(sp, False, True, True, False, "0", "", False, True)
print("
")
# slect pages with a date
dpages = [p for p in enpages if p.get("date", "") != ""]
# only those that have a parent in ['projects', 'stuff']
mpages = [p for p in dpages if any(x in p.get("parent", "") for x in [ 'projects', 'stuff' ])]
# sort by date
mpages.sort(key=lambda p: [p.get("date", "9999-01-01")], reverse = True)
# print all pages
lastyear = "0"
for p in mpages:
lastyear = printMenuItem(p, True, True, False, False, lastyear)
# print subpages for these top-level items
subpages = [sub for sub in enpages if sub.get("parent", "none") == p.get("child-id", "unknown")]
subpages.sort(key=lambda p: [p.get("date", "9999-01-01")], reverse = True)
if len(subpages) > 0:
print("
")
for sp in subpages:
printMenuItem(sp, False, True, True, False, "0", "", False, True)
print("
")
print("
")
def print3DPrintingMenu():
mpages = [p for p in pages if p.get("parent", "") == "3d-printing" and p.lang == "en"]
mpages.sort(key=lambda p: int(p["position"]))
print("
")
for p in mpages:
printMenuItem(p, False, True, True)
print("
")
def printInputDevicesMenu():
mpages = [p for p in pages if p.get("parent", "") == "input_devices" and p.lang == "en"]
mpages.sort(key=lambda p: [p.get("date", "9999-01-01")], reverse = True)
print("
")
for p in mpages:
printMenuItem(p, False, True, True)
print("
")
def printInputDevicesRelatedMenu():
mpages = [p for p in pages if p.get("show_in_input_devices", "false") == "true"]
mpages.sort(key=lambda p: [p.get("date", "9999-01-01")], reverse = True)
print("
")
for p in mpages:
printMenuItem(p, False, True, True)
print("
")
def printSmarthomeMenu():
mpages = [p for p in pages if p.get("parent", "") == "smarthome" and p.lang == "en"]
mpages.sort(key=lambda p: int(p["position"]))
print("
")
for p in mpages:
printMenuItem(p, False, True, True)
print("
")
def printQuadcopterMenu():
mpages = [p for p in pages if p.get("parent", "") == "quadcopters" and p.lang == "en"]
mpages.sort(key=lambda p: int(p["position"]))
print("
")
for p in mpages:
printMenuItem(p, False, True, True)
print("
")
def printQuadcopterRelatedMenu():
mpages = [p for p in pages if p.get("show_in_quadcopters", "false") == "true"]
mpages.sort(key=lambda p: [p.get("date", "9999-01-01")], reverse = True)
print("
")
for p in mpages:
printMenuItem(p, False, True, True)
print("
")
def printRobotMenuEnglish():
mpages = [p for p in pages if p.get("parent", "") == "xyrobot" and p.lang == "en"]
mpages.sort(key=lambda p: int(p["position"]))
print("
")
for p in mpages:
printMenuItem(p)
print("
")
def printRobotMenuDeutsch():
mpages = [p for p in pages if p.get("parent", "") == "xyrobot" and p.lang == "de"]
mpages.sort(key=lambda p: int(p["position"]))
print("
")
for p in mpages:
printMenuItem(p, False, False, False, False, "0", "de")
print("
")
def printSteamMenuEnglish():
mpages = [p for p in pages if p.get("parent", "") == "steam" and p.lang == "en"]
mpages.sort(key=lambda p: [p.get("date", "9999-01-01")], reverse = True)
print("
")
for p in mpages:
printMenuItem(p, False, False, False, True)
print("
")
def printSteamMenuDeutsch():
# TODO show german pages, or english pages when german not available
printSteamMenuEnglish()
# -----------------------------------------------------------------------------
# lightgallery helper macro
# -----------------------------------------------------------------------------
# call this macro like this:
# lightgallery([
# [ "image-link", "description" ],
# [ "image-link", "thumbnail-link", "description" ],
# [ "youtube-link", "thumbnail-link", "description" ],
# [ "video-link", "mime", "thumbnail-link", "image-link", "description" ],
# [ "video-link", "mime", "", "", "description" ],
# ])
# it will also auto-generate thumbnails and resize and strip EXIF from images
# using the included web-image-resize script.
# and it can generate video thumbnails and posters with the video-thumb script.
def lightgallery_check_thumbnail(link, thumb):
# only check local image links
if not link.startswith('img/'):
return
# generate thumbnail filename web-image-resize will create
x = link.rfind('.')
img = link[:x] + '_small' + link[x:]
# only run when desired thumb path matches calculated ones
if thumb != img:
return
# generate fs path to images
path = os.path.join(os.getcwd(), 'static', link)
img = os.path.join(os.getcwd(), 'static', thumb)
# no need to generate thumb again
if os.path.exists(img):
return
# run web-image-resize to generate thumbnail
script = os.path.join(os.getcwd(), 'web-image-resize')
os.system(script + ' ' + path)
def lightgallery_check_thumbnail_video(link, thumb, poster):
# only check local image links
if not link.startswith('img/'):
return
# generate thumbnail filenames video-thumb will create
x = link.rfind('.')
thumb_l = link[:x] + '_thumb.png'
poster_l = link[:x] + '_poster.png'
# only run when desired thumb path matches calculated ones
if (thumb_l != thumb) or (poster_l != poster):
return
# generate fs path to images
path = os.path.join(os.getcwd(), 'static', link)
thumb_p = os.path.join(os.getcwd(), 'static', thumb)
poster_p = os.path.join(os.getcwd(), 'static', poster)
# no need to generate thumb again
if os.path.exists(thumb_p) or os.path.exists(poster_p):
return
# run video-thumb to generate thumbnail
script = os.path.join(os.getcwd(), 'video-thumb')
os.system(script + ' ' + path)
def lightgallery(links):
global v_ii
try:
v_ii += 1
except NameError:
v_ii = 0
videos = [l for l in links if len(l) == 5]
v_i = -1
for v in videos:
link, mime, thumb, poster, alt = v
v_i += 1
print('
')
print('')
print('
')
print('
')
v_i = -1
for l in links:
if (len(l) == 3) or (len(l) == 2):
link = img = alt = ""
style = img2 = ""
if len(l) == 3:
link, img, alt = l
else:
link, alt = l
if "youtube.com" in link:
img = "https://img.youtube.com/vi/"
img += urlparse_foo(link)
img += "/0.jpg" # full size preview
#img += "/default.jpg" # default thumbnail
style = ' style="width:300px;"'
img2 = ''
else:
x = link.rfind('.')
img = link[:x] + '_small' + link[x:]
lightgallery_check_thumbnail(link, img)
print('
')
elif len(l) == 5:
v_i += 1
link, mime, thumb, poster, alt = videos[v_i]
if len(thumb) <= 0:
x = link.rfind('.')
thumb = link[:x] + '_thumb.png'
if len(poster) <= 0:
x = link.rfind('.')
poster = link[:x] + '_poster.png'
lightgallery_check_thumbnail_video(link, thumb, poster)
print('
')
else:
raise NameError('Invalid number of arguments for lightgallery')
print('
')
# -----------------------------------------------------------------------------
# github helper macros
# -----------------------------------------------------------------------------
import json, sys
def restRequest(url):
response = urllib.request.urlopen(url) if PY3 else urllib.urlopen(url)
if response.getcode() != 200:
sys.stderr.write("\n")
sys.stderr.write("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n")
sys.stderr.write("!!!!!!! WARNING !!!!!\n")
sys.stderr.write("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n")
sys.stderr.write("invalid response code: " + str(response.getcode()) + "\n")
sys.stderr.write("url: \"" + url + "\"\n")
sys.stderr.write("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n")
sys.stderr.write("!!!!!!! WARNING !!!!!\n")
sys.stderr.write("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n")
sys.stderr.write("\n")
return ""
data = json.loads(response.read().decode("utf-8"))
return data
def restReleases(user, repo):
s = "https://api.github.com/repos/"
s += user
s += "/"
s += repo
s += "/releases"
return restRequest(s)
def printLatestRelease(user, repo):
repo_url = "https://github.com/" + user + "/" + repo
print("
")
print("Release builds for " + repo + " are available on GitHub. \n")
releases = restReleases(user, repo)
if len(releases) <= 0:
print("No release has been published on GitHub yet.")
print("
")
return
releases.sort(key=lambda x: x["published_at"], reverse=True)
r = releases[0]
release_url = r["html_url"]
print("Latest release of " + repo + ", at the time of this writing: " + r["name"] + " (" + datetime.strptime(r["published_at"], "%Y-%m-%dT%H:%M:%SZ").strftime("%Y-%m-%d %H:%M:%S") + ")\n")
if len(r["assets"]) <= 0:
print(" No release assets have been published on GitHub for that.")
print("")
return
print("
")
print("Release Assets:")
for a in r["assets"]:
size = int(a["size"])
ss = " "
if size >= (1024 * 1024):
ss += "(%.1f MiB)" % (size / (1024.0 * 1024.0))
elif size >= 1024:
ss += "(%d KiB)" % (size // 1024)
else:
ss += "(%d Byte)" % (size)
print("