add versioning
This commit is contained in:
parent
956dbadabd
commit
2f37577aaa
|
@ -5,15 +5,19 @@ from sqlalchemy.ext.declarative import declarative_base
|
||||||
from sqlalchemy import Column, ForeignKey
|
from sqlalchemy import Column, ForeignKey
|
||||||
from sqlalchemy.types import String, Unicode, Integer, DateTime, Boolean, UnicodeText, Enum
|
from sqlalchemy.types import String, Unicode, Integer, DateTime, Boolean, UnicodeText, Enum
|
||||||
from sqlalchemy import func
|
from sqlalchemy import func
|
||||||
from sqlalchemy.orm import relationship, validates, synonym
|
from sqlalchemy.orm import relationship, validates, synonym, configure_mappers
|
||||||
from sqlalchemy.sql import exists
|
from sqlalchemy.sql import exists
|
||||||
from flask_login import UserMixin
|
from flask_login import UserMixin
|
||||||
from werkzeug.security import generate_password_hash, check_password_hash
|
from werkzeug.security import generate_password_hash, check_password_hash
|
||||||
from sqlalchemy.ext.hybrid import hybrid_property
|
from sqlalchemy.ext.hybrid import hybrid_property
|
||||||
|
from sqlalchemy_continuum import make_versioned
|
||||||
|
from sqlalchemy_continuum.plugins import FlaskPlugin, ActivityPlugin
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from hashids import Hashids
|
from hashids import Hashids
|
||||||
|
|
||||||
|
activity_plugin = ActivityPlugin()
|
||||||
|
make_versioned(plugins=[FlaskPlugin(), activity_plugin])
|
||||||
|
|
||||||
doc_hashids = Hashids(min_length=8)
|
doc_hashids = Hashids(min_length=8)
|
||||||
|
|
||||||
Base = declarative_base()
|
Base = declarative_base()
|
||||||
|
@ -119,7 +123,9 @@ class User(TimeStampedModel, UserMixin):
|
||||||
return user
|
return user
|
||||||
|
|
||||||
class Item(TimeStampedModel):
|
class Item(TimeStampedModel):
|
||||||
|
__versioned__ = {}
|
||||||
__tablename__ = 'item'
|
__tablename__ = 'item'
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
user_id = Column(Integer, ForeignKey('user.id'))
|
user_id = Column(Integer, ForeignKey('user.id'))
|
||||||
published = Column(DateTime)
|
published = Column(DateTime)
|
||||||
|
@ -148,7 +154,19 @@ class Item(TimeStampedModel):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def url(self):
|
def url(self):
|
||||||
return url_for('.view_item', username=self.user.username, hashid=self.hashid)
|
return url_for('.view_item',
|
||||||
|
username=self.user.username,
|
||||||
|
hashid=self.hashid)
|
||||||
|
|
||||||
|
def version_url(self, version):
|
||||||
|
return url_for('.view_item',
|
||||||
|
username=self.user.username,
|
||||||
|
hashid=self.hashid,
|
||||||
|
v=version.transaction_id)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def history_url(self):
|
||||||
|
return url_for('.history', username=self.user.username, hashid=self.hashid)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def external_url(self):
|
def external_url(self):
|
||||||
|
@ -245,3 +263,6 @@ class SourceDoc(Item):
|
||||||
return self.db_price_per_character or self.db_document_price / len(self.text)
|
return self.db_price_per_character or self.db_document_price / len(self.text)
|
||||||
|
|
||||||
__mapper_args__ = {'polymorphic_identity': 'sourcedoc'}
|
__mapper_args__ = {'polymorphic_identity': 'sourcedoc'}
|
||||||
|
|
||||||
|
|
||||||
|
configure_mappers()
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="#">History</a>
|
<a class="nav-link" href="{{ doc.history_url }}">History</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link active" href="{{ doc.edit_url }}">Edit</a>
|
<a class="nav-link active" href="{{ doc.edit_url }}">Edit</a>
|
||||||
|
|
35
sourcing/templates/history.html
Normal file
35
sourcing/templates/history.html
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}{{ doc.title() }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ doc.url }}">View</a>
|
||||||
|
</li>
|
||||||
|
{% if doc.type == 'xanadoc' %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ doc.url }}/fulfil">Fulfil</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" href="{{ doc.history_url }}">History</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ doc.edit_url }}">Edit</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ request.url }}/raw">Raw</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h1 class="mt-3">{{ self.title() }}</h1>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{% for v in doc.versions %}
|
||||||
|
<li><a href="{{ doc.version_url(v) }}">{{ v.modified.strftime('%H:%M:%S, %d %B %Y') }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -14,7 +14,7 @@
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="#">History</a>
|
<a class="nav-link" href="{{ doc.history_url }}">History</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{{ doc.edit_url }}">Edit</a>
|
<a class="nav-link" href="{{ doc.edit_url }}">Edit</a>
|
||||||
|
@ -25,11 +25,15 @@
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h1 class="mt-3">{{ self.title() }}</h1>
|
<h1 class="mt-3">{{ self.title() }}</h1>
|
||||||
|
{% if version %}
|
||||||
|
<p>Revision as of {{ version.modified.strftime('%H:%M, %d %B %Y') }}</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<div id="text">
|
<div class="card mb-2" id="text">
|
||||||
|
<div class="card-block">
|
||||||
{% if span_length %}
|
{% if span_length %}
|
||||||
{%- for start, line in add_highlight(doc.text, span_start, span_length) if line -%}
|
{%- for start, line in add_highlight(text, span_start, span_length) if line -%}
|
||||||
<p data-start="{{ start }}">
|
<p class="card-text" data-start="{{ start }}">
|
||||||
{% for i in line %}
|
{% for i in line %}
|
||||||
{%- if i.highlight -%}
|
{%- if i.highlight -%}
|
||||||
{%- if i.highlight != '\n' and i.highlight != '\r\n' -%}
|
{%- if i.highlight != '\n' and i.highlight != '\r\n' -%}
|
||||||
|
@ -42,15 +46,16 @@
|
||||||
</p>
|
</p>
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
{% else %}
|
{% else %}
|
||||||
{%- for start, line in iter_lines(doc.text) if line -%}
|
{%- for start, line in iter_lines(text) if line -%}
|
||||||
{# <p data-start="{{ start }}">{% if line != "\n" and line != "\r\n" %}{{ line }}{% else %} {% endif
|
{# <p data-start="{{ start }}">{% if line != "\n" and line != "\r\n" %}{{ line }}{% else %} {% endif
|
||||||
%}</p>#}
|
%}</p>#}
|
||||||
<p data-start="{{ start }}">{{ nbsp_at_start(line) }}</p>
|
<p data-start="{{ start }}">{{ nbsp_at_start(line) }}</p>
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% if doc.type == 'sourcedoc' and not span_length %}
|
{% if not version and doc.type == 'sourcedoc' and not span_length %}
|
||||||
<button id="show-span-selector" class="btn btn-primary">show span selector</button>
|
<button id="show-span-selector" class="btn btn-primary">show span selector</button>
|
||||||
<button id="select-all" class="btn btn-primary">get entire document span</button>
|
<button id="select-all" class="btn btn-primary">get entire document span</button>
|
||||||
<p id="span-selector" class="d-none">span: <span id="span"></span></p>
|
<p id="span-selector" class="d-none">span: <span id="span"></span></p>
|
||||||
|
@ -60,7 +65,7 @@
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
<script>
|
<script>
|
||||||
var doc_url = '{{ doc.external_url }}';
|
var doc_url = '{{ doc.external_url }}';
|
||||||
var doc_length = {{ doc.text | length }};
|
var doc_length = {{ text | length }};
|
||||||
</script>
|
</script>
|
||||||
<script src="{{ url_for('static', filename='js/sourcedoc.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/sourcedoc.js') }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -16,6 +16,7 @@ from jinja2 import evalcontextfilter, Markup
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from .utils import nbsp_at_start
|
from .utils import nbsp_at_start
|
||||||
from itsdangerous import URLSafeTimedSerializer
|
from itsdangerous import URLSafeTimedSerializer
|
||||||
|
from sqlalchemy_continuum import version_class
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
@ -212,14 +213,31 @@ def view_item(username, hashid, raw=False):
|
||||||
if raw:
|
if raw:
|
||||||
return Response(item.text, mimetype='text/plain')
|
return Response(item.text, mimetype='text/plain')
|
||||||
|
|
||||||
|
if 'v' in request.args and request.args['v'].isdigit():
|
||||||
|
ItemVersion = version_class(Item)
|
||||||
|
version = (session.query(ItemVersion)
|
||||||
|
.filter_by(transaction_id=int(request.args['v']))
|
||||||
|
.first())
|
||||||
|
text = version.text
|
||||||
|
else:
|
||||||
|
version = None
|
||||||
|
text = item.text
|
||||||
|
|
||||||
return render_template('view.html',
|
return render_template('view.html',
|
||||||
doc=item,
|
doc=item,
|
||||||
|
version=version,
|
||||||
|
text=text,
|
||||||
span_start=start,
|
span_start=start,
|
||||||
span_length=length,
|
span_length=length,
|
||||||
add_highlight=add_highlight,
|
add_highlight=add_highlight,
|
||||||
nbsp_at_start=nbsp_at_start,
|
nbsp_at_start=nbsp_at_start,
|
||||||
iter_lines=iter_lines)
|
iter_lines=iter_lines)
|
||||||
|
|
||||||
|
@bp.route('/<username>/<hashid>/history')
|
||||||
|
def history(username, hashid):
|
||||||
|
item = get_item(username, hashid)
|
||||||
|
return render_template('history.html', doc=item)
|
||||||
|
|
||||||
@bp.route('/<username>/<hashid>/edit', methods=['GET', 'POST'])
|
@bp.route('/<username>/<hashid>/edit', methods=['GET', 'POST'])
|
||||||
def edit_item(username, hashid):
|
def edit_item(username, hashid):
|
||||||
obj = get_item(username, hashid)
|
obj = get_item(username, hashid)
|
||||||
|
|
Loading…
Reference in a new issue