mirror of
https://github.com/quantum5/uwat.cc.git
synced 2025-04-24 11:01:56 -04:00
Convert to using a YAML file to describe links
This commit is contained in:
parent
d1850e44c1
commit
ae93124f5d
30
README.md
30
README.md
|
@ -13,22 +13,28 @@ Everyone is welcome to contribute! Simply send in a pull request with your
|
||||||
useful link, and if it passes quality control, it will be merged and made
|
useful link, and if it passes quality control, it will be merged and made
|
||||||
available to the public.
|
available to the public.
|
||||||
|
|
||||||
To add a link, add the shortcut link and description to the relevant section in
|
To add a link, add find the relevant section in under [`src/links.yml`][3],
|
||||||
[`src/index.html`][3]. You can also add new sections if none of the existing
|
and under the `links` key, add a new item for your link. This item should be a
|
||||||
sections fit the bill.
|
mapping with three keys:
|
||||||
|
|
||||||
To add the redirect, add it to [`src/redirects.conf`][4], into the same place
|
* `name`: the shortcut link, starting with `/`, followed by letters, numbers,
|
||||||
as you did in `src/index.html`. `redirects.conf` is included inside an
|
and `-`;
|
||||||
[nginx `map` block][5], and the syntax is:
|
* `target`: the URL to redirect to; and
|
||||||
|
* `description`: the description of the link shown on the home page.
|
||||||
|
|
||||||
```
|
To be able to run the python scripts locally, run
|
||||||
/shortcut "https://example.com/long/url";
|
`pip install -r requirements.txt` to install our dependencies.
|
||||||
```
|
|
||||||
|
To verify that your changes follow the correct format, run automatic sanity
|
||||||
|
checks with [`python3 check.py`][4].
|
||||||
|
|
||||||
|
To generate the HTML for the site, run [`python3 build.py`][5]. Output will be
|
||||||
|
generated in a directory called `dist`.
|
||||||
|
|
||||||
Thank you for contributing.
|
Thank you for contributing.
|
||||||
|
|
||||||
[1]: https://uwat.cf
|
[1]: https://uwat.cf
|
||||||
[2]: https://uwat.cf/exams
|
[2]: https://uwat.cf/exams
|
||||||
[3]: src/index.html
|
[3]: src/links.yml
|
||||||
[4]: src/redirects.conf
|
[4]: check.py
|
||||||
[5]: https://nginx.org/en/docs/http/ngx_http_map_module.html
|
[5]: build.py
|
||||||
|
|
73
build.py
73
build.py
|
@ -1,7 +1,10 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
import errno
|
import errno
|
||||||
import os
|
import os
|
||||||
|
from html import escape
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
|
|
||||||
|
import yaml
|
||||||
from rcssmin import cssmin
|
from rcssmin import cssmin
|
||||||
|
|
||||||
DIR = os.path.dirname(__file__)
|
DIR = os.path.dirname(__file__)
|
||||||
|
@ -10,8 +13,6 @@ DIST_DIR = os.path.join(DIR, 'dist')
|
||||||
ASSETS_SRC = os.path.join(SRC_DIR, 'assets')
|
ASSETS_SRC = os.path.join(SRC_DIR, 'assets')
|
||||||
ASSETS_DIST = os.path.join(DIST_DIR, 'assets')
|
ASSETS_DIST = os.path.join(DIST_DIR, 'assets')
|
||||||
|
|
||||||
bytes = type(b'')
|
|
||||||
|
|
||||||
|
|
||||||
def build_assets():
|
def build_assets():
|
||||||
name_map = []
|
name_map = []
|
||||||
|
@ -26,29 +27,62 @@ def build_assets():
|
||||||
dist_name = '%s-%s%s' % (name, hash, ext)
|
dist_name = '%s-%s%s' % (name, hash, ext)
|
||||||
|
|
||||||
if ext == '.css':
|
if ext == '.css':
|
||||||
content = cssmin(content)
|
content = cssmin(content.decode('utf-8')).encode('utf-8')
|
||||||
|
|
||||||
with open(os.path.join(ASSETS_DIST, dist_name), 'wb') as f:
|
with open(os.path.join(ASSETS_DIST, dist_name), 'wb') as f:
|
||||||
f.write(content)
|
f.write(content)
|
||||||
name_map.append((bytes(asset), bytes(dist_name)))
|
name_map.append((asset, dist_name))
|
||||||
return name_map
|
return name_map
|
||||||
|
|
||||||
|
|
||||||
def build_files(html_replace):
|
def build_links(links):
|
||||||
for name in os.listdir(SRC_DIR):
|
output = []
|
||||||
src_path = os.path.join(SRC_DIR, name)
|
for section in links['sections']:
|
||||||
if not os.path.isfile(src_path):
|
output.append(' <h2 id="%s">%s</h2>' % (section['id'], escape(section['name'])))
|
||||||
continue
|
output.append(' <ul>')
|
||||||
|
|
||||||
with open(os.path.join(SRC_DIR, name), 'rb') as f:
|
for link in section['links']:
|
||||||
content = f.read()
|
output.append(' <li><a href="{url}">{url}</a> — {description}</li>'.format(
|
||||||
|
url=escape(link['name']), description=escape(link['description'])
|
||||||
|
))
|
||||||
|
|
||||||
if name.endswith('.html'):
|
output.append(' </ul>')
|
||||||
for old, new in html_replace:
|
output.append('')
|
||||||
content = content.replace(old, new)
|
|
||||||
|
|
||||||
with open(os.path.join(DIST_DIR, name), 'wb') as f:
|
return '\n'.join(output)
|
||||||
f.write(content)
|
|
||||||
|
|
||||||
|
def build_redirects(links):
|
||||||
|
output = []
|
||||||
|
|
||||||
|
def build_link(link):
|
||||||
|
output.append('%s "%s";' % (link['name'], link['target']))
|
||||||
|
|
||||||
|
if 'other_links' in links:
|
||||||
|
for link in links['other_links']:
|
||||||
|
build_link(link)
|
||||||
|
output.append('')
|
||||||
|
|
||||||
|
for section in links['sections']:
|
||||||
|
output.append('# %s' % (section['name'],))
|
||||||
|
for link in section['links']:
|
||||||
|
build_link(link)
|
||||||
|
output.append('')
|
||||||
|
|
||||||
|
with open(os.path.join(DIST_DIR, 'redirects.conf'), 'w', encoding='utf-8') as f:
|
||||||
|
f.write('\n'.join(output))
|
||||||
|
|
||||||
|
|
||||||
|
def build_index(html_replace, links):
|
||||||
|
with open(os.path.join(SRC_DIR, 'index.html'), encoding='utf-8') as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
for old, new in html_replace:
|
||||||
|
content = content.replace(old, new)
|
||||||
|
content = content.replace('{listing}', build_links(links))
|
||||||
|
|
||||||
|
with open(os.path.join(DIST_DIR, 'index.html'), 'w', encoding='utf-8') as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
@ -58,8 +92,13 @@ def main():
|
||||||
if e.errno != errno.EEXIST:
|
if e.errno != errno.EEXIST:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
with open(os.path.join(SRC_DIR, 'links.yml'), encoding='utf-8') as f:
|
||||||
|
links = yaml.safe_load(f)
|
||||||
|
|
||||||
name_map = build_assets()
|
name_map = build_assets()
|
||||||
build_files(name_map)
|
build_index(name_map, links)
|
||||||
|
build_redirects(links)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
78
check.py
Normal file
78
check.py
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
SRC_DIR = os.path.join(os.path.dirname(__file__), 'src')
|
||||||
|
|
||||||
|
|
||||||
|
def ensure(cond, output):
|
||||||
|
if not cond:
|
||||||
|
raise SystemExit(output)
|
||||||
|
|
||||||
|
|
||||||
|
def check_link(link, description=True):
|
||||||
|
ensure(isinstance(link, dict), 'a link must be dict, not: %s' % (link,))
|
||||||
|
|
||||||
|
ensure('name' in link, 'a link must contain a name: %s' % (link,))
|
||||||
|
ensure(isinstance(link['name'], str), 'key "name" under link must be string: %s' % (link,))
|
||||||
|
ensure(link['name'].startswith('/'), 'the name of a link must start with /: %s' % (link,))
|
||||||
|
ensure(re.match('^/[a-z0-9-]+$', link['name']),
|
||||||
|
'the name of a link must be / followed by letters, numbers, and -, not %s' % (link['name'],))
|
||||||
|
|
||||||
|
ensure('target' in link, 'link "%s" must contain a target' % (link['name'],))
|
||||||
|
ensure(isinstance(link['target'], str), 'key "target" under link "%s" must be string' % (link['name'],))
|
||||||
|
|
||||||
|
if description:
|
||||||
|
ensure('description' in link, 'link "%s" must contain a description' % (link['name'],))
|
||||||
|
ensure(isinstance(link['description'], str),
|
||||||
|
'key "description" under link "%s" must be string' % (link['name'],))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
with open(os.path.join(SRC_DIR, 'links.yml'), encoding='utf-8') as f:
|
||||||
|
links = yaml.safe_load(f)
|
||||||
|
|
||||||
|
unique = set()
|
||||||
|
|
||||||
|
ensure('sections' in links, 'links.yml should contain key "sections"')
|
||||||
|
ensure(isinstance(links['sections'], list), 'key "sections" should map to a list')
|
||||||
|
|
||||||
|
for section in links['sections']:
|
||||||
|
ensure(isinstance(section, dict), 'every item in "sections" should be a dict')
|
||||||
|
|
||||||
|
ensure('id' in section, 'every section must have an id')
|
||||||
|
ensure(isinstance(section['id'], str), 'section IDs must be strings')
|
||||||
|
ensure(re.match('^[a-z-]+$', section['id']), 'section IDs should only contain lowercase letters and -')
|
||||||
|
|
||||||
|
ensure('name' in section, 'every section must have a name')
|
||||||
|
ensure(isinstance(section['name'], str), 'section names must be strings')
|
||||||
|
|
||||||
|
ensure('links' in section, 'every section must have links')
|
||||||
|
ensure(isinstance(section['links'], list), 'links under %s must be a list' % (section['id'],))
|
||||||
|
|
||||||
|
for link in section['links']:
|
||||||
|
check_link(link)
|
||||||
|
|
||||||
|
if link['name'] in unique:
|
||||||
|
raise SystemExit('duplicate link "%s"' % link['name'])
|
||||||
|
unique.add(link['name'])
|
||||||
|
|
||||||
|
if 'other_links' in links:
|
||||||
|
ensure(isinstance(links['other_links'], list), 'other_links must be a list')
|
||||||
|
|
||||||
|
for link in links['other_links']:
|
||||||
|
check_link(link, description=False)
|
||||||
|
|
||||||
|
if link['name'] in unique:
|
||||||
|
raise SystemExit('duplicate link "%s"' % link['name'])
|
||||||
|
unique.add(link['name'])
|
||||||
|
|
||||||
|
with open(os.path.join(SRC_DIR, 'index.html'), encoding='utf-8') as f:
|
||||||
|
contents = f.read()
|
||||||
|
ensure('{listing}' in contents, 'index.html should have {listing}')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pyyaml
|
||||||
|
rcssmin
|
|
@ -12,36 +12,7 @@
|
||||||
<h1>Useful UWaterloo Links</h1>
|
<h1>Useful UWaterloo Links</h1>
|
||||||
<p><em>All <a href="/link">/link</a>s can be accessed as <a href="https://uwat.cf/link">uwat.cf/link</a>.</em></p>
|
<p><em>All <a href="/link">/link</a>s can be accessed as <a href="https://uwat.cf/link">uwat.cf/link</a>.</em></p>
|
||||||
|
|
||||||
<h2 id="general">General</h2>
|
{listing}
|
||||||
<ul>
|
|
||||||
<li><a href="/learn">/learn</a> — UWaterloo Learn</li>
|
|
||||||
<li><a href="/quest">/quest</a> — Quest</li>
|
|
||||||
<li><a href="/wp">/wp</a> — WatIAM white pages</li>
|
|
||||||
<li><a href="/watcard">/watcard</a> — Manage my WatCard</li>
|
|
||||||
<li><a href="/exams">/exams</a> — Exam seating and schedules</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2 id="math">Faculty of Mathematics</h2>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/marmoset">/marmoset</a> — Marmoset</li>
|
|
||||||
<li><a href="/mathexams">/mathexams</a> — MathSoc Exam Bank</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2 id="eng">Faculty of Engineering</h2>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/engexams">/engexams</a> — EngSoc Exam Bank</li>
|
|
||||||
<li><a href="/engrank">/engrank</a> — Undergrad student rankings</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2 id="coop">Co-operative Education</h2>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/coopcal">/coopcal</a> — Co-op important dates</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2 id="finance">Finances</h2>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/endow">/endow</a> — Endowment refund</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2 id="about">About</h2>
|
<h2 id="about">About</h2>
|
||||||
<p>Want to add more links? Send us a pull request on <a href="https://github.com/quantum5/uwat.cf">GitHub</a>!</p>
|
<p>Want to add more links? Send us a pull request on <a href="https://github.com/quantum5/uwat.cf">GitHub</a>!</p>
|
||||||
|
|
57
src/links.yml
Normal file
57
src/links.yml
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
other_links:
|
||||||
|
- name: /link
|
||||||
|
target: /
|
||||||
|
|
||||||
|
sections:
|
||||||
|
- id: general
|
||||||
|
name: General
|
||||||
|
links:
|
||||||
|
- name: /learn
|
||||||
|
target: https://learn.uwaterloo.ca/d2l/home
|
||||||
|
description: LEARN
|
||||||
|
- name: /quest
|
||||||
|
target: https://quest.pecs.uwaterloo.ca/psp/SS/ACADEMIC/SA/?cmd=login&languageCd=ENG
|
||||||
|
description: Quest
|
||||||
|
- name: /wp
|
||||||
|
target: https://idm.uwaterloo.ca/search/authen/
|
||||||
|
description: WatIAM white pages
|
||||||
|
- name: /watcard
|
||||||
|
target: https://watcard.uwaterloo.ca/OneWeb/Account/LogOn
|
||||||
|
description: Manage my WatCard
|
||||||
|
- name: /exams
|
||||||
|
target: https://odyssey.uwaterloo.ca/teaching/schedule
|
||||||
|
description: Exam seating and schedules
|
||||||
|
|
||||||
|
- id: math
|
||||||
|
name: Faculty of Mathematics
|
||||||
|
links:
|
||||||
|
- name: /marmoset
|
||||||
|
target: https://marmoset.student.cs.uwaterloo.ca/
|
||||||
|
description: Marmoset
|
||||||
|
- name: /mathexams
|
||||||
|
target: http://mathsoc.uwaterloo.ca/exambank
|
||||||
|
description: MathSoc Exam Bank
|
||||||
|
|
||||||
|
- id: eng
|
||||||
|
name: Faculty of Engineering
|
||||||
|
links:
|
||||||
|
- name: /engexams
|
||||||
|
target: https://exams.engsoc.uwaterloo.ca/
|
||||||
|
description: EngSoc Exam Bank
|
||||||
|
- name: /engrank
|
||||||
|
target: https://engug.uwaterloo.ca/
|
||||||
|
description: Undergrad student rankings
|
||||||
|
|
||||||
|
- id: coop
|
||||||
|
name: Co-operative Education
|
||||||
|
links:
|
||||||
|
- name: /coopcal
|
||||||
|
target: https://uwaterloo.ca/co-operative-education/important-dates
|
||||||
|
description: Co-op important dates
|
||||||
|
|
||||||
|
- id: finance
|
||||||
|
name: Finances
|
||||||
|
links:
|
||||||
|
- name: /endow
|
||||||
|
target: https://uwaterloo.ca/forms/finance/user?destination=endowment_request
|
||||||
|
description: Endowment refund
|
|
@ -1,23 +0,0 @@
|
||||||
# Example link
|
|
||||||
/link "https://uwat.cf/";
|
|
||||||
|
|
||||||
# General
|
|
||||||
/learn "https://learn.uwaterloo.ca/d2l/home";
|
|
||||||
/quest "https://quest.pecs.uwaterloo.ca/psp/SS/ACADEMIC/SA/?cmd=login&languageCd=ENG";
|
|
||||||
/wp "https://idm.uwaterloo.ca/search/authen/";
|
|
||||||
/watcard "https://watcard.uwaterloo.ca/OneWeb/Account/LogOn";
|
|
||||||
/exams "https://odyssey.uwaterloo.ca/teaching/schedule";
|
|
||||||
|
|
||||||
# Faculty of Math
|
|
||||||
/marmoset "https://marmoset.student.cs.uwaterloo.ca/";
|
|
||||||
/mathexams "http://mathsoc.uwaterloo.ca/exambank";
|
|
||||||
|
|
||||||
# Faculty of Engineering
|
|
||||||
/engexams "https://exams.engsoc.uwaterloo.ca/";
|
|
||||||
/engrank "https://engug.uwaterloo.ca/";
|
|
||||||
|
|
||||||
# Co-op
|
|
||||||
/coopcal "https://uwaterloo.ca/co-operative-education/important-dates";
|
|
||||||
|
|
||||||
# Finances
|
|
||||||
/endow "https://uwaterloo.ca/forms/finance/user?destination=endowment_request";
|
|
Loading…
Reference in a new issue