-
Notifications
You must be signed in to change notification settings - Fork 0
/
build.py
216 lines (190 loc) · 7.93 KB
/
build.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# -*- coding: utf-8 -*-
from staticjinja import Site
import os,json
from shutil import rmtree, move
from PIL import Image
import requests
from io import BytesIO
from urllib.parse import urlparse
import favicon
from base64 import b64encode
def createManifest(subjectPath = None, subjectTitle = None, color = None, background_color = None, toBasePath = '.'):
data = {
"name": "Wuel Quick Links",
"short_name": "Wuel",
"description": "Quick links for students at the university würzburg to save you time",
"theme_color": "#fff4f4",
"background_color": "#004188",
"display": "minimal-ui",
"orientation": "portrait",
"scope": "/",
"start_url": "/",
"icons": [
{
"src": toBasePath + "/static/icons/maskable_icon_x48.png",
"sizes": "48x48",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": toBasePath + "/static/icons/maskable_icon_x72.png",
"sizes": "72x72",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": toBasePath + "/static/icons/maskable_icon_x128.png",
"sizes": "128x128",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": toBasePath + "/static/icons/maskable_icon_x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": toBasePath + "/static/icons/maskable_icon_x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
]
}
if subjectPath and subjectTitle and color and background_color:
data["name"] = "Wuel - " + subjectTitle + " Links"
data["short_name"] = "Wuel - " + subjectPath
data["description"] = "Quick links for " + subjectTitle + " students at the university würzburg to save you time"
data["theme_color"] = color
data["background_color"] = background_color
data["start_url"] = "/" + subjectPath + "/"
data["scope"] = "/" + subjectPath + "/"
base_path = "dist/"
filename = "manifest.json"
if subjectPath:
path = base_path + subjectPath + "/" + filename
print("Creating manifest.json for " + subjectPath + "...")
else:
path = base_path + filename
print("Creating manifest.json for index...")
# write json file for web manifest
with open(path, 'w', encoding='utf-8') as outfile:
json.dump(data, outfile, ensure_ascii=False)
iconsDownloadedUrls = []
def cacheDownloadAndRelinkImages(data):
for index, link in enumerate(data["links"]):
# parse icon name with and without extensions
try:
icon_url = link["iconUrl"]
except KeyError as e:
icon_url = None
# use library to find iconUrl if none given
icons = favicon.get(link["href"])
for icon in icons:
# else have horde problem
# gets first valid so best quality favicon
if ".php?url=" not in icon.url:
icon_url = icon.url
break
# found no valid iconUrl
if not icon_url:
print("Found no favicon for " + link["href"])
print(icons)
continue
# could also use new webp or other next gen formats
# but webP not supported in safari
# thanks to https://stackoverflow.com/a/27253809
icon_name_new_extension = b64encode(icon_url.encode()).decode() + '.png'
webLink = '/static/' + icon_name_new_extension
# create dist folder if new and construct path
static_path = 'dist/static/'
if not os.path.exists(static_path):
os.makedirs(static_path)
path = static_path + icon_name_new_extension
# get and cache iconImages or relink if existing
if icon_url in iconsDownloadedUrls:
data["links"][index]["iconUrl"] = webLink
else:
response = requests.get(icon_url)
if response and response.ok:
print("Cached " + icon_url + "...")
img = Image.open(BytesIO(response.content))
# resize to 150px width and height
basewidth = 150
# would be to calculate height via aspect ratio
# wpercent = (basewidth/float(img.size[0]))
# hsize = int((float(img.size[1])*float(wpercent)))
# img = img.resize((basewidth,hsize), Image.ANTIALIAS)
img = img.resize((basewidth,basewidth), Image.ANTIALIAS)
# convert to rgba for png file, save and relink
img.convert('RGBA')
img.save(path)
data["links"][index]["iconUrl"] = webLink
iconsDownloadedUrls.append(icon_url)
return data
def renderSubjectPages():
# renders pages for all subjects
path_to_subject_jsons = './subjects/'
subjectLinks = []
for file_name in [file for file in os.listdir(path_to_subject_jsons) if file.endswith('.json')]:
subject = file_name.replace('.json', '')
with open(path_to_subject_jsons + file_name, encoding='utf-8') as json_file:
data = json.load(json_file)
# download all linked images and chang links to them to use cached local ones
data = cacheDownloadAndRelinkImages(data)
title = data["title"]
subjectLinks.append({
"name": title,
"href": "/" + subject + "/"
})
print("-------- Rendering " + subject + "----------")
site = Site.make_site(
searchpath="./templates/subjects",
outpath="dist/" + subject,
env_globals=data)
site.render()
createManifest(subject,data["title"],data["color"], data["backgroundColor"], '..')
createLinksJson(data["links"], subject)
# have to move this service worker file because of scope reasons into base folder not keep in static
move(f"dist/{subject}/static/subject-files-cache-worker.js", f"dist/{subject}/subject-files-cache-worker.js")
return subjectLinks
def renderHomepage(subjectLinks):
# homepage site to copy static files
# and link to all subjects
print("===> Rendering homepage...")
site = Site.make_site(
searchpath="./templates/main",
outpath="dist/",
staticpaths=["static/"],
env_globals={
"title": "Hauptseite",
"links": subjectLinks
})
site.render()
# don't actually need a manifest for the index page
# only useful for specifc course with offline caching and so on
# would be uneeded clicks anyways
# createManifest()
def createCoursesJson(subjectLinks):
courseJsonPath = 'dist/courses.json'
# write json file for dropdown of courses links in wuex extension
with open(courseJsonPath, 'w', encoding='utf-8') as outfile:
print("Creating courses.json...")
json.dump(subjectLinks, outfile)
def createLinksJson(servicesLinks, courseName):
linkJsonPath = f"dist/{courseName}/links.json"
# write json file with all links available to course
# can be used by apis and bots as api without parsing
with open(linkJsonPath, 'w', encoding='utf-8') as outfile:
print(f"Creating links.json for {courseName}...")
json.dump(servicesLinks, outfile)
if __name__ == "__main__":
try:
# clear dist folder and does not ignore errors
rmtree('./dist/', False)
except Exception as e:
print(e)
subjectLinks = renderSubjectPages()
renderHomepage(subjectLinks)
createCoursesJson(subjectLinks)