Compare commits
1 Commits
Author | SHA1 | Date |
---|---|---|
km0 | 059880cd39 | 1 year ago |
@ -0,0 +1,5 @@
|
||||
TODO: comment the code
|
||||
|
||||
TODO: remove new from homepage?
|
||||
or
|
||||
TODO: add per-new level in the xquisite branches (this makes more sense!)
|
@ -1,63 +0,0 @@
|
||||
from flask import (Blueprint, jsonify)
|
||||
from exquisite_branch.db import get_db
|
||||
|
||||
bp = Blueprint('api', __name__, url_prefix='/api/v1')
|
||||
|
||||
|
||||
def makeTree(entries):
|
||||
if len(entries) > 1:
|
||||
last = entries.pop()
|
||||
if last['parent'] != 'NEWS':
|
||||
parent_index = next(
|
||||
(index for (index, entry) in enumerate(entries) if entry['id'] == last['parent']),
|
||||
None)
|
||||
entries[parent_index].setdefault('branches', []).append(last)
|
||||
makeTree(entries)
|
||||
return entries
|
||||
|
||||
|
||||
@bp.route('/trees/')
|
||||
def list():
|
||||
db = get_db()
|
||||
trees = db.execute('SELECT * FROM trees').fetchall()
|
||||
t = []
|
||||
for tree in trees:
|
||||
t.append(tree['slug'])
|
||||
response = jsonify(t)
|
||||
response.headers.add("Access-Control-Allow-Origin", "*")
|
||||
return response
|
||||
|
||||
|
||||
@bp.route('tree/<tree>/')
|
||||
def tree(tree=None):
|
||||
db = get_db()
|
||||
|
||||
tree_info = db.execute('SELECT * FROM trees WHERE slug = ?', (tree,)).fetchone()
|
||||
entries = db.execute(
|
||||
"SELECT content, id, parent, username FROM branches WHERE tree = ?",
|
||||
(tree,)
|
||||
).fetchall()
|
||||
|
||||
r = {
|
||||
"id": tree_info['id'],
|
||||
"slug": tree_info['slug'],
|
||||
"name": tree_info['name'],
|
||||
}
|
||||
|
||||
# convert sqlite entries to list of normal dict
|
||||
branches = []
|
||||
for entry in entries:
|
||||
branches.append({**entry})
|
||||
|
||||
# make tree:
|
||||
# while list is not empty
|
||||
# start from the last one
|
||||
# insert it in its parent if not NEW
|
||||
# remove it from the list
|
||||
|
||||
r['branches'] = makeTree(branches)
|
||||
response = jsonify(r)
|
||||
response.headers.add("Access-Control-Allow-Origin", "*")
|
||||
|
||||
return response
|
||||
|
@ -0,0 +1,93 @@
|
||||
from flask import (Blueprint, flash, g, redirect,
|
||||
render_template, request, session, url_for)
|
||||
|
||||
from exquisite_branch.db import get_db
|
||||
from werkzeug.exceptions import abort
|
||||
|
||||
from shortuuid import uuid
|
||||
|
||||
bp = Blueprint('draw', __name__, url_prefix='/draw')
|
||||
|
||||
|
||||
@bp.route('/<parent>', methods=('GET', 'POST'))
|
||||
def draw(parent=None):
|
||||
db = get_db()
|
||||
|
||||
if request.method == 'POST':
|
||||
content = request.form.get('content')
|
||||
branch = request.form.get('branch')
|
||||
username = request.form.get('username')
|
||||
if request.is_json:
|
||||
data = request.get_json()
|
||||
content = data['content']
|
||||
branch = data['branch']
|
||||
username = data['username']
|
||||
|
||||
db.execute(
|
||||
'INSERT INTO branches (content, parent, branch, username) VALUES (?, ?, ?, ?)',
|
||||
(content, parent, branch, username)
|
||||
)
|
||||
db.commit()
|
||||
print(url_for('share.share', branch=f"{branch}"))
|
||||
return redirect(url_for('share.share', branch=branch))
|
||||
|
||||
branch = uuid()
|
||||
|
||||
previous = db.execute(
|
||||
"SELECT content, branch, parent FROM branches"
|
||||
" WHERE branch = ?",
|
||||
(parent,)
|
||||
).fetchone()
|
||||
|
||||
if previous is None:
|
||||
abort(404, f"Previous with id {parent} doesn't exist")
|
||||
|
||||
return render_template('draw.html', parent=parent, content=previous['content'], branch=branch)
|
||||
|
||||
|
||||
@bp.route('/last', methods=('GET', 'POST'))
|
||||
def last():
|
||||
|
||||
branch = uuid()
|
||||
db = get_db()
|
||||
previous = db.execute(
|
||||
'SELECT * FROM branches ORDER BY id DESC LIMIT 1'
|
||||
).fetchone()
|
||||
|
||||
parent = previous['branch']
|
||||
|
||||
if request.method == 'POST':
|
||||
content = request.form['content']
|
||||
branch = request.form['branch']
|
||||
username = request.form['username']
|
||||
|
||||
db.execute(
|
||||
'INSERT INTO branches (content, parent, branch, username) VALUES (?, ?, ?, ?)',
|
||||
(content, parent, branch, username)
|
||||
)
|
||||
db.commit()
|
||||
return redirect(url_for('share.share', branch=branch))
|
||||
|
||||
return render_template('draw.html', parent=parent, content=previous['content'], branch=branch)
|
||||
|
||||
|
||||
@bp.route('/', methods=('GET', 'POST'))
|
||||
def new():
|
||||
db = get_db()
|
||||
branch = uuid()
|
||||
parent = 'NEW'
|
||||
|
||||
if request.method == 'POST':
|
||||
content = request.form['content']
|
||||
branch = request.form['branch']
|
||||
|
||||
username = request.form['username']
|
||||
|
||||
db.execute(
|
||||
'INSERT INTO branches (content, parent, branch, username) VALUES (?, ?, ?, ?)',
|
||||
(content, parent, branch, username)
|
||||
)
|
||||
db.commit()
|
||||
return redirect(url_for('share.share', branch=branch))
|
||||
|
||||
return render_template('draw.html', parent=parent, branch=branch)
|
@ -1,17 +1,9 @@
|
||||
DROP TABLE IF EXISTS branches;
|
||||
DROP TABLE IF EXISTS trees;
|
||||
|
||||
CREATE TABLE branches (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
parent TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
username TEXT,
|
||||
tree TEXT NOT NULL,
|
||||
FOREIGN KEY (tree) REFERENCES trees(slug)
|
||||
);
|
||||
|
||||
CREATE TABLE trees (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
slug TEXT NOT NULL UNIQUE
|
||||
);
|
||||
branch TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
parent TEXT NOT NULL,
|
||||
username TEXT
|
||||
);
|
@ -0,0 +1,32 @@
|
||||
svg {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
background-color: white;
|
||||
}
|
||||
#svg-container {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
#previous {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
#previous svg {
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#previous::after {
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
transform: translateX(-25%);
|
||||
background-color: var(--background);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
:root {
|
||||
--background: #2a9d8f;
|
||||
--background: #edd;
|
||||
--color: black;
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 13 KiB |
@ -0,0 +1,114 @@
|
||||
// Great resource from https://stackoverflow.com/a/40700068
|
||||
// Thank you ConnorFan
|
||||
|
||||
var strokeWidth = 2;
|
||||
var bufferSize;
|
||||
|
||||
var svgElement = document.getElementById("svgElement");
|
||||
var rect = svgElement.getBoundingClientRect();
|
||||
var path = null;
|
||||
var strPath;
|
||||
var buffer = []; // Contains the last positions of the mouse cursor
|
||||
|
||||
svgElement.addEventListener("mousedown", function (e) {
|
||||
bufferSize = document.getElementById("cmbBufferSize").value;
|
||||
path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
||||
path.setAttribute("fill", "none");
|
||||
path.setAttribute("stroke", "currentColor");
|
||||
path.setAttribute("stroke-width", strokeWidth);
|
||||
buffer = [];
|
||||
var pt = getMousePosition(e);
|
||||
appendToBuffer(pt);
|
||||
strPath = "M" + pt.x + " " + pt.y;
|
||||
path.setAttribute("d", strPath);
|
||||
svgElement.appendChild(path);
|
||||
});
|
||||
|
||||
svgElement.addEventListener("mousemove", function (e) {
|
||||
if (path) {
|
||||
appendToBuffer(getMousePosition(e));
|
||||
updateSvgPath();
|
||||
}
|
||||
});
|
||||
|
||||
svgElement.addEventListener("mouseup", function () {
|
||||
if (path) {
|
||||
path = null;
|
||||
}
|
||||
});
|
||||
|
||||
svgElement.addEventListener("mouseleave", function () {
|
||||
if (path) {
|
||||
path = null;
|
||||
}
|
||||
});
|
||||
|
||||
var getMousePosition = function (e) {
|
||||
return {
|
||||
x: e.pageX - rect.left,
|
||||
y: e.pageY - rect.top,
|
||||
};
|
||||
};
|
||||
|
||||
var appendToBuffer = function (pt) {
|
||||
buffer.push(pt);
|
||||
while (buffer.length > bufferSize) {
|
||||
buffer.shift();
|
||||
}
|
||||
};
|
||||
|
||||
// Calculate the average point, starting at offset in the buffer
|
||||
var getAveragePoint = function (offset) {
|
||||
var len = buffer.length;
|
||||
if (len % 2 === 1 || len >= bufferSize) {
|
||||
var totalX = 0;
|
||||
var totalY = 0;
|
||||
var pt, i;
|
||||
var count = 0;
|
||||
for (i = offset; i < len; i++) {
|
||||
count++;
|
||||
pt = buffer[i];
|
||||
totalX += pt.x;
|
||||
totalY += pt.y;
|
||||
}
|
||||
return {
|
||||
x: totalX / count,
|
||||
y: totalY / count,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
var updateSvgPath = function () {
|
||||
var pt = getAveragePoint(0);
|
||||
|
||||
if (pt) {
|
||||
// Get the smoothed part of the path that will not change
|
||||
strPath += " L" + pt.x + " " + pt.y;
|
||||
|
||||
// Get the last part of the path (close to the current mouse position)
|
||||
// This part will change if the mouse moves again
|
||||
var tmpPath = "";
|
||||
for (var offset = 2; offset < buffer.length; offset += 2) {
|
||||
pt = getAveragePoint(offset);
|
||||
tmpPath += " L" + pt.x + " " + pt.y;
|
||||
}
|
||||
|
||||
// Set the complete current path coordinates
|
||||
path.setAttribute("d", strPath + tmpPath);
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
// SAVE THE BRANCH
|
||||
|
||||
const form = document.querySelector("form");
|
||||
|
||||
form.addEventListener("submit", () => {
|
||||
let wrapper = document.createElement("div");
|
||||
wrapper.appendChild(svgElement);
|
||||
form["content"].value = wrapper.innerHTML;
|
||||
return true;
|
||||
});
|
@ -1,22 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Exquisite Branch</title>
|
||||
<link rel="stylesheet" href="${url_for('static', filename='css/variables.css')}" />
|
||||
<link rel="stylesheet" href="${url_for('static', filename='css/global.css')}" />
|
||||
|
||||
<%block name='head' %></%block>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<%block name='nav' >
|
||||
<a href="${url_for('home.home')}">Home</a>
|
||||
<a href="${url_for('display.list')}">Results</a>
|
||||
</%block>
|
||||
</nav>
|
||||
${self.body()}
|
||||
</body>
|
||||
</html>
|
@ -1,44 +1,49 @@
|
||||
<%inherit file="base_mako.html" />
|
||||
|
||||
|
||||
<%block name="head">
|
||||
<link rel="stylesheet" href="${url_for('static', filename='css/display.css')}">
|
||||
</%block>
|
||||
|
||||
<header>
|
||||
<h1>Exquisite Excerpts</h1>
|
||||
<p>Here texts branch and follow their own path. Click on an excerpt to create a new branch and continue toward new meanings. </p>
|
||||
</header>
|
||||
|
||||
<main class="container">
|
||||
|
||||
<% from random import random %>
|
||||
<% from collections import defaultdict %>
|
||||
<% transform = {'NEW': ''} %>
|
||||
<% visited = defaultdict(int) %>
|
||||
|
||||
<div class="stream">
|
||||
|
||||
% for branch in branches:
|
||||
|
||||
<% visited[branch['parent']] += 1 %>
|
||||
|
||||
|
||||
% if visited[branch['parent']] > 1:
|
||||
<% steer = (random() - 0.5) * 0.25 %>
|
||||
% else:
|
||||
<% steer = 0 %>
|
||||
% endif
|
||||
|
||||
<% transform[branch['id']] = f'{transform[branch["parent"]]} rotate({random() * 0.04 + steer}turn) translateX(100%)' %>
|
||||
|
||||
<div class="text-container" style="transform: ${transform[branch['parent']]}">
|
||||
<a href="${url_for('write.write', tree=tree, parent=branch['id'], _external=True, _scheme='http')}" target="__blank">
|
||||
${branch['content']}
|
||||
</a>
|
||||
<span class="author">${branch['username']}</span>
|
||||
{%extends 'base.html' %} {%block head %}
|
||||
|
||||
<link rel="stylesheet" href="{{url_for('static', filename='css/display.css')}}" />
|
||||
<title>Display</title>
|
||||
{%endblock%} {%block nav%}
|
||||
<a href="{{url_for('home.home')}}">Home</a>
|
||||
<a href="{{url_for('write.last')}}">Write</a>
|
||||
|
||||
{%endblock%} {%block contents%}
|
||||
|
||||
<h1>Exquisite Branch</h1>
|
||||
|
||||
<div class="streams">
|
||||
{% for stream in streams %}
|
||||
<div
|
||||
class="stream"
|
||||
style="color: rgb({{ range(0, 255) | random }},{{ range(0, 255) | random }},{{ range(0, 255) | random }}); transform: translate({{ range(-5, 5) | random }}px, {{ range(-5, 5) | random }}px)"
|
||||
>
|
||||
{% for branch in stream %}
|
||||
<div class="svg-container">
|
||||
{{branch['content'] | safe}}
|
||||
<span
|
||||
class="author"
|
||||
style="transform: translate({{ range(-50, 50) | random }}px, {{ range(-10, 10) | random }}px"
|
||||
>
|
||||
{{ branch['username']}}
|
||||
</span>
|
||||
</div>
|
||||
{%endfor%}
|
||||
</div>
|
||||
{%endfor%}
|
||||
</div>
|
||||
|
||||
{{colors}}
|
||||
|
||||
<h2>Branches</h2>
|
||||
<div class="branches">
|
||||
{% for stream in streams %}
|
||||
<div class="branch">
|
||||
{% for branch in stream %}
|
||||
<div class="svg-container">
|
||||
{{branch['content'] | safe}}
|
||||
<span class="author"> {{ branch['username']}} </span>
|
||||
</div>
|
||||
|
||||
% endfor
|
||||
{%endfor%}
|
||||
</div>
|
||||
</main>
|
||||
{%endfor%}
|
||||
</div>
|
||||
{%endblock%}
|
||||
|
@ -1,64 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>${tree}</title>
|
||||
<style>
|
||||
|
||||
.container {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.child {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
background-color: rgba(255,0,0,0.075);
|
||||
border: 1px solid rgba(255,0,0,0.1);
|
||||
gap: 0.5em;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.child+.child {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="tree">${tree}</h1>
|
||||
<div class="container"></div>
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
const title = document.querySelector('#tree').innerText
|
||||
const slug = title.innerText
|
||||
const container = document.querySelector('.container')
|
||||
|
||||
const display = (element, tree) => {
|
||||
if(tree.branches){
|
||||
let content = document.createElement('div')
|
||||
content.classList.add('child')
|
||||
if (tree.content) {
|
||||
content.innerHTML = tree.content
|
||||
}
|
||||
element.appendChild(content)
|
||||
for (const b of tree.branches) {
|
||||
display(content, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
fetch(`/api/v1/tree/${tree}/`).then(res=>res.json()).then(res=>display(container, res))
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,51 @@
|
||||
{%extends 'base.html' %} {%block head %}
|
||||
<title>Draw</title>
|
||||
<link rel="stylesheet" href="{{url_for('static', filename='css/draw.css')}}" />
|
||||
<script src="{{url_for('static', filename='js/draw.js')}}" defer></script>
|
||||
|
||||
{%endblock%} {%block nav%}
|
||||
<a href="{{url_for('home.home')}}">Home</a>
|
||||
<a href="{{url_for('display.display')}}">Results</a>
|
||||
|
||||
{%endblock%} {%block contents%}
|
||||
|
||||
<h1>Draw</h1>
|
||||
|
||||
<div id="divSmoothingFactor">
|
||||
<label for="cmbBufferSize">Smoothing</label>
|
||||
<select id="cmbBufferSize">
|
||||
<option value="1">1 - No smoothing</option>
|
||||
<option value="4">4 - Sharp curves</option>
|
||||
<option value="8" selected="selected">8 - Smooth curves</option>
|
||||
<option value="12">12 - Very smooth curves</option>
|
||||
<option value="16">16 - Super smooth curves</option>
|
||||
<option value="20">20 - Hyper smooth curves</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div id="svg-container">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
version="1.1"
|
||||
id="svgElement"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="500px"
|
||||
height="500px"
|
||||
viewBox="0 0 500 500"
|
||||
enable-background="new 0 0 500 500"
|
||||
xml:space="preserve"
|
||||
data-parent="{{parent or None}}"
|
||||
data-branch="{{branch}}"
|
||||
></svg>
|
||||
<div id="previous">{% if content %} {{content|safe}} {% endif %}</div>
|
||||
</div>
|
||||
|
||||
<form method="POST">
|
||||
<input type="hidden" name="branch" value="{{branch}}" />
|
||||
<input type="hidden" name="content" />
|
||||
<input type="text" name="username" placeholder="Name" />
|
||||
<input type="submit" />
|
||||
</form>
|
||||
{%endblock%}
|
@ -1,28 +0,0 @@
|
||||
<%inherit file="base_mako.html" />
|
||||
|
||||
|
||||
<%block name="head">
|
||||
<link rel="stylesheet" href="${url_for('static', filename='css/display.css')}">
|
||||
</%block>
|
||||
|
||||
<header>
|
||||
<h1>Exquisite Excerpts</h1>
|
||||
<p>Here is a list of all the writings trees</p>
|
||||
</header>
|
||||
|
||||
|
||||
<main class="container">
|
||||
<ul>
|
||||
% for tree in trees:
|
||||
<li class="tree">
|
||||
|
||||
<a href="${url_for('display.display', tree=tree['slug'])}">
|
||||
${tree['name']}
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
% endfor
|
||||
</ul>
|
||||
</main>
|
||||
|
@ -1,49 +1,21 @@
|
||||
{%extends 'base.html' %} {%block head %}
|
||||
<title>Draw</title>
|
||||
<title>Write</title>
|
||||
<link rel="stylesheet" href="{{url_for('static', filename='css/write.css')}}" />
|
||||
<script src="{{url_for('static', filename='js/draw.js')}}" defer></script>
|
||||
|
||||
{%endblock%} {%block nav%}
|
||||
<a href="{{url_for('home.home')}}">Home</a>
|
||||
|
||||
|
||||
|
||||
{% if tree is undefined %}
|
||||
<a href="{{url_for('display.list')}}">See all</a>
|
||||
{% else %}
|
||||
<a href="{{url_for('display.display', tree=tree)}}">See tree</a>
|
||||
{% endif %}
|
||||
<a href="{{url_for('display.display')}}">Results</a>
|
||||
|
||||
{%endblock%} {%block contents%}
|
||||
|
||||
<!-- mmm i dont remember what i was using these two things for! -->
|
||||
<!-- data-parent="{{parent or None}}" -->
|
||||
<!-- data-id="{{id}}" -->
|
||||
<!-- Maybe to create links in the display page? -->
|
||||
<header>
|
||||
<h1>Write</h1>
|
||||
</header>
|
||||
|
||||
|
||||
<main>
|
||||
|
||||
|
||||
<div id="previous">{% if content %} {{content|safe}} {% endif %}</div>
|
||||
|
||||
<form method="POST">
|
||||
<input type="hidden" name="id" value="{{id}}" />
|
||||
|
||||
{% if tree is undefined %}
|
||||
<input type="text" name="tree_name" placeholder="Tree name" required />
|
||||
<br/>
|
||||
{% endif %}
|
||||
<textarea name="content" placeholder="Write here..." required></textarea>
|
||||
<br/>
|
||||
<input type="text" name="username" placeholder="Author" />
|
||||
<br/>
|
||||
<input type="submit" value="Submit" />
|
||||
|
||||
<input type="hidden" name="branch" value="{{branch}}" />
|
||||
<textarea name="content" ></textarea>
|
||||
<input type="text" name="username" placeholder="Name" />
|
||||
<input type="submit" />
|
||||
</form>
|
||||
</main>
|
||||
|
||||
{%endblock%}
|
||||
|
@ -1,102 +1,93 @@
|
||||
from flask import (Blueprint, redirect, render_template, request, url_for)
|
||||
from flask import (Blueprint, redirect,
|
||||
render_template, request, url_for)
|
||||
|
||||
from exquisite_branch.db import get_db
|
||||
from werkzeug.exceptions import abort
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
from shortuuid import uuid
|
||||
|
||||
bp = Blueprint('write', __name__, url_prefix='/write')
|
||||
|
||||
|
||||
@bp.route('/<tree>/<parent>', methods=('GET', 'POST'))
|
||||
def write(tree=None, parent=None):
|
||||
@bp.route('/<parent>', methods=('GET', 'POST'))
|
||||
def write(parent=None):
|
||||
db = get_db()
|
||||
|
||||
if request.method == 'POST':
|
||||
content = request.form.get('content')
|
||||
id = request.form.get('id')
|
||||
branch = request.form.get('branch')
|
||||
username = request.form.get('username')
|
||||
if request.is_json:
|
||||
data = request.get_json()
|
||||
content = data['content']
|
||||
id = data['id']
|
||||
branch = data['branch']
|
||||
username = data['username']
|
||||
|
||||
db.execute(
|
||||
'INSERT INTO branches (content, parent, id, username, tree) VALUES (?, ?, ?, ?, ?)',
|
||||
(content, parent, id, username, tree)
|
||||
'INSERT INTO branches (content, parent, branch, username) VALUES (?, ?, ?, ?)',
|
||||
(content, parent, branch, username)
|
||||
)
|
||||
db.commit()
|
||||
print(url_for('share.share', tree=tree, id=id))
|
||||
return redirect(url_for('share.share', tree=tree, id=id))
|
||||
print(url_for('share.share', branch=f"{branch}"))
|
||||
return redirect(url_for('share.share', branch=branch))
|
||||
|
||||
id = uuid()
|
||||
branch = uuid()
|
||||
|
||||
previous = db.execute(
|
||||
"SELECT content, id, parent FROM branches"
|
||||
" WHERE id = ? AND tree = ?",
|
||||
(parent, tree)
|
||||
"SELECT content, branch, parent FROM branches"
|
||||
" WHERE branch = ?",
|
||||
(parent,)
|
||||
).fetchone()
|
||||
|
||||
if previous is None:
|
||||
abort(404, f"Previous with id {parent} doesn't exist")
|
||||
|
||||
return render_template('write.html', tree=tree, parent=parent, content=previous['content'], id=id)
|
||||
return render_template('write.html', parent=parent, content=previous['content'], branch=branch)
|
||||
|
||||
|
||||
@bp.route('/last', methods=('GET', 'POST'))
|
||||
def last():
|
||||
id = uuid()
|
||||
|
||||
branch = uuid()
|
||||
db = get_db()
|
||||
previous = db.execute(
|
||||
'SELECT * FROM branches ORDER BY id DESC LIMIT 1'
|
||||
).fetchone()
|
||||
|
||||
parent = previous['id']
|
||||
tree = previous['tree']
|
||||
parent = previous['branch']
|
||||
|
||||
if request.method == 'POST':
|
||||
content = request.form['content']
|
||||
id = request.form['id']
|
||||
branch = request.form['branch']
|
||||
username = request.form['username']
|
||||
|
||||
db.execute(
|
||||
'INSERT INTO branches (content, parent, id, username, tree) VALUES (?, ?, ?, ?, ?)',
|
||||
(content, parent, id, username, tree)
|
||||
'INSERT INTO branches (content, parent, branch, username) VALUES (?, ?, ?, ?)',
|
||||
(content, parent, branch, username)
|
||||
)
|
||||
db.commit()
|
||||
return redirect(url_for('share.share', tree=tree, id=id))
|
||||
return redirect(url_for('share.share', branch=branch))
|
||||
|
||||
return render_template('write.html', tree=tree, parent=parent, content=previous['content'], id=id)
|
||||
return render_template('write.html', parent=parent, content=previous['content'], branch=branch)
|
||||
|
||||
|
||||
@bp.route('/', methods=('GET', 'POST'))
|
||||
def new():
|
||||
db = get_db()
|
||||
id = uuid()
|
||||
branch = uuid()
|
||||
parent = 'NEW'
|
||||
|
||||
if request.method == 'POST':
|
||||
content = request.form['content']
|
||||
id = request.form['id']
|
||||
username = request.form['username']
|
||||
tree_name = request.form['tree_name']
|
||||
|
||||
if tree_name == '':
|
||||
return redirect(url_for('write.new'))
|
||||
branch = request.form['branch']
|
||||
|
||||
tree = secure_filename(tree_name)
|
||||
|
||||
db.execute(
|
||||
'INSERT INTO trees (name, slug) VALUES (?, ?)',
|
||||
(tree_name, tree)
|
||||
)
|
||||
username = request.form['username']
|
||||
|
||||
db.execute(
|
||||
'INSERT INTO branches (content, parent, id, username, tree) VALUES (?, ?, ?, ?, ?)',
|
||||
(content, parent, id, username, tree)
|
||||
'INSERT INTO branches (content, parent, branch, username) VALUES (?, ?, ?, ?)',
|
||||
(content, parent, branch, username)
|
||||
)
|
||||
db.commit()
|
||||
return redirect(url_for('share.share', tree=tree, id=id))
|
||||
return redirect(url_for('share.share', branch=branch))
|
||||
|
||||
return render_template('write.html', parent=parent, id=id)
|
||||
return render_template('write.html', parent=parent, branch=branch)
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 194 KiB |
@ -1,61 +0,0 @@
|
||||
# Exquisite Excerpts
|
||||
|
||||
A branching version of the [exquisite corpse](https://en.wikipedia.org/wiki/Exquisite_corpse) game, forked from the [exquisite branch](https://git.xpub.nl/kamo/exquisite-branch) drawing app developed for [SI17](https://issue.xpub.nl/17/).
|
||||
|
||||
Write something, upload it and send the link to someone else: they will continue from your excerpt. With a catch: if you send to just one person the chain will continue linearly, but send it to more people and things will start branching in different directions.
|
||||
|
||||
Could be a writing machine to work on the collective pubblication for the graduation, inspired by what [Kim wrote here](https://pad.xpub.nl/p/gradcollectivexpub).
|
||||
|
||||
|
||||
## Install
|
||||
|
||||
Clone the repository.
|
||||
```
|
||||
git clone https://git.xpub.nl/kamo/exex.git
|
||||
```
|
||||
|
||||
Create a virtual environment.
|
||||
```
|
||||
python3 -m venv venv
|
||||
```
|
||||
|
||||
Install the requirements with `pip` and the `requirements.txt` file.
|
||||
```
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
Create an `.env` file in the root folder of the project with the following variables:
|
||||
```
|
||||
DEBUG=True
|
||||
FLASK_ENV=development
|
||||
FLASK_APP=exquisite_branch
|
||||
```
|
||||
|
||||
Before running the app for the first time, be sure to initialize the database. __Watch out:__ this will delete the previous instance of the database along with its contents!
|
||||
```
|
||||
flask init-db
|
||||
```
|
||||
After initializing the database you can run the flask application
|
||||
```
|
||||
flask run
|
||||
```
|
||||
|
||||
## Overview
|
||||
|
||||
Exex saves contents in a database, and join them together in a branching version of the exquisite corpse.
|
||||
|
||||
The original exquisite corpse data structure is something similar to a linked list, where every drawing is connected to the previous one. Contents in _exex_ are saved in a database with the same principle.
|
||||
|
||||
![Diagram with the situationship of the entries](linked.jpg)
|
||||
|
||||
Each entry in the database has the following properties:
|
||||
|
||||
- `id`: a unique identifier for every entry
|
||||
- `branch`: a random name for the excerpt
|
||||
- `parent`: the random name of the previous excerpt
|
||||
- `content`: the actual content of the writings
|
||||
- `username`: the author of the excerpt
|
||||
|
||||
When generating the display page, every entry look up its `parent` property to position itself after that.
|
||||
|
||||
Mhh should rewrite this better bc is super convoluted ahah.
|
@ -1,14 +0,0 @@
|
||||
autopep8==1.6.0
|
||||
click==8.0.3
|
||||
colorama==0.4.4
|
||||
Flask==2.0.2
|
||||
Flask-Mako==0.4
|
||||
itsdangerous==2.0.1
|
||||
Jinja2==3.0.3
|
||||
Mako==1.2.0
|
||||
MarkupSafe==2.0.1
|
||||
pycodestyle==2.8.0
|
||||
python-dotenv==0.19.2
|
||||
shortuuid==1.0.8
|
||||
toml==0.10.2
|
||||
Werkzeug==2.0.3
|
Loading…
Reference in New Issue