diff --git a/README.md b/README.md index 5e97b69..5983175 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# Mathematics-and-Artifice +# Mathematics & Artifice Site logics of "Mathematics & Artifice" \ No newline at end of file diff --git a/conf.edit-me.yml b/conf.edit-me.yml new file mode 100644 index 0000000..08bc866 --- /dev/null +++ b/conf.edit-me.yml @@ -0,0 +1,9 @@ +site: 'Mathematics & Artifice' +template: 'template/' +content: 'content/' +output: 'public/' +footer_img: '' +ics_url: '' +zotero_group_id: '' +zotero_api_key: '' +zotero_lib_type: '' \ No newline at end of file diff --git a/fetch_bib.py b/fetch_bib.py new file mode 100644 index 0000000..d017235 --- /dev/null +++ b/fetch_bib.py @@ -0,0 +1,138 @@ +import pathlib, re, frontmatter, markdown, citeproc, json +from pyzotero import zotero +from html.parser import HTMLParser +import utils + +CEND = '\33[0m' +CRED = '\33[31m' +CGREEN = '\33[32m' +CVIOLET = '\33[35m' +CBLUE = '\33[34m' + +re_html = '<.*?>' +re_punc = r'[^\w\s]' +CLEANR = re.compile(f'{re_html}|{re_punc}') + +def format_reading(title:str, desc:str): + c = frontmatter.Post(content=desc) + c['title'] = title + c['type'] = 'reading' + return c + +def compare_readings(path:pathlib.PosixPath, title:str, desc:str): + p = frontmatter.load(path) + pd = p.to_dict() + pd['content'] = pd['content'].strip() + return p, (pd == {'title': title, 'type': 'reading', 'content': desc.strip()}) + +def update_reading(previous:frontmatter.Post, new:frontmatter.Post): + keys = set(previous.keys()).union(set(new.keys())) + for k in keys: + if not str(previous[k]).strip() == str(new[k]).strip(): + print(f"Update '{k}' (y/n)?\n\tprev: {CRED}{previous[k]}{CEND}\n\tnew: {CGREEN}{new[k]}{CEND}") + c = input() + if c == 'y': + previous[k] = new[k] + + if not previous.content.strip() == new.content.strip(): + print(f"Update 'content' (y/n)?\n\tprev: {CRED}{previous.content}{CEND}\n\tnew: {CGREEN}{new.content}{CEND}") + c = input() + if c == 'y': + previous.content = new.content + + return previous + +def valid_bib_entry(csljson): + v = True + v = v and not ("full text" in csljson['title'].lower()) + v = v and not ("Table of Contents PDF".lower() in csljson['title'].lower()) + v = v and not ("Submitted Version".lower() in csljson['title'].lower()) + v = v and not ("Includes Bibliographical References".lower() in csljson['title'].lower()) + + # add more... + return v + +def format_filename_title(data_csl:dict, bib_entry:str): + index = bib_entry.lower().find(data_csl['title'].lower()) + if index == -1: + return None + index += len(data_csl['title']) + 4 if data_csl['type'] == 'book' else len(data_csl['title']) + 1 + + title = bib_entry[:index] + filename = re.sub(CLEANR, '', title).replace(' ', '-') + ".md" + + return filename, title + + +if __name__ == "__main__": + + conf = utils.load_conf() + + z = zotero.Zotero(conf['zotero_group_id'], conf['zotero_lib_type'], conf['zotero_api_key']) + + for d in z.collections(): + collection_name = d['data']['name'] + collection_key = d['key'] + print("---") + + z.add_parameters(content='csljson') + collection = z.collection_items(collection_key) + collection.reverse() + + # compile bib + + style_file = pathlib.Path(conf['template']) / 'csl' / 'chicago-author-date.csl' + + src = citeproc.source.json.CiteProcJSON(json_data=collection) + style = citeproc.CitationStylesStyle(style_file.absolute(), validate=False) + bib = citeproc.CitationStylesBibliography(style=style, source=src, formatter=citeproc.formatter.html) + + # print(collection) + entries = [citeproc.CitationItem(e['id']) for e in collection if valid_bib_entry(e)] + bib.register(citeproc.Citation(entries)) + + # because citeproc-py can't design shit... + kv = dict(zip(bib.keys, [str(e) for e in bib.style.render_bibliography(entries)])) + + # print(kv) + + # process collection and bib + + for e in collection: + + eid = e['id'].lower() + + if eid not in bib.keys: + continue + + bib_entry = kv[eid] + filename, title = format_filename_title(e, bib_entry) + filepath = pathlib.Path(conf['content']) / "bibliography" / collection_name / filename + + if not filepath.exists(): + print(f"new reading: {title}") + new = format_reading(title=title, desc=bib_entry) + utils.save_file(filepath, frontmatter.dumps(new), mkdirs=True) + + else: + prev, eq = compare_readings(filepath, title, bib_entry) + + if eq: + print(f"reading {CVIOLET}{title}{CEND} already exists... continuing") + continue + print(f"updating reading: {CBLUE}{title}{CEND}") + + ## selective update + new = format_reading(title=title, desc=bib_entry) + + updated = update_reading(prev, new) + utils.save_file(filepath, frontmatter.dumps(updated), overwrite=True) + + print(f"reading {e} updated") + + + + + + + diff --git a/fetch_ics.py b/fetch_ics.py new file mode 100644 index 0000000..84b40dd --- /dev/null +++ b/fetch_ics.py @@ -0,0 +1,91 @@ +import pathlib, ics, requests, arrow, frontmatter +import utils + +DFMT = "YYYY-MM-DD" +DHFMT = "YYYY-MM-DD HH:mm" +HFMT = "HH:mm" + +CEND = '\33[0m' +CRED = '\33[31m' +CGREEN = '\33[32m' +CVIOLET = '\33[35m' +CBLUE = '\33[34m' + +def format_event(title:str, date:str, location:str, desc:str): + c = frontmatter.Post(content=desc) + c['title'] = title + c['date'] = date + c['location'] = location + c['type'] = 'event' + return c + +def compare_events(path:pathlib.PosixPath, title:str, date:str, location:str, desc:str): + p = frontmatter.load(path) + pd = p.to_dict() + pd['content'] = pd['content'].strip() + return p, (pd == {'title': title, 'date': date, 'location': location, 'type': 'event', 'content': desc.strip()}) + +def update_event(previous:frontmatter.Post, new:frontmatter.Post): + keys = set(previous.keys()).union(set(new.keys())) + for k in keys: + if not str(previous[k]).strip() == str(new[k]).strip(): + print(f"Update '{k}' (y/n)?\n\tprev: {CRED}{previous[k]}{CEND}\n\tnew: {CGREEN}{new[k]}{CEND}") + c = input() + if c == 'y': + previous[k] = new[k] + + if not previous.content.strip() == new.content.strip(): + print(f"Update 'content' (y/n)?\n\tprev: {CRED}{previous.content}{CEND}\n\tnew: {CGREEN}{new.content}{CEND}") + c = input() + if c == 'y': + previous.content = new.content + + return previous + +def read_date(p:frontmatter.Post): + date = p['date'] + # 2024-10-06 10:00-12:00 + date = "-".join(date.split("-")[:-1]) + return arrow.get(date, DHFMT) + +if __name__ == "__main__": + + conf = utils.load_conf() + + req = requests.get(conf['ics_url']).text + c = ics.Calendar(req) + + for e in c.events: + + # file path + name + name = e.name.replace(' ', '-') + fm_date = e.begin.format(DFMT) + fm_date_interval = f"{e.begin.format(DHFMT)}-{e.end.format(HFMT)}" + filename = f'{fm_date}-{name}.md' + filepath = pathlib.Path(conf['content']) / 'event' / filename + + if not filepath.exists(): + print(f"new event: {e.name}") + new = format_event(title=e.name, date=fm_date_interval, location=e.location, desc=e.description) + utils.save_file(filepath, frontmatter.dumps(new), mkdirs=True) + + else: + prev, eq = compare_events(filepath, e.name, fm_date_interval, e.location, e.description) + + if eq: + print(f"event {CVIOLET}{e.name}{CEND} already exists... continuing") + continue + print(f"updating event: {CBLUE}{e.name}{CEND}") + + ## selective update + new = format_event(title=e.name, date=fm_date_interval, location=e.location, desc=e.description) + + updated = update_event(prev, new) + utils.save_file(filepath, frontmatter.dumps(updated), overwrite=True) + + print(f"event {e.name} updated") + + + + + diff --git a/make.py b/make.py new file mode 100644 index 0000000..4e63f30 --- /dev/null +++ b/make.py @@ -0,0 +1,118 @@ +import yaml, pathlib, shutil, markdown, frontmatter, arrow, jinja2 +from fetch_ics import read_date +import utils + +if __name__ == "__main__": + + with open('conf.yml') as fp: + conf = yaml.safe_load(fp.read()) + + content = pathlib.Path(conf['content']) + template = pathlib.Path(conf['template']) + + ## description + desc = utils.read_file(content / 'description.md') + if not desc: + print("not description.md in content/") + exit(1) + + # template + index = utils.read_file(template / 'index.html') + if not index: + print("not index.md in template/") + exit(1) + + index_template = jinja2.Template(index) + + # partials templates + partials = utils.get_files_in_subdir(template, 'partials', 'html') + + # head + head_template = jinja2.Template(utils.read_file(partials['head'])) + res_js = [p.relative_to('resources') for p in list(utils.get_files_in_subdir('resources/', 'js', 'js').values())] + res_css = [p.relative_to('resources') for p in list(utils.get_files_in_subdir('resources/', 'style', 'css').values())] + head = head_template.render(title=conf['site'], js_items=res_js, css_items=res_css) + + # header + header_template = jinja2.Template(utils.read_file(partials['header'])) + header = header_template.render(title=conf['site']) + + # cases + cases_template = jinja2.Template(utils.read_file(partials['cases'])) + cases = cases_template.render() + + # description + description_template = jinja2.Template(utils.read_file(partials['description'])) + desc = markdown.markdown(frontmatter.loads(desc).content) + description = description_template.render(desc=desc) + + # section {events + readings} + section_template = jinja2.Template(utils.read_file(partials['section'])) + + # events + events = list(utils.get_files_in_subdir(conf['content'], 'event', 'md').values()) + upcoming_events = [] + previous_events = [] + for e in events: + p = frontmatter.load(e) + summary = f"{p['title']} - {p['date']}" + content = markdown.markdown(p.content) + detail = f"When: {p['date']}
Where: {p['location']}
{content}" + el = {'summary': summary, 'detail': detail} + + if read_date(p) > arrow.utcnow(): + upcoming_events.append(el) + else: + previous_events.append(el) + + upcoming = section_template.render(section_id='events', section_title="Upcoming session(s)", section_items=upcoming_events) + previous = section_template.render(section_id='archive', section_title="Previous sessions", section_items=previous_events) + + # reading bibliography + reading = "" + bibliography_dir = pathlib.Path(conf['content']) / "bibliography" + bibliography = utils.ls_dir(bibliography_dir) + + for b in bibliography: + reading_list = utils.get_files_in_subdir(bibliography_dir, b.stem, 'md') + if not reading_list: + continue + reading_list = list(reading_list.values()) + readings = [] + for r in sorted(reading_list): + p = frontmatter.load(r) + summary = p['title'] + detail = markdown.markdown(p.content) + readings.append({'summary': summary, 'detail': detail}) + + reading += section_template.render(section_id='readings', section_title=b.stem, section_items=readings) + "\n" + + # footer + if 'footer' in partials and 'footer_img' in conf: + footer_template = jinja2.Template(utils.read_file(partials['footer'])) + footer = footer_template.render(footer_img=conf['footer_img']) + + + # index_html + index_html = index_template.render(head=head, header=header, cases=cases, description=description, upcoming=upcoming, reading=reading, previous=previous, footer=footer) + + # save + output_path = pathlib.Path(f"{conf['output']}") + index_path = output_path / "index.html" + utils.save_file(index_path, index_html, overwrite=True, mkdirs=True) + shutil.copytree('resources', output_path, dirs_exist_ok=True) + + + + + + + + + + + + + + + diff --git a/new.py b/new.py new file mode 100644 index 0000000..4975062 --- /dev/null +++ b/new.py @@ -0,0 +1,43 @@ +import yaml, argparse, pathlib +from datetime import datetime +import utils + +if __name__ == "__main__": + + p = argparse.ArgumentParser(description='creates new content files') + p.add_argument('filepath', metavar='filepath', type=str, help='path for new file') + + args = p.parse_args() + + file = pathlib.Path(args.filepath) + + type = file.parent.name + + conf = utils.load_conf() + + atypes = utils.get_files_in_subdir(conf['template'], 'archetypes', 'md') + + if atypes is None: + print('no archetypes in template') + exit(1) + + if type not in atypes.keys(): + ## can relax this later + print('directory of the new file must match an archetype') + exit(1) + + with open(atypes[type]) as fp: + archetype = fp.read() + + + #### process str if needed #### + + file.parent.mkdir(parents=True, exist_ok=True) + + with open(file, 'w') as fp: + fp.write(archetype) + + + + + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..abb48da --- /dev/null +++ b/requirements.txt @@ -0,0 +1,23 @@ +arrow==1.3.0 +attrs==24.2.0 +bibtexparser==1.4.2 +certifi==2024.8.30 +charset-normalizer==3.3.2 +feedparser==6.0.11 +ics==0.7.2 +idna==3.10 +Jinja2==3.1.4 +Markdown==3.7 +MarkupSafe==2.1.5 +pyparsing==3.1.4 +python-dateutil==2.9.0.post0 +python-frontmatter==1.1.0 +pytz==2024.2 +PyYAML==6.0.2 +pyzotero==1.5.25 +requests==2.32.3 +sgmllib3k==1.0.0 +six==1.16.0 +TatSu==5.12.1 +types-python-dateutil==2.9.0.20241003 +urllib3==2.2.3 diff --git a/resources/img/UU_logo_2021_EN_RGB.png b/resources/img/UU_logo_2021_EN_RGB.png new file mode 100755 index 0000000..c547716 Binary files /dev/null and b/resources/img/UU_logo_2021_EN_RGB.png differ diff --git a/resources/js/le_script.js b/resources/js/le_script.js new file mode 100644 index 0000000..f0154b9 --- /dev/null +++ b/resources/js/le_script.js @@ -0,0 +1,107 @@ + +var cases = document.getElementsByClassName("case"); + +var cases_el = document.getElementById("cases"); + +for (let i = cases.length - 1; i >= 0; i--) { + toggle_case(cases[i]); + draggable(cases[i]); + initial_height_closed(cases[i]); +} + +function draggable(e) { + var x, y, x_offset, y_offset, dragged = false; + + function drag_start(el) { + console.log("start"); + el = el || window.event; + el.preventDefault(); + x = el.clientX || el.targetTouches[0].pageX; + y = el.clientY || el.targetTouches[0].pageY; + x_offset = x - parseInt(e.offsetLeft); + y_offset = y - parseInt(e.offsetTop); + document.onmouseup = drag_stop; + document.ontouchend = drag_stop; + document.onmousemove = drag; + document.ontouchmove = drag; + // el.target.focus(); + } + + function drag(el) { + el = el || window.event; + el.preventDefault(); + + let cx = el.clientX || el.touches[el.touches.length].pageX; + let cy = el.clientY || el.touches[el.touches.length].pageY; + + if ((cx !== x) & (cy !== y)) { + e.style.left = (cx - x_offset) + "px"; + e.style.top = (cy - y_offset) + "px"; + if (!dragged) + fix_cases_el_height(e); + dragged = true; + } + } + + function drag_stop(el) { + el = el || window.event; + el.preventDefault(); + document.onmouseup = null; + document.ontouchend = null; + document.onmousemove = null; + document.ontouchmove = null; + + let cx = el.clientX || el.touches[el.touches.length].pageX; + let cy = el.clientY || el.touches[el.touches.length].pageY; + + // if no move then collapse + if ((cx == x) & (cy == y)) { + toggle_case(e); + } + } + + e.onmousedown = drag_start; + e.touchstart = drag_start; + +} + +function initial_height_closed(c) { + c.initial_height_closed = c.offsetHeight; +} + +function toggle_case(c) { + for (let i = c.children.length - 1; i >= 1; i--) { + c.children[i].classList.toggle('hidden'); + c.opened = !c.children[i].classList.contains('hidden') + } + + + if(c.opened) { + // c.style.removeProperty('height'); + c.classList.remove('close'); + fix_cases_height(); + } else { + c.classList.add('close'); + } +} + +function fix_cases_height(){ + for (let i = cases.length - 1; i >= 0; i--) { + if (!cases[i].opened) { + cases[i].classList.add('close'); + } + } +} + +function fix_cases_el_height(e){ + let h = cases_el.offsetHeight; + // e.style.position = 'absolute'; + for (let i = cases.length - 1; i >= 0; i--) { + cases[i].style.position = 'absolute'; + } + + cases_el.style.height = h + 'px'; +} + + + diff --git a/resources/style/le_style.css b/resources/style/le_style.css new file mode 100644 index 0000000..30c98d1 --- /dev/null +++ b/resources/style/le_style.css @@ -0,0 +1,245 @@ +#frame { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} + +#header { + height: 100px; +} + +#content { + max-width: 1200px; + min-width: 500px; +} + +#content > #description { + font-family: baskerville; + font-size: 2rem; +} + +ma { + font-family: baskerville; + font-size: 5rem; +} + +#footer { + display: flex; + align-items: center; + flex-direction: column; + width: 1200px; +/* min-width: 500px; */ + margin-top: 0.5em; + padding: 0.5em; + border-top: 3px double black; +} + +#footer > img { + max-width: 250px; +} + + +/*section*/ + +.section { + padding-bottom: 1em; +} + + +.section_header { + display: flex; + flex-direction: row; + border-bottom: 3px double red; + margin-bottom: 1.5em; +} + +.section_header > h2 { + padding-right: 0.5em; +} + +#events .list > details { + background-color: lightgoldenrodyellow; +} + +.list > details { + font-size: 1.8rem; + border: 1px solid #aaa; + border-radius: 4px; + margin-bottom: 0.5em; +} + +.list > details[open] { + background-color: lightsalmon; + border-bottom: 3px black solid; + border-radius: 4px; + padding: 0 1em 1em 1em; +} + +.list > details > summary { + font-size: 1.8rem; +/* font-weight: bold;*/ +/* margin: 0.5em;*/ + padding: 0.5em; +} + +.list > details[open] > summary { + border-bottom: 2px black solid; + margin-bottom: 1em; +} + +/*cases*/ + +.hidden { + display: none; +} + +#cases { +/* border: green 1px solid;*/ + display: flex; + flex-direction: row; +} + +.case { + margin: 1em; + padding: 1em; + width: 500px; + border: 1px solid black; + background-color: lightgrey; + border-radius: 5px; +} + +.case.close { + height: 2em; +} + +.title { + font-family: helvetica, sans-serif; + padding-bottom: 0.8em; +} + + +.definition, .origin { + font-family: helvetica, sans-serif; +} + +.title entry { + font-size: 1.5em; +} + +.title phone { + color: grey; + font-size: 1.2em; +} + +.qual { + font-size: 1em; +} + +.qual genre { + font-size: 1.2em; +} + +extra { + color: grey; + font-style: italic; +} + +ex { + font-family: Arial, sans-serif; + font-style: italic; + font-weight: lighter; +} + +xe { + font-style: italic; + font-weight: bold; +} + +.def { + margin: 1em; +} + +.example { + margin-top: 1em; + margin-bottom: 1em; +} + +.origin or { + color: grey; + font-size: 0.8em; + text-transform: uppercase; +} + +/* max-width: 1200px */ + +@media screen and (max-width: 1200px) { + + body { +/* background: #aaa;*/ + } + + #header { + margin-bottom: 1em; + } + + + ma { + font-size: 6em; + } + + #content > #description, .section_header > h2 { + font-size: 3rem; + font-weight: 400; + } + + .title { + font-size: 1.7em; + } + + .origin { + font-size: 1.2em; + } + + .title entry { + font-size: 1.7em; + } + + .title phone { + color: grey; + font-size: 1.4em; + } + + .qual { + font-size: 1.7em; + } + + .qual genre { + font-size: 1.4em; + } + + .list > details, .list > details > summary { + font-size: 1.7rem; + } + + #content, #footer, #cases, .case { + width: 95%; + } + + #cases { + display: flex; + flex-direction: column; + } + + .case { + margin-left: 0.5em; + } + + .case.close { + height: 4rem; + } + + +} + + diff --git a/template/archetypes/event.md b/template/archetypes/event.md new file mode 100644 index 0000000..964a0f1 --- /dev/null +++ b/template/archetypes/event.md @@ -0,0 +1,6 @@ +--- +type: 'event' +title: '' +date: '<event_date>' +location: '<location>' +--- \ No newline at end of file diff --git a/template/archetypes/misc.md b/template/archetypes/misc.md new file mode 100644 index 0000000..0a58bc4 --- /dev/null +++ b/template/archetypes/misc.md @@ -0,0 +1,4 @@ +--- +type: 'misc' +title: '<event title>' +--- \ No newline at end of file diff --git a/template/archetypes/reading.md b/template/archetypes/reading.md new file mode 100644 index 0000000..4b2f91b --- /dev/null +++ b/template/archetypes/reading.md @@ -0,0 +1,4 @@ +--- +type: 'reading' +title: '<title>' +--- \ No newline at end of file diff --git a/template/csl/chicago-author-date.csl b/template/csl/chicago-author-date.csl new file mode 100644 index 0000000..9f126b3 --- /dev/null +++ b/template/csl/chicago-author-date.csl @@ -0,0 +1,658 @@ +<?xml version="1.0" encoding="utf-8"?> +<style xmlns="http://purl.org/net/xbiblio/csl" class="in-text" version="1.0" demote-non-dropping-particle="display-and-sort" page-range-format="chicago"> + <info> + <title>Chicago Manual of Style 17th edition (author-date) + http://www.zotero.org/styles/chicago-author-date + + + + Julian Onions + julian.onions@gmail.com + + + Sebastian Karcher + + + Richard Karnesky + karnesky+zotero@gmail.com + http://arc.nucapt.northwestern.edu/Richard_Karnesky + + + Andrew Dunning + andrew.dunning@utoronto.ca + https://orcid.org/0000-0003-0464-5036 + + + Matthew Roth + matthew.g.roth@yale.edu + https://orcid.org/0000-0001-7902-6331 + + + Brenton M. Wiernik + + + + The author-date variant of the Chicago style + 2018-01-24T12:00:00+00:00 + This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License + + + + ed. + by + trans. + edited and translated by + trans. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/template/index.html b/template/index.html new file mode 100644 index 0000000..0c49bd2 --- /dev/null +++ b/template/index.html @@ -0,0 +1,18 @@ + + +{{ head }} + +
+ {{ header }} + {{ cases }} +
+ {{ description }} + {{ upcoming }} + {{ previous }} + {{ reading }} + {{ misc }} +
+ {{ footer }} +
+ + \ No newline at end of file diff --git a/template/partials/cases.html b/template/partials/cases.html new file mode 100644 index 0000000..bca280b --- /dev/null +++ b/template/partials/cases.html @@ -0,0 +1,47 @@ +
+
+
+ mathematics + | ˌmaθ(ə)ˈmatɪks | +
+
+
+ plural noun + [usually treated as singular] +
+
+ the abstract science of number, quantity, and space, either as abstract concepts (pure mathematics), or as applied to other disciplines such as physics and engineering (applied mathematics): a taste for mathematics. +
+
  • [often treated as plural] the mathematical aspects of something: James immerses himself in the mathematics of baseball.
  • +
    +
    +
    +
    + origin +
    + mid 16th century: plural of obsolete mathematic ‘mathematics’, from Old French mathematique, from Latin (ars) mathematica ‘mathematical (art)’, from Greek mathēmatikē (epistēmē), from the base of manthanein ‘learn’. +
    +
    +
    +
    +
    + artifice + | ˈɑːtɪfɪs | +
    +
    +
    + noun + [mass noun] +
    +
    + clever or cunning devices or expedients, especially as used to trick or deceive others: an industry dominated by artifice | [count noun] : the style is not free from the artifices of the period. +
    +
    +
    + origin +
    + early 16th century (in the sense ‘workmanship’): from Anglo-Norman French, from Latin artificium, based on ars, art- ‘art’ + facere ‘make’. Late Middle English has the form artificie, directly from Latin. +
    +
    +
    +
    \ No newline at end of file diff --git a/template/partials/description.html b/template/partials/description.html new file mode 100644 index 0000000..b2ac6f7 --- /dev/null +++ b/template/partials/description.html @@ -0,0 +1,3 @@ +
    + {{ desc }} +
    diff --git a/template/partials/footer.html b/template/partials/footer.html new file mode 100644 index 0000000..f65f451 --- /dev/null +++ b/template/partials/footer.html @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/template/partials/head.html b/template/partials/head.html new file mode 100644 index 0000000..6599b40 --- /dev/null +++ b/template/partials/head.html @@ -0,0 +1,6 @@ + + + {% for item in css_items %}{% endfor %} + {% for item in js_items %}{% endfor %} + {{ title }} + diff --git a/template/partials/header.html b/template/partials/header.html new file mode 100644 index 0000000..cafd9d9 --- /dev/null +++ b/template/partials/header.html @@ -0,0 +1,3 @@ + diff --git a/template/partials/section.html b/template/partials/section.html new file mode 100644 index 0000000..8560c38 --- /dev/null +++ b/template/partials/section.html @@ -0,0 +1,13 @@ +
    +
    +

    {{ section_title }}

    +
    +
    + {% for item in section_items %} +
    + {{ item.summary }} + {{ item.detail }} +
    + {% endfor %} +
    +
    diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..7ce1b75 --- /dev/null +++ b/utils.py @@ -0,0 +1,47 @@ +import yaml, pathlib + +def load_conf(): + p = pathlib.Path('conf.yml') + if not p.exists(): + return None + with open(p) as fp: + return yaml.safe_load(fp.read()) + +def read_file(path:pathlib.PosixPath): + if not path.exists(): + print(f"File {path} does not exist...") + return None + with open(path) as fp: + return fp.read() + +def save_file(path:pathlib.PosixPath, contents:str, overwrite=False, mkdirs=False): + if path.exists() and not overwrite: + print(f"File {path} already exists and overwrite is False...") + return + + if not path.parent.exists() and not mkdirs: + print(f"Directory {path.parent} does not exists and mkdirs is False...") + return + + if mkdirs: + path.parent.mkdir(parents=True, exist_ok=True) + + with open(path, 'w') as fp: + fp.write(contents) + +def ls_dir(path:str): + directory = pathlib.Path(path) + if not directory.exists() and not directory.is_dir(): + return None + return list(directory.iterdir()) + +def get_files_in_subdir(path:str, subdir:str, suffix:str): + template = pathlib.Path(path) + if template.exists() and template.is_dir(): + partials_path = template / subdir; + if partials_path.exists() and partials_path.is_dir(): + files = list(partials_path.glob('**/*.' + suffix)) + partials = [a.stem for a in files] + return dict(zip(partials, files)) + return None +