Some good progress, but also lightly broken code :)

This commit is contained in:
Flancian 2020-11-10 22:48:20 +01:00
parent 8c17feafc7
commit fc75e469c0
10 changed files with 219 additions and 29 deletions

View file

@ -22,33 +22,40 @@ 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'))
return render_template('index.html', help=url_for('agora.help'), nodes=url_for('agora.nodes'), subnodes=url_for('agora.subnodes'), users=url_for('agora.users'), 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 <a href="https://flancia.org/go/agora">code</a>.'
@bp.route('/notes') # alias for now
@bp.route('/nodes')
def nodes():
current_app.logger.warning('Config: %s' % config.AGORA_PATH)
# current_app.logger.warning('Config: %s' % config.AGORA_PATH)
return render_template('nodes.html', nodes=db.all_nodes())
@bp.route('/notes') # alias
@bp.route('/subnodes')
def subnodes():
return render_template('subnodes.html', subnodes=db.all_subnodes())
@bp.route('/users')
def users():
return render_template('users.html', users=db.all_users())
@bp.route('/journals')
def journals():
return render_template('nodes.html', nodes=db.all_journals())
return render_template('subnodes.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/<username>')
@bp.route('/user/<username>')
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('/u/<user>')
@bp.route('/user/<user>')
def user(user):
return render_template('subnodes.html', nodes=db.subnodes_by_user(user))
@bp.route('/garden/<garden>')
def garden(garden):
@ -58,7 +65,11 @@ def garden(garden):
@bp.route('/node/<node>')
@bp.route('/wikilink/<node>') # 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))
return render_template('node_rendered.html', wikilink=node, subnodes=db.subnodes_by_wikilink(node), backlinks=db.subnodes_by_outlink(node))
@bp.route('/subnode/<path:subnode>')
def subnode(subnode):
return render_template('subnode_rendered.html', subnode=db.subnode_by_uri(subnode), backlinks=db.subnodes_by_outlink(subnode))
@bp.route('/asset/<user>/<asset>')
def asset(user, asset):

View file

@ -1,3 +1,7 @@
import os
import getpass
AGORA_PATH = os.path.join('/home', getpass.getuser(), 'agora')
# With trailing slash.
URL_BASE = "https://anagora.org/"

View file

@ -20,18 +20,76 @@ from operator import attrgetter
RE_WIKILINKS = re.compile('\[\[(.*?)\]\]')
# URIs are ids.
# - In the case of nodes, their [[wikilink]].
# - Example: 'foo', meaning the node that is rendered when you click on [[foo]] somewhere.
# - In the case of subnodes, a relative path within the Agora.
# - Example: 'garden/flancian/README.md', meaning an actual file called README.md.
# - Note the example subnode above gets rendered in node [[README]], so fetching node with uri README would yield it (and others).
# TODO: implement.
class Graph:
def __init__(self):
# [[wikilink]] -> Node
self.nodes = {}
# node -> [n0, ..., nn] such that node has outlinks to the target list.
self.edges = {}
def addsubnode(self, subnode):
if subnode.wikilink in self.nodes:
G.nodes[subnode.wikilink].subnodes.append(subnode)
else:
G.nodes[subnode.wikilink] = Node(subnode.wikilink)
G = Graph()
class Node:
"""Nodes map 1:1 to wikilinks.
They resolve to a series of subnodes when being rendered (see below).
It maps to a particular file in the Agora repository, stored (relative to
the Agora root) in the attribute 'uri'."""
def __init__(self, wikilink):
# Use a node's URI as its identifier.
# Subnodes are attached to the node matching their wikilink.
# i.e. if two users contribute subnodes titled [[foo]], they both show up when querying node [[foo]].
self.wikilink = wikilink
self.uri = wikilink
self.url = '/node/' + self.uri
self.subnodes = []
class Subnode:
"""A subnode is a note or media resource volunteered by a user of the Agora.
It maps to a particular file in the Agora repository, stored (relative to
the Agora root) in the attribute 'uri'."""
def __init__(self, path):
self.dir = path_to_url(path)
# Use a subnode's URI as its identifier.
self.uri = path_to_uri(path)
self.url = '/subnode/' + path_to_uri(path)
# Subnodes are attached to the node matching their wikilink.
# i.e. if two users contribute subnodes titled [[foo]], they both show up when querying node [[foo]].
self.wikilink = path_to_wikilink(path)
self.url = '/node/' + self.wikilink
self.user = path_to_user(path)
with open(path) as f:
self.content = f.read()
self.outlinks = content_to_outlinks(self.content)
self.node = self.wikilink
# Initiate node for wikilink if this is the first subnode, append otherwise.
G.addsubnode(self)
def path_to_url(path):
class User:
def __init__(self, user):
self.uri = user
self.url = '/user/' + self.uri
def path_to_uri(path):
return path.replace(config.AGORA_PATH + '/', '')
def path_to_user(path):
m = re.search('garden/(.+?)/', path)
if m:
return m.group(1)
else:
return 'agora'
def path_to_wikilink(path):
return os.path.splitext(os.path.basename(path))[0]
@ -43,19 +101,46 @@ def content_to_outlinks(content):
else:
return []
def all_nodes():
def all_subnodes():
l = sorted([f for f in glob.glob(os.path.join(config.AGORA_PATH, '**/*.md'), recursive=True)])
return [Node(f) for f in l]
return [Subnode(f) for f in l]
def all_nodes():
# hack hack.
l = all_subnodes()
return sorted([Node(sn.wikilink) for sn in l], key=attrgetter('wikilink'))
def all_users():
# hack hack.
users = os.listdir(os.path.join(config.AGORA_PATH, 'garden'))
return sorted([User(u) for u in users], key=attrgetter('uri'))
def all_journals():
# hack hack.
l = sorted([f for f in glob.glob(os.path.join(config.AGORA_PATH, '**/????-??-??.md'), recursive=True)])
return sorted([Node(f) for f in l], key=attrgetter('wikilink'), reverse=True)
l = all_subnodes()
# return sorted([sn for sn in l], key=attrgetter('wikilink'), reverse=True)
return sorted([f for f in glob.glob(os.path.join(config.AGORA_PATH, '**/????-??-??.md'), recursive=True)])
def nodes_by_wikilink(wikilink):
nodes = [node for node in all_nodes() if node.wikilink == wikilink]
return nodes
def subnodes_by_wikilink(wikilink):
subnodes = [subnode for subnode in all_subnodes() if subnode.wikilink == wikilink]
return subnodes
def subnodes_by_user(user):
subnodes = [subnode for subnode in all_subnodes() if subnode.user == user]
return subnodes
def subnode_by_uri(uri):
subnode = [subnode for subnode in all_subnodes() if subnode.uri == uri][0]
return subnode
def nodes_by_outlink(wikilink):
nodes = [node for node in all_nodes() if wikilink in node.outlinks]
return nodes
def subnodes_by_outlink(wikilink):
subnodes = [subnode for subnode in all_subnodes() if wikilink in subnode.outlinks]
return subnodes

View file

@ -29,7 +29,10 @@
<a href="/"><img src="/static/img/agora.png" style="vertical-align: top" width="19" height="19"></a> |
</span>
<span align=left>
<a href="/nodes">/nodes</a> | <a href="/journals">/journals</a>
<a href="/nodes">/nodes</a>
| <a href="/subnodes">/subnodes</a>
| <a href="/journals">/journals</a>
| <a href="/users">/users</a>
</span>
<span style="vertical-align: bottom; float:right">
<a href="/node/agora-help">help</a>

View file

@ -18,12 +18,13 @@
{% block content %}
Ahoy, matey!<br />
<br />
This is the <strong>Agora v0.5a</strong>.<br />
This is the <strong>Agora v0.5b</strong>.<br />
<br />
This site is very much under construction, but feel free to look around:
<ul>
<li><a href="{{nodes}}">{{nodes}}</a> lists all nodes in this Agora; currently these are mostly notes as volunteered by users.</li>
<li><a href="{{nodes}}">{{nodes}}</a> lists all nodes in this Agora, created out of aggregating subnodes (mostly notes currently) volunteered by users.</li>
<li><a href="{{journals}}">{{journals}}</a> displays all journal entries.</li>
<li><a href="{{users}}">{{users}}</a> displays all users.</li>
</ul>
The [[<a href="/node/wikilink">wikilink</a>]] is the heart of the Agora. <a href="/node/foo">/node/foo</a> and <a href="/wikilink/foo">/wikilink/foo</a> will both render every node that responds to wikilink [[<a href="/node/foo">foo</a>]]. For example: [[<a href="/node/agora-help">agora-help</a>]].

View file

@ -16,22 +16,24 @@
{% extends "base.html" %}
{% block content %}
{% if not nodes %}
No nodes found for '{{wikilink}}'.
{% if not subnodes %}
No node found for '{{wikilink}}'.
<br /><br />
If you <a href="https://flancia.org/go/agora">contributed a node</a> named '{{wikilink}}' to the Agora, here is where your node would be :)
If you <a href="https://flancia.org/go/agora">contributed a subnode</a> named '{{wikilink}}' to the Agora, this node would not be empty :)
<br /><br />
Go back to <a href="/nodes">/nodes</a>?
{% endif %}
{% for node in nodes %}
{{ node.dir }}: <a href="{{node.url}}">{{node.wikilink}}</a><br />
{{ node.content|markdown|linkify|safe }}
{% for subnode in subnodes %}
{{ subnode.uri }}: <a href="{{subnode.url}}">{{subnode.wikilink}}</a><br />
{{ subnode.content|markdown|linkify|safe }}
<hr />
{% endfor %}
<br />
<em>Backlinks:</em><br />
{% for node in backlinks %}
{{ node.dir }}: <a href="{{node.url}}">{{node.wikilink}}</a><br />
{% for subnode in backlinks %}
{{ subnode.uri }}: <a href="{{subnode.url}}">{{subnode.wikilink}}</a><br />
{% endfor %}
{% endblock %}

View file

@ -18,6 +18,9 @@
<h1> Nodes </h1>
{% block content %}
{% for node in nodes %}
{{ node.dir }}: <a href="{{node.url}}">{{node.wikilink}}</a><br />
{% for subnode in node.subnodes %}}
{% subnode.user %}
{% endfor %}
<a href="{{node.url}}">{{node.wikilink}}</a><br />
{% endfor %}
{% endblock %}

View file

@ -0,0 +1,35 @@
<!--
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.
-->
{% extends "base.html" %}
{% block content %}
{% if not subnode %}
No subnode found matching '{{subnode}}'.
<br /><br />
Go back to <a href="/nodes">/nodes</a>?
{% endif %}
{{ subnode.uri }}: <a href="{{subnode.url}}">{{subnode.wikilink}}</a><br />
{{ subnode.content|markdown|linkify|safe }}
<hr />
<br />
<em>Backlinks:</em><br />
{% for subnode in backlinks %}
{{ subnode.uri }}: <a href="{{subnode.url}}">{{subnode.wikilink}}</a><br />
{% endfor %}
{% endblock %}

View file

@ -0,0 +1,23 @@
<!--
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.
-->
{% extends "base.html" %}
<h1> Subnodes </h1>
{% block content %}
{% for subnode in subnodes %}
(user: {{subnode.user}}) {{ subnode.uri }}: <a href="{{subnode.url}}">{{subnode.wikilink}}</a><br />
{% endfor %}
{% endblock %}

23
app/templates/users.html Normal file
View file

@ -0,0 +1,23 @@
<!--
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.
-->
{% extends "base.html" %}
<h1> Users </h1>
{% block content %}
{% for user in users %}
<a href="{{user.url}}">{{user.uri}}</a><br />
{% endfor %}
{% endblock %}