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