Skip to content

Commit

Permalink
Allow 'rev' to be specified in GET requests. - #42
Browse files Browse the repository at this point in the history
Allows inspecting a historical revision of a transcript.

Facilitates further caching improvements.
  • Loading branch information
matthewryanscott committed Feb 12, 2012
1 parent d04edb7 commit 67b3759
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 57 deletions.
22 changes: 17 additions & 5 deletions fanscribed/repos.py
Expand Up @@ -30,12 +30,22 @@ def _snippet_ms():
return snippet_seconds * 1000


def repo_from_request(request):
def repo_from_request(request, rev=None):
"""Return the repository and commit based on the request.
The host of the request is inspected to determine the repository.
The 'rev' GET param is used to determine the commit (default: master).
"""
repos_path = app_settings()['fanscribed.repos']
repo_path = os.path.join(repos_path, request.host)
# Make sure repo path is underneath outer repos path.
assert '..' not in os.path.relpath(repo_path, repos_path)
return git.Repo(repo_path)
repo = git.Repo(repo_path)
# Only get rev from user if not specified in function call.
if rev is None:
rev = request.GET.get('rev', 'master')
commit = repo.commit(rev)
return (repo, commit)


def latest_revision(repo):
Expand All @@ -47,13 +57,15 @@ def transcription_info(tree):
return json.load(blob.data_stream)


def custom_css(tree):
def custom_css(repo, commit='master'):
tree = repo.tree(commit)
if 'custom.css' in tree:
mtime = repo.iter_commits(commit, 'custom.css').next().authored_date
blob = tree['custom.css']
return blob.data_stream.read().decode('utf8')
return (blob.data_stream.read().decode('utf8'), mtime)
else:
# Not yet created.
return ''
return ('', None)


def custom_css_revision(repo):
Expand Down
10 changes: 6 additions & 4 deletions fanscribed/static/fanscribed.js
Expand Up @@ -38,7 +38,7 @@ var common_onload = function () {
set_default_player_preferences();
fill_player_preferences();
fill_identity();
check_identity();
check_identity_and_rev();
player_enable();
player_shortcuts_enable();
};
Expand Down Expand Up @@ -69,14 +69,16 @@ var save_identity = function () {
$('#identity-saved').text('Saved!');
var clear = function () { $('#identity-saved').text(''); };
window.setTimeout(clear, 1000);
check_identity();
check_identity_and_rev();
return false;
};


// update UI elements based on whether identity found.
var check_identity = function () {
if (has_identity(false)) {
// if rev not specified or is not 'master', assume anonymous identity and present read-only behavior.
var check_identity_and_rev = function () {
var rev_is_master = !url_params.rev || url_params.rev == 'master';
if (has_identity(false) && rev_is_master) {
// enable UI needing identity
$('.needs-identity').removeClass('no-identity');
$('.needs-unset-identity').addClass('no-unset-identity');
Expand Down
2 changes: 1 addition & 1 deletion fanscribed/templates/base.mako
Expand Up @@ -15,7 +15,7 @@
<![endif]-->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript" src="${request.static_url('fanscribed:static/jquery.cookie.js')}?2012012401"></script>
<script type="text/javascript" src="${request.static_url('fanscribed:static/fanscribed.js')}?2012021101"></script>
<script type="text/javascript" src="${request.static_url('fanscribed:static/fanscribed.js')}?2012021201"></script>
<script type="text/javascript">
var cookie_options = {
expires: 365,
Expand Down
105 changes: 58 additions & 47 deletions fanscribed/views.py
Expand Up @@ -8,7 +8,7 @@

import git

from pyramid.httpexceptions import HTTPFound
from pyramid.httpexceptions import HTTPFound, HTTPNotFound
from pyramid.renderers import render
from pyramid.response import Response
from pyramid.threadlocal import get_current_registry
Expand Down Expand Up @@ -132,7 +132,8 @@ def _split_lines_and_expand_abbreviations(text, speakers_map):
return lines


def _standard_response(repo, tree):
def _standard_response(repo, commit):
tree = commit.tree
transcription_info = repos.transcription_info(tree)
return dict(
_progress_dicts(tree, transcription_info),
Expand Down Expand Up @@ -160,10 +161,9 @@ def robots_txt(request):
renderer='fanscribed:templates/edit.mako',
)
def edit(request):
repo = repos.repo_from_request(request)
master = repo.tree('master')
repo, commit = repos.repo_from_request(request, rev='master')
return dict(
_standard_response(repo, master),
_standard_response(repo, commit),
)


Expand All @@ -173,16 +173,16 @@ def edit(request):
context='fanscribed:resources.Root',
)
def view(request):
repo = repos.repo_from_request(request)
repo, commit = repos.repo_from_request(request)
# Return cached if found.
cache_key = repo.head.commit.hexsha
cache_key = commit.hexsha
content, mtime = cache.get_cached_content(cache_key)
if content is None:
mtime = repo.head.commit.authored_date
master = repo.tree('master')
transcription_info = repos.transcription_info(master)
mtime = commit.authored_date
tree = commit.tree
transcription_info = repos.transcription_info(tree)
raw_snippets = {}
for obj in master:
for obj in tree:
if isinstance(obj, git.Blob):
name, ext = os.path.splitext(obj.name)
if ext == '.txt':
Expand All @@ -194,13 +194,13 @@ def view(request):
raw_snippets[starting_point] = obj.data_stream.read().decode('utf8')
# Go through all snippets, whether they've been transcribed or not.
snippets = []
speakers_map = repos.speakers_map(master)
speakers_map = repos.speakers_map(tree)
for starting_point in range(0, transcription_info['duration'], _snippet_ms()):
text = raw_snippets.get(starting_point, '').strip()
lines = _split_lines_and_expand_abbreviations(text, speakers_map)
snippets.append((starting_point, lines))
data = dict(
_standard_response(repo, master),
_standard_response(repo, commit),
snippets=sorted(snippets),
)
content = render('fanscribed:templates/view.mako', data, request=request)
Expand All @@ -214,10 +214,16 @@ def view(request):
context='fanscribed:resources.Root',
)
def custom_css(request):
repo = repos.repo_from_request(request)
master = repo.tree('master')
text = repos.custom_css(master)
return Response(text, content_type='text/css')
repo, commit = repos.repo_from_request(request)
tree = commit.tree
if 'custom.css' in tree:
mtime = repo.iter_commits(commit, 'custom.css').next().authored_date
blob = tree['custom.css']
content = blob.data_stream.read().decode('utf8')
return Response(content, date=mtime, content_type='text/css')
else:
# Not yet created.
raise HTTPNotFound()


@view_config(
Expand All @@ -226,9 +232,9 @@ def custom_css(request):
context='fanscribed:resources.Root',
)
def speakers_txt(request):
repo = repos.repo_from_request(request)
master = repo.tree('master')
text = repos.speakers_text(master)
repo, commit = repos.repo_from_request(request)
tree = commit.tree
text = repos.speakers_text(tree)
return Response(text, content_type='text/plain')


Expand All @@ -242,7 +248,7 @@ def post_speakers_txt(request):
identity_name = request.POST.getone('identity_name')
identity_email = request.POST.getone('identity_email')
# Save transcription info.
repo = repos.repo_from_request(request)
repo, commit = repos.repo_from_request(request, rev='master')
with repos.commit_lock:
repo.heads['master'].checkout()
index = repo.index
Expand All @@ -254,8 +260,8 @@ def post_speakers_txt(request):
os.environ['GIT_AUTHOR_EMAIL'] = identity_email
index.commit('speakers: save')
# Reload from repo and serve it up.
master = repo.tree('master')
text = repos.speakers_text(master)
tree = commit.tree
text = repos.speakers_text(tree)
return Response(text, content_type='text/plain')


Expand All @@ -265,9 +271,9 @@ def post_speakers_txt(request):
context='fanscribed:resources.Root',
)
def transcription_json(request):
repo = repos.repo_from_request(request)
master = repo.tree('master')
info = repos.transcription_info(master)
repo, commit = repos.repo_from_request(request)
tree = commit.tree
info = repos.transcription_info(tree)
# Inject additional information into the info dict.
settings = app_settings()
info['snippet_ms'] = int(settings['fanscribed.snippet_seconds']) * 1000
Expand All @@ -281,10 +287,10 @@ def transcription_json(request):
context='fanscribed:resources.Root',
)
def progress(request):
repo = repos.repo_from_request(request)
master = repo.tree('master')
info = repos.transcription_info(master)
return Response(body=json.dumps(_progress_dicts(master, info)), content_type='application/json')
repo, commit = repos.repo_from_request(request)
tree = commit.tree
info = repos.transcription_info(tree)
return Response(body=json.dumps(_progress_dicts(tree, info)), content_type='application/json')


@view_config(
Expand All @@ -293,11 +299,11 @@ def progress(request):
context='fanscribed:resources.Root',
)
def snippet_info(request):
repo, commit = repos.repo_from_request(request)
starting_point = int(request.GET.getone('starting_point'))
filename = '{0:016d}.txt'.format(starting_point)
repo = repos.repo_from_request(request)
contributor_list = []
for commit in repo.iter_commits('master', paths=filename):
for commit in repo.iter_commits(commit, paths=filename):
contributor = dict(author_name=commit.author.name)
if contributor not in contributor_list:
contributor_list.append(contributor)
Expand Down Expand Up @@ -353,7 +359,7 @@ def lock_snippet(request):
if desired_starting_point is not None:
desired_starting_point = int(desired_starting_point)
with repos.commit_lock:
repo = repos.repo_from_request(request)
repo, commit = repos.repo_from_request(request, rev='master')
index = repo.index
# find and lock available snippet
starting_point, lock_secret_or_message = repos.lock_available_snippet(repo, index, desired_starting_point)
Expand Down Expand Up @@ -400,7 +406,8 @@ def lock_review(request):
identity_name = request.POST.getone('identity_name')
identity_email = request.POST.getone('identity_email')
with repos.commit_lock:
repo = repos.repo_from_request(request)
# Ignore commit; we want to save to master.
repo, commit = repos.repo_from_request(request, rev='master')
index = repo.index
# find and lock available review
starting_point, lock_secret_or_message = repos.lock_available_review(repo, index)
Expand Down Expand Up @@ -446,7 +453,8 @@ def save_snippet(request):
inline = request.POST.get('inline') == '1'
with repos.commit_lock:
# find and validate the lock
repo = repos.repo_from_request(request)
# Ignore commit; we want to save to master.
repo, commit = repos.repo_from_request(request, rev='master')
index = repo.index
if not repos.lock_is_valid(repo, index, 'snippet', starting_point, lock_secret):
raise ValueError('Invalid lock')
Expand Down Expand Up @@ -482,7 +490,8 @@ def save_review(request):
review_text_2 = request.POST.getone('review_text_2')
with repos.commit_lock:
# find and validate the lock
repo = repos.repo_from_request(request)
# Ignore commit; we want to save to master.
repo, commit = repos.repo_from_request(request, rev='master')
index = repo.index
if not repos.lock_is_valid(repo, index, 'review', starting_point, lock_secret):
raise ValueError('Invalid lock')
Expand Down Expand Up @@ -514,7 +523,8 @@ def cancel_snippet(request):
identity_email = request.POST.getone('identity_email')
with repos.commit_lock:
# find and validate the lock
repo = repos.repo_from_request(request)
# Ignore commit; we want to save to master.
repo, commit = repos.repo_from_request(request, rev='master')
index = repo.index
if not repos.lock_is_valid(repo, index, 'snippet', starting_point, lock_secret):
raise ValueError('Invalid lock')
Expand All @@ -541,7 +551,8 @@ def cancel_review(request):
identity_email = request.POST.getone('identity_email')
with repos.commit_lock:
# find and validate the lock
repo = repos.repo_from_request(request)
# Ignore commit; we want to save to master.
repo, commit = repos.repo_from_request(request, rev='master')
index = repo.index
if not repos.lock_is_valid(repo, index, 'review', starting_point, lock_secret):
raise ValueError('Invalid lock')
Expand All @@ -561,14 +572,14 @@ def cancel_review(request):
context='fanscribed:resources.Root',
)
def snippet_mp3(request):
repo, commit = repos.repo_from_request(request)
# Get information needed from settings and repository.
settings = app_settings()
full_mp3 = os.path.join(
settings['fanscribed.audio'],
'{0}.mp3'.format(request.host),
)
repo = repos.repo_from_request(request)
transcription_info = repos.transcription_info(repo.tree('master'))
transcription_info = repos.transcription_info(commit.tree)
duration = transcription_info['duration']
snippet_cache = settings['fanscribed.snippet_cache']
snippet_url_prefix = settings['fanscribed.snippet_url_prefix']
Expand Down Expand Up @@ -596,27 +607,27 @@ def snippet_mp3(request):
)
def updated(request):
"""Return formatted snippets that have been updated since the given revision."""
repo = repos.repo_from_request(request)
since_revision = request.GET.getone('since')
since_commit = repo.commit(since_revision)
repo, request_commit = repos.repo_from_request(request)
since_rev = request.GET.getone('since')
since_commit = repo.commit(since_rev)
files_updated = set()
for commit in repo.iter_commits('master'):
for commit in repo.iter_commits(request_commit):
# Have we reached the end?
if commit == since_commit:
break
# Look for snippet changes.
for filename in commit.stats.files:
if len(filename) == 20 and filename.endswith('.txt'):
files_updated.add(filename)
master = repo.tree('master')
speakers_map = repos.speakers_map(master)
tree = request_commit.tree
speakers_map = repos.speakers_map(tree)
snippets = []
for filename in files_updated:
starting_point = int(filename[:16])
snippet = dict(
starting_point=starting_point,
)
text = master[filename].data_stream.read().strip()
text = tree[filename].data_stream.read().strip()
snippet['lines'] = _split_lines_and_expand_abbreviations(text, speakers_map)
snippets.append(snippet)
data = dict(
Expand Down

0 comments on commit 67b3759

Please sign in to comment.