From bdce0d9fc380420dfd281e1fb8e598718c917baf Mon Sep 17 00:00:00 2001
From: Flancian <0@flancia.org>
Date: Sat, 7 Nov 2020 15:34:48 +0100
Subject: [PATCH] First commit after re-import.
---
.gitignore | 5 +
CONTRIBUTING.md | 29 +++++
LICENSE | 201 ++++++++++++++++++++++++++++++
README.md | 49 ++++++++
app/__init__.py | 61 +++++++++
app/agora.py | 70 +++++++++++
app/db.py | 61 +++++++++
app/static/css/screen-dark.css | 21 ++++
app/static/css/screen.css | 42 +++++++
app/static/img/agora.png | Bin 0 -> 2511 bytes
app/templates/base.html | 40 ++++++
app/templates/index.html | 33 +++++
app/templates/nodes.html | 23 ++++
app/templates/nodes_rendered.html | 37 ++++++
requirements.txt | 17 +++
run.sh | 18 +++
uwsgi.ini | 8 ++
uwsgi.sh | 16 +++
18 files changed, 731 insertions(+)
create mode 100644 .gitignore
create mode 100644 CONTRIBUTING.md
create mode 100644 LICENSE
create mode 100755 README.md
create mode 100644 app/__init__.py
create mode 100644 app/agora.py
create mode 100644 app/db.py
create mode 100644 app/static/css/screen-dark.css
create mode 100644 app/static/css/screen.css
create mode 100644 app/static/img/agora.png
create mode 100644 app/templates/base.html
create mode 100644 app/templates/index.html
create mode 100644 app/templates/nodes.html
create mode 100644 app/templates/nodes_rendered.html
create mode 100644 requirements.txt
create mode 100755 run.sh
create mode 100644 uwsgi.ini
create mode 100755 uwsgi.sh
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a08dd18
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+*~
+*swp
+*swo
+__pycache__
+venv
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..22b241c
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,29 @@
+# How to Contribute
+
+We'd love to accept your patches and contributions to this project. There are
+just a few small guidelines you need to follow.
+
+## Contributor License Agreement
+
+Contributions to this project must be accompanied by a Contributor License
+Agreement (CLA). You (or your employer) retain the copyright to your
+contribution; this simply gives us permission to use and redistribute your
+contributions as part of the project. Head over to
+ to see your current agreements on file or
+to sign a new one.
+
+You generally only need to submit a CLA once, so if you've already submitted one
+(even if it was for a different project), you probably don't need to do it
+again.
+
+## Code reviews
+
+All submissions, including submissions by project members, require review. We
+use GitHub pull requests for this purpose. Consult
+[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
+information on using pull requests.
+
+## Community Guidelines
+
+This project follows
+[Google's Open Source Community Guidelines](https://opensource.google/conduct/).
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100755
index 0000000..b2bea15
--- /dev/null
+++ b/README.md
@@ -0,0 +1,49 @@
+# To use
+This Agora Server is meant to be used in conjunction with an Agora. An Agora is
+a collection of digital gardens and other information sources that are assembled
+into a distributed knowledge graph.
+
+For an example Agora, and for more information on the Agora design, please refer to .
+
+To see the Agora Server in action with the example Agora, please visit
+.
+
+# To develop
+
+Install OS dependencies:
+```
+$ apt-get install python3 python3-venv python3-pip
+```
+
+Then Flask inside a virtual environment in this directory:
+```
+python3 -m venv venv
+. venv/bin/activate
+pip install -r requirements.txt
+```
+
+Then run the development server:
+```
+./run.sh
+```
+
+## Optional: my git configuration
+
+(Tangent) in case this is your first git contribution/you just haven't done it and you find this useful. This is my git config:
+
+```
+git config --global user.email "example@example.org"
+git config --global user.name "Example"
+git config --global alias.st status
+git config --global alias.ci commit
+git config --global alias.co checkout
+git config --global alias.f "reset --hard HEAD"
+```
+
+## About the project
+As you might have inferred from the above, this project is based on [Flask](https://flask.palletsprojects.com). ```/app``` hosts the Flask app. In it:
+
+```/templates``` are Jinja2 templates.
+```__init__.py``` has the high level Flask setup.
+```agora.py``` does rendering (url maps, views).
+```db.py``` has logic to read/process notes. The db is actually the filesystem :)
diff --git a/app/__init__.py b/app/__init__.py
new file mode 100644
index 0000000..bdcf6fd
--- /dev/null
+++ b/app/__init__.py
@@ -0,0 +1,61 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import bleach
+import os
+from flask import Flask
+from flaskext.markdown import Markdown
+from markdown.extensions.wikilinks import WikiLinkExtension
+
+def wikilink_to_url(label, base, end):
+ label = label.lower()
+ label = label.replace(' ', '-')
+ label = label.replace('\'', '')
+ label = label.replace(',', '')
+ url = '/node/' + label
+ return url
+
+def create_app(test_config=None):
+ # create and configure the app
+ app = Flask(__name__, instance_relative_config=True)
+ app.config.from_mapping(
+ SECRET_KEY='dev',
+ )
+
+ if test_config is None:
+ # load the instance config, if it exists, when not testing
+ app.config.from_pyfile('config.py', silent=True)
+ else:
+ # load the test config if passed in
+ app.config.from_mapping(test_config)
+
+ # ensure the instance folder exists
+ try:
+ os.makedirs(app.instance_path)
+ except OSError:
+ pass
+
+ # Add blueprints here.
+ from . import agora
+ app.register_blueprint(agora.bp)
+ app.add_url_rule('/', endpoint='index')
+
+ # Jinja2 extensions.
+ Markdown(app, tab_length=2, extensions=["sane_lists", WikiLinkExtension(build_url=wikilink_to_url)])
+
+ @app.template_filter('linkify')
+ def linkify(s):
+ return bleach.linkify(s)
+
+ return app
diff --git a/app/agora.py b/app/agora.py
new file mode 100644
index 0000000..b413d1a
--- /dev/null
+++ b/app/agora.py
@@ -0,0 +1,70 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import datetime
+from flask import Blueprint, url_for, render_template, current_app, Response, redirect
+from markupsafe import escape
+from . import db
+bp = Blueprint('agora', __name__)
+
+@bp.route('/')
+def index():
+ return render_template('index.html', help=url_for('agora.help'), nodes=url_for('agora.nodes'), journals=url_for('agora.journals'))
+
+@bp.route('/help')
+def help():
+ current_app.logger.warning('Not implemented.')
+ return 'If I had implemented help already, here you\'d see documentation on all URL endpoints. For now, please refer to the code.'
+
+@bp.route('/notes') # alias for now
+@bp.route('/nodes')
+def nodes():
+ return render_template('nodes.html', nodes=db.all_nodes())
+
+@bp.route('/journals')
+def journals():
+ return render_template('nodes.html', nodes=db.all_journals())
+
+@bp.route('/today')
+def today():
+ today = datetime.datetime.now().date()
+ return redirect("https://anagora.org/node/%s" % today.strftime("%Y-%m-%d"))
+
+@bp.route('/u/')
+@bp.route('/user/')
+def user(username):
+ current_app.logger.warning('Not implemented.')
+ return 'If I had implemented reading user profiles already, here you would see user named "%s".' % escape(username)
+
+@bp.route('/garden/')
+def garden(garden):
+ current_app.logger.warning('Not implemented.')
+ return 'If I had implemented rendering gardens already, here you would see garden named "%s".' % escape(garden)
+
+@bp.route('/node/')
+@bp.route('/wikilink/') # alias for now
+def wikilink(node):
+ return render_template('nodes_rendered.html', wikilink=node, nodes=db.nodes_by_wikilink(node), backlinks=db.nodes_by_outlink(node))
+
+@bp.route('/raw/')
+def raw(node):
+ # hack hack
+ # outlinks
+ return Response("\n\n".join([str(n.outlinks) for n in db.nodes_by_wikilink(node)]), mimetype="text/plain")
+ # content
+ # return Response("\n\n".join([n.content for n in db.nodes_by_wikilink(node)]), mimetype="text/plain")
+
+@bp.route('/backlinks/')
+def backlinks(node):
+ return render_template('nodes.html', nodes=db.nodes_by_outlink(node))
diff --git a/app/db.py b/app/db.py
new file mode 100644
index 0000000..4e02b72
--- /dev/null
+++ b/app/db.py
@@ -0,0 +1,61 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import glob
+import re
+import os
+from operator import attrgetter
+
+BASE = '../../agora/'
+RE_WIKILINKS = re.compile('\[\[(.*?)\]\]')
+
+class Node:
+ def __init__(self, path):
+ self.dir = path_to_url(path)
+ self.wikilink = path_to_wikilink(path)
+ self.url = '/node/' + self.wikilink
+ with open(path) as f:
+ self.content = f.read()
+ self.outlinks = content_to_outlinks(self.content)
+
+def path_to_url(path):
+ return os.path.split(path)[0].replace(BASE, '')
+
+def path_to_wikilink(path):
+ return os.path.splitext(os.path.basename(path))[0]
+
+def content_to_outlinks(content):
+ # hack hack.
+ match = RE_WIKILINKS.findall(content)
+ if match:
+ return [m.lower().replace(' ', '-').replace('\'', '').replace(',', '') for m in match]
+ else:
+ return []
+
+def all_nodes():
+ l = sorted([f for f in glob.glob(BASE + '**/*.md', recursive=True)])
+ return [Node(f) for f in l]
+
+def all_journals():
+ # hack hack.
+ l = sorted([f for f in glob.glob(BASE + '**/????-??-??.md', recursive=True)])
+ return sorted([Node(f) for f in l], key=attrgetter('wikilink'), reverse=True)
+
+def nodes_by_wikilink(wikilink):
+ nodes = [node for node in all_nodes() if node.wikilink == wikilink]
+ return nodes
+
+def nodes_by_outlink(wikilink):
+ nodes = [node for node in all_nodes() if wikilink in node.outlinks]
+ return nodes
diff --git a/app/static/css/screen-dark.css b/app/static/css/screen-dark.css
new file mode 100644
index 0000000..f331dff
--- /dev/null
+++ b/app/static/css/screen-dark.css
@@ -0,0 +1,21 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+a { color: rgb(140, 140, 250); }::-webkit-scrollbar-track-piece { background-color: rgba(255, 255, 255, 0.2) !important; }::-webkit-scrollbar-track { background-color: rgba(255, 255, 255, 0.3) !important; }::-webkit-scrollbar-thumb { background-color: rgba(255, 255, 255, 0.5) !important; }embed[type="application/pdf"] { filter: invert(90%); }
+
html { color: rgb(191, 191, 191); background: rgb(31, 31, 31) !important; }body { background-color: rgb(31, 31, 31); background-image: none !important; }input, select, textarea, button { color: rgb(191, 191, 191); background-color: rgb(31, 31, 31); }font { color: rgb(191, 191, 191); }
+
html { filter: contrast(100%) brightness(100%) saturate(100%); }.NIGHTEYE_Filter { width: 100%; height: 100%; position: fixed; left: 0px; top: 0px; pointer-events: none; z-index: 2147483647; }.NIGHTEYE_YellowFilter { background: rgba(255, 255, 0, 0.15); opacity: 0; }.NIGHTEYE_BlueFilter { background: rgba(0, 0, 255, 0.15); opacity: 0; }.NIGHTEYE_DimFilter { background: rgba(0, 0, 0, 0.5); opacity: 0; }.NIGHTEYE_TransformZ { transform: translateZ(0px); }
+
@font-face { font-family: Merriweather; font-style: italic; font-weight: 300; src: local("Merriweather Light Italic"), local("Merriweather-LightItalic"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR7lXff1jvzRPA.woff2") format("woff2"); unicode-range: U+460-52F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }@font-face { font-family: Merriweather; font-style: italic; font-weight: 300; src: local("Merriweather Light Italic"), local("Merriweather-LightItalic"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR7lXff8jvzRPA.woff2") format("woff2"); unicode-range: U+400-45F, U+490-491, U+4B0-4B1, U+2116; }@font-face { font-family: Merriweather; font-style: italic; font-weight: 300; src: local("Merriweather Light Italic"), local("Merriweather-LightItalic"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR7lXff3jvzRPA.woff2") format("woff2"); unicode-range: U+102-103, U+110-111, U+128-129, U+168-169, U+1A0-1A1, U+1AF-1B0, U+1EA0-1EF9, U+20AB; }@font-face { font-family: Merriweather; font-style: italic; font-weight: 300; src: local("Merriweather Light Italic"), local("Merriweather-LightItalic"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR7lXff2jvzRPA.woff2") format("woff2"); unicode-range: U+100-24F, U+259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; }@font-face { font-family: Merriweather; font-style: italic; font-weight: 300; src: local("Merriweather Light Italic"), local("Merriweather-LightItalic"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR7lXff4jvw.woff2") format("woff2"); unicode-range: U+0-FF, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }@font-face { font-family: Merriweather; font-style: italic; font-weight: 700; src: local("Merriweather Bold Italic"), local("Merriweather-BoldItalic"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR71Wvf1jvzRPA.woff2") format("woff2"); unicode-range: U+460-52F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }@font-face { font-family: Merriweather; font-style: italic; font-weight: 700; src: local("Merriweather Bold Italic"), local("Merriweather-BoldItalic"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR71Wvf8jvzRPA.woff2") format("woff2"); unicode-range: U+400-45F, U+490-491, U+4B0-4B1, U+2116; }@font-face { font-family: Merriweather; font-style: italic; font-weight: 700; src: local("Merriweather Bold Italic"), local("Merriweather-BoldItalic"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR71Wvf3jvzRPA.woff2") format("woff2"); unicode-range: U+102-103, U+110-111, U+128-129, U+168-169, U+1A0-1A1, U+1AF-1B0, U+1EA0-1EF9, U+20AB; }@font-face { font-family: Merriweather; font-style: italic; font-weight: 700; src: local("Merriweather Bold Italic"), local("Merriweather-BoldItalic"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR71Wvf2jvzRPA.woff2") format("woff2"); unicode-range: U+100-24F, U+259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; }@font-face { font-family: Merriweather; font-style: italic; font-weight: 700; src: local("Merriweather Bold Italic"), local("Merriweather-BoldItalic"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR71Wvf4jvw.woff2") format("woff2"); unicode-range: U+0-FF, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }@font-face { font-family: Merriweather; font-style: normal; font-weight: 300; src: local("Merriweather Light"), local("Merriweather-Light"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l521wRZVcf6lvg.woff2") format("woff2"); unicode-range: U+460-52F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }@font-face { font-family: Merriweather; font-style: normal; font-weight: 300; src: local("Merriweather Light"), local("Merriweather-Light"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l521wRZXMf6lvg.woff2") format("woff2"); unicode-range: U+400-45F, U+490-491, U+4B0-4B1, U+2116; }@font-face { font-family: Merriweather; font-style: normal; font-weight: 300; src: local("Merriweather Light"), local("Merriweather-Light"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l521wRZV8f6lvg.woff2") format("woff2"); unicode-range: U+102-103, U+110-111, U+128-129, U+168-169, U+1A0-1A1, U+1AF-1B0, U+1EA0-1EF9, U+20AB; }@font-face { font-family: Merriweather; font-style: normal; font-weight: 300; src: local("Merriweather Light"), local("Merriweather-Light"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l521wRZVsf6lvg.woff2") format("woff2"); unicode-range: U+100-24F, U+259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; }@font-face { font-family: Merriweather; font-style: normal; font-weight: 300; src: local("Merriweather Light"), local("Merriweather-Light"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l521wRZWMf6.woff2") format("woff2"); unicode-range: U+0-FF, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }@font-face { font-family: Merriweather; font-style: normal; font-weight: 700; src: local("Merriweather Bold"), local("Merriweather-Bold"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l52xwNZVcf6lvg.woff2") format("woff2"); unicode-range: U+460-52F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }@font-face { font-family: Merriweather; font-style: normal; font-weight: 700; src: local("Merriweather Bold"), local("Merriweather-Bold"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l52xwNZXMf6lvg.woff2") format("woff2"); unicode-range: U+400-45F, U+490-491, U+4B0-4B1, U+2116; }@font-face { font-family: Merriweather; font-style: normal; font-weight: 700; src: local("Merriweather Bold"), local("Merriweather-Bold"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l52xwNZV8f6lvg.woff2") format("woff2"); unicode-range: U+102-103, U+110-111, U+128-129, U+168-169, U+1A0-1A1, U+1AF-1B0, U+1EA0-1EF9, U+20AB; }@font-face { font-family: Merriweather; font-style: normal; font-weight: 700; src: local("Merriweather Bold"), local("Merriweather-Bold"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l52xwNZVsf6lvg.woff2") format("woff2"); unicode-range: U+100-24F, U+259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; }@font-face { font-family: Merriweather; font-style: normal; font-weight: 700; src: local("Merriweather Bold"), local("Merriweather-Bold"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l52xwNZWMf6.woff2") format("woff2"); unicode-range: U+0-FF, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 400; src: local("Open Sans Regular"), local("OpenSans-Regular"), url("https://fonts.gstatic.com/s/opensans/v18/mem8YaGs126MiZpBA-UFWJ0bbck.woff2") format("woff2"); unicode-range: U+460-52F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 400; src: local("Open Sans Regular"), local("OpenSans-Regular"), url("https://fonts.gstatic.com/s/opensans/v18/mem8YaGs126MiZpBA-UFUZ0bbck.woff2") format("woff2"); unicode-range: U+400-45F, U+490-491, U+4B0-4B1, U+2116; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 400; src: local("Open Sans Regular"), local("OpenSans-Regular"), url("https://fonts.gstatic.com/s/opensans/v18/mem8YaGs126MiZpBA-UFWZ0bbck.woff2") format("woff2"); unicode-range: U+1F00-1FFF; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 400; src: local("Open Sans Regular"), local("OpenSans-Regular"), url("https://fonts.gstatic.com/s/opensans/v18/mem8YaGs126MiZpBA-UFVp0bbck.woff2") format("woff2"); unicode-range: U+370-3FF; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 400; src: local("Open Sans Regular"), local("OpenSans-Regular"), url("https://fonts.gstatic.com/s/opensans/v18/mem8YaGs126MiZpBA-UFWp0bbck.woff2") format("woff2"); unicode-range: U+102-103, U+110-111, U+128-129, U+168-169, U+1A0-1A1, U+1AF-1B0, U+1EA0-1EF9, U+20AB; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 400; src: local("Open Sans Regular"), local("OpenSans-Regular"), url("https://fonts.gstatic.com/s/opensans/v18/mem8YaGs126MiZpBA-UFW50bbck.woff2") format("woff2"); unicode-range: U+100-24F, U+259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 400; src: local("Open Sans Regular"), local("OpenSans-Regular"), url("https://fonts.gstatic.com/s/opensans/v18/mem8YaGs126MiZpBA-UFVZ0b.woff2") format("woff2"); unicode-range: U+0-FF, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 700; src: local("Open Sans Bold"), local("OpenSans-Bold"), url("https://fonts.gstatic.com/s/opensans/v18/mem5YaGs126MiZpBA-UN7rgOX-hpOqc.woff2") format("woff2"); unicode-range: U+460-52F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 700; src: local("Open Sans Bold"), local("OpenSans-Bold"), url("https://fonts.gstatic.com/s/opensans/v18/mem5YaGs126MiZpBA-UN7rgOVuhpOqc.woff2") format("woff2"); unicode-range: U+400-45F, U+490-491, U+4B0-4B1, U+2116; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 700; src: local("Open Sans Bold"), local("OpenSans-Bold"), url("https://fonts.gstatic.com/s/opensans/v18/mem5YaGs126MiZpBA-UN7rgOXuhpOqc.woff2") format("woff2"); unicode-range: U+1F00-1FFF; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 700; src: local("Open Sans Bold"), local("OpenSans-Bold"), url("https://fonts.gstatic.com/s/opensans/v18/mem5YaGs126MiZpBA-UN7rgOUehpOqc.woff2") format("woff2"); unicode-range: U+370-3FF; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 700; src: local("Open Sans Bold"), local("OpenSans-Bold"), url("https://fonts.gstatic.com/s/opensans/v18/mem5YaGs126MiZpBA-UN7rgOXehpOqc.woff2") format("woff2"); unicode-range: U+102-103, U+110-111, U+128-129, U+168-169, U+1A0-1A1, U+1AF-1B0, U+1EA0-1EF9, U+20AB; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 700; src: local("Open Sans Bold"), local("OpenSans-Bold"), url("https://fonts.gstatic.com/s/opensans/v18/mem5YaGs126MiZpBA-UN7rgOXOhpOqc.woff2") format("woff2"); unicode-range: U+100-24F, U+259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 700; src: local("Open Sans Bold"), local("OpenSans-Bold"), url("https://fonts.gstatic.com/s/opensans/v18/mem5YaGs126MiZpBA-UN7rgOUuhp.woff2") format("woff2"); unicode-range: U+0-FF, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
+
body { font-family: sans-serif; background: rgb(43, 43, 43); }a, h1, h2 { color: rgb(159, 198, 224); }h1, h2 { font-family: Georgia, serif; margin: 0px; }h1 { border-bottom: 2px solid rgb(43, 43, 43); }h2 { font-size: 1.2em; }.page { margin: 2em auto; width: 35em; border: 5px solid rgb(77, 77, 77); padding: 0.8em; background: rgb(28, 28, 33); }.entries { list-style: none; margin: 0px; padding: 0px; }.entries li { margin: 0.8em 1.2em; }.entries li h2 { margin-left: -1em; }.add-entry { font-size: 0.9em; border-bottom: 1px solid rgb(77, 77, 77); }.add-entry dl { font-weight: bold; }.metanav { text-align: right; font-size: 0.8em; padding: 0.3em; margin-bottom: 1em; background: rgb(31, 31, 31); }.flash { background: rgb(22, 62, 90); padding: 0.5em; border: 1px solid rgb(42, 88, 122); }.error { background: rgb(78, 29, 29); padding: 0.5em; }.wikilink::before { content: "[["; }.wikilink::after { content: "]]"; }
\ No newline at end of file
diff --git a/app/static/css/screen.css b/app/static/css/screen.css
new file mode 100644
index 0000000..27722b4
--- /dev/null
+++ b/app/static/css/screen.css
@@ -0,0 +1,42 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+body { font-family: sans-serif; background: #eee; }
+a, h1, h2 { color: #377ba8; }
+h1, h2 { font-family: 'Georgia', serif; margin: 0; }
+h1 { border-bottom: 2px solid #eee; }
+h2 { font-size: 1.2em; }
+
+.page { margin: 2em auto; width: 35em; border: 5px solid #ccc;
+ padding: 0.8em; background: white; }
+.entries { list-style: none; margin: 0; padding: 0; }
+.entries li { margin: 0.8em 1.2em; }
+.entries li h2 { margin-left: -1em; }
+.add-entry { font-size: 0.9em; border-bottom: 1px solid #ccc; }
+.add-entry dl { font-weight: bold; }
+.metanav { text-align: right; font-size: 0.8em; padding: 0.3em;
+ margin-bottom: 1em; background: #fafafa; }
+.flash { background: #cee5F5; padding: 0.5em;
+ border: 1px solid #aacbe2; }
+.error { background: #f0d6d6; padding: 0.5em; }
+
+.wikilink:before {
+ content: "[["
+}
+
+.wikilink:after {
+ content: "]]"
+}
diff --git a/app/static/img/agora.png b/app/static/img/agora.png
new file mode 100644
index 0000000000000000000000000000000000000000..4a43460ed9aa1f914aa036bb0d2adec45acc6b90
GIT binary patch
literal 2511
zcmV;=2{87FP)CDe
zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00}oqL_t(|+U=ctj1|=#$3HB~
zVi8aQg>HdV!IfG?(WV;Oq-u+=ri27)>I*CQhiR*}n3~q~k@Szorb(X(wKgqns%Q}(
z)u@1|Xu;Q7BkJx3TVgeU0`d@)WnXOnIJ4pI+?ly^&V9_ecfOxwvU~5HGjry5zH{E^
zcR&asgb+dqA%v(aMl|SiwE>p{X9LrK7T^%D6L=fg0z3|=YXex!IZ~qz%9U7
znXa)}1`JpTqwv@TER_ixlPerW5k6J{pO85lho+z&F<7DF8Q>V1wK14eL17FXD`nQk
zU~v`1F&JDo_gQki(^94D{vz-7k46y3yH&dG6yO?V%=OJU;Bb|SmM3N2`sa65631a+
zl+0V-91FahBC+2Ceg<3(d>42nMVJ{fZ~ZeC_yf=%k-$B`&jS&K>s)u25XTqtYS2a;
zc$*5$5Bfij1O5jr5B^3m0oVbIj`{rp%pje}DH{@UjyY<|=9_H^zwMAX^36cZ_XjW|
zwh~8Pj?2&o$%S2#bM6PiV
zXq9>L&3!RN%6DX%e6uV@828IG`R3cekqBXIL=%j2%V^?<_cvhXhKL+859o{$#yx1p
zdG45iS*-LT@soLOl9}?wSAjQ)p4%~WJO_M6rph1ZCWxW~SVZG4A{QkX$?(pt9^fJ1
zGMOel%?UN&
zlpbOdumpG^;aV#kUZ#l-xyHYh%yx_feq&+aJ%Ny1a>jW;SIl+qu)Nkq4&t~|$(^1S
zjKg}HH*Z>*_dF&@q^RqE-Qqg8JBZ_3O7e6xW2X0|HS8uTyLU>|s<{2Y^pxT6a}dWI
zC40Kc@?R~C+5wEOLOgDZxYwGLVQqJF=q289ohaZ(7Dhclb^V3j6>-liN`yIq=p~m)
zN4t`}G?!Q!_M26X&uEGr+P+KjD!DH^6e-J<^vSeh#yrv*bVH5$Yi0(WVukl!mTG5b
z0`^)O^lKT!^RiGJUX3{CIETupJQpVLM@wVwMVdkcYh$dpc
zr7^q7-gnpXP@K*z`0YQI&-1d63FAYtaD-O>yUpYrH3ve4O+vI`mdVrS-J`Z)q&9=PG0G>`J&RQH-
zI)v#CB}$gO80x1v893ka8gJmKW0@?)F|MGTwqbKnf%Sp#DC%s7oMXMOWqy|8xB$1U
z#$WKrejTCD=VL!UqV*{e{1&(60!ux5h7pvv8+I?n+nFloz<6U5bWw;5~^W8rO*}2RyW)%d&{ar~|SR$0)~e2R*dmNQoo+tQfMN;1h-I
z+Scv9H(|3DhifADduT)3BJx4%gsjA|ug>C|#01OpS4$kpxONl;~$!*$sG1P^VPcARK;JryZgilYNF
zH^Y__J+t2@Nk4o3A?nPxXjbAlfN@Cgk{CW=<7xO|>WnO#hAvsGZ_Z6-r97`2ct+w#
zkINFkQ}L^>rd1W&&1!lu>uj^+Sc-j;hL3wG
zb{coFlG~2C4)ugXwT^{BYxkF-7vri%r+iF;V0)Y_jtroAb-jz*Jv*h
zuRj-@zcA*U2Q2P+ZYb>dJc?r`cJ2)NseO0)HI#NyUHE3VCY-x0c=qSHV%nS{+fI`p)nG-5c*M;O{Y#1Z$?ZgU
zM$X49#V9=g&AhidKs-Sk^)MfL}ZGCVm?Ddx?8Jg4v15mOIK6$4bc{o&`ZCldYf6)xfnu#WIRh
zF}pH8TB7232)Lz;PN>NdO;ejgBf2G+3Orthf%`Dt|2i@L#d<9|+h$fE{)-*s{VE#G
zwaP_0D^$r#IyO`J?JjIRHxB?`RFc~L2S
zHsGzGS^5g@UIEL^MJ~kbAaOco`r!wdd9thU(|e*2LI@#*5JCtcgb+dqA%qY@2qCJ=
Z{{iIflH3_o9F710002ovPDHLkV1nzQ!#4l`
literal 0
HcmV?d00001
diff --git a/app/templates/base.html b/app/templates/base.html
new file mode 100644
index 0000000..4dc373f
--- /dev/null
+++ b/app/templates/base.html
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+ Agora
+
+
+
+
+
+
+
+ |
+
+
+ /nodes | /journals
+
+
+ help
+
+
+ {% block content %}
+ {% endblock %}
+
diff --git a/app/templates/index.html b/app/templates/index.html
new file mode 100644
index 0000000..bc4a903
--- /dev/null
+++ b/app/templates/index.html
@@ -0,0 +1,33 @@
+
+
+{% extends "base.html" %}
+{% block content %}
+Ahoy, matey!
+
+This is the Agora v0.5a.
+
+This site is very much under construction, but feel free to look around:
+
+
{{nodes}} lists all nodes in this Agora; currently these are mostly notes as volunteered by users.
+For more information, please visit the Agora repository.
+{% endblock %}
diff --git a/app/templates/nodes.html b/app/templates/nodes.html
new file mode 100644
index 0000000..a457811
--- /dev/null
+++ b/app/templates/nodes.html
@@ -0,0 +1,23 @@
+
+
+{% extends "base.html" %}
+
Nodes
+{% block content %}
+{% for node in nodes %}
+{{ node.dir }}: {{node.wikilink}}
+{% endfor %}
+{% endblock %}
diff --git a/app/templates/nodes_rendered.html b/app/templates/nodes_rendered.html
new file mode 100644
index 0000000..23c0049
--- /dev/null
+++ b/app/templates/nodes_rendered.html
@@ -0,0 +1,37 @@
+
+
+{% extends "base.html" %}
+{% block content %}
+{% if not nodes %}
+No nodes found for '{{wikilink}}'.
+
+If you contributed a node named '{{wikilink}}' to the Agora, here is where your node would be :)
+
+Go back to /nodes?
+{% endif %}
+
+{% for node in nodes %}
+{{ node.dir }}: {{node.wikilink}}
+{{ node.content|markdown|linkify|safe }}
+
+{% endfor %}
+Backlinks:
+{% for node in backlinks %}
+{{ node.dir }}: {{node.wikilink}}
+{% endfor %}
+
+{% endblock %}
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..fc57b3d
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,17 @@
+bleach==3.2.1
+click==7.1.2
+Flask==1.1.2
+Flask-Markdown==0.3
+importlib-metadata==2.0.0
+itsdangerous==1.1.0
+Jinja2==2.11.2
+Markdown==3.3.3
+MarkupSafe==1.1.1
+packaging==20.4
+pkg-resources==0.0.0
+pyparsing==2.4.7
+six==1.15.0
+uWSGI==2.0.19.1
+webencodings==0.5.1
+Werkzeug==1.0.1
+zipp==3.4.0
diff --git a/run.sh b/run.sh
new file mode 100755
index 0000000..c114714
--- /dev/null
+++ b/run.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+export FLASK_APP=app
+export FLASK_ENV=development
+flask run
diff --git a/uwsgi.ini b/uwsgi.ini
new file mode 100644
index 0000000..c814033
--- /dev/null
+++ b/uwsgi.ini
@@ -0,0 +1,8 @@
+[uwsgi]
+module = app:create_app()
+master = true
+processes = 5
+socket = /tmp/uwsgi.sock
+chmod-socket = 666
+vacuum = true
+die-on-term = true
diff --git a/uwsgi.sh b/uwsgi.sh
new file mode 100755
index 0000000..20a7be3
--- /dev/null
+++ b/uwsgi.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+uwsgi --socket 0.0.0.0:5000 --protocol=http --module 'app:create_app()'