diff --git a/swh/web/ui/templates/content.html b/swh/web/ui/templates/content.html
index 406933db..2da5bd02 100644
--- a/swh/web/ui/templates/content.html
+++ b/swh/web/ui/templates/content.html
@@ -1,27 +1,28 @@
{% extends "layout.html" %}
{% block title %}Content information{% endblock %}
+
{% block content %}
{% if message is not none %}
-
{{ message | safe }}
+ {{ message }}
{% endif %}
{% if content is not none %}
{% for key in ['sha1', 'sha256', 'sha1_git', 'status', 'length', 'ctime'] %}
{{ key }}
{{ content[key] }}
{% endfor %}
{% if content['data_url'] is not none %}
{% endif %}
{% if content['data'] is not none %}
{% endif %}
{% endif %}
{% endblock %}
diff --git a/swh/web/ui/tests/test_utils.py b/swh/web/ui/tests/test_utils.py
index 6f424724..efe58d38 100644
--- a/swh/web/ui/tests/test_utils.py
+++ b/swh/web/ui/tests/test_utils.py
@@ -1,559 +1,560 @@
# Copyright (C) 2015 The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU Affero General Public License version 3, or any later version
# See top-level LICENSE file for more information
import datetime
import dateutil
import unittest
from unittest.mock import patch, call
from nose.tools import istest
from swh.web.ui import utils
class UtilsTestCase(unittest.TestCase):
def setUp(self):
self.url_map = [dict(rule='/other/',
methods=set(['GET', 'POST', 'HEAD']),
endpoint='foo'),
dict(rule='/some/old/url/',
methods=set(['GET', 'POST']),
endpoint='blablafn'),
dict(rule='/other/old/url/',
methods=set(['GET', 'HEAD']),
endpoint='bar'),
dict(rule='/other',
methods=set([]),
endpoint=None),
dict(rule='/other2',
methods=set([]),
endpoint=None)]
@istest
def filter_endpoints_1(self):
# when
actual_data = utils.filter_endpoints(self.url_map, '/some')
# then
self.assertEquals(actual_data, {
'/some/old/url/': {
'methods': ['GET', 'POST'],
'endpoint': 'blablafn'
}
})
@istest
def filter_endpoints_2(self):
# when
actual_data = utils.filter_endpoints(self.url_map, '/other',
blacklist=['/other2'])
# then
# rules /other is skipped because its' exactly the prefix url
# rules /other2 is skipped because it's blacklisted
self.assertEquals(actual_data, {
'/other/': {
'methods': ['GET', 'HEAD', 'POST'],
'endpoint': 'foo'
},
'/other/old/url/': {
'methods': ['GET', 'HEAD'],
'endpoint': 'bar'
}
})
@istest
def prepare_data_for_view_default_encoding(self):
self.maxDiff = None
# given
inputs = [
{
'data': b'some blah data'
},
{
'data': 1,
'data_url': '/api/1/some/api/call',
},
{
'blah': 'foobar',
'blah_url': '/some/non/changed/api/call'
}]
# when
actual_result = utils.prepare_data_for_view(inputs)
# then
self.assertEquals(actual_result, [
{
'data': 'some blah data',
},
{
'data': 1,
'data_url': '/browse/some/api/call',
},
{
'blah': 'foobar',
'blah_url': '/some/non/changed/api/call'
}
])
@istest
def prepare_data_for_view(self):
self.maxDiff = None
# given
inputs = [
{
'data': b'some blah data'
},
{
'data': 1,
'data_url': '/api/1/some/api/call',
},
{
'blah': 'foobar',
'blah_url': '/some/non/changed/api/call'
}]
# when
actual_result = utils.prepare_data_for_view(inputs, encoding='ascii')
# then
self.assertEquals(actual_result, [
{
'data': 'some blah data',
},
{
'data': 1,
'data_url': '/browse/some/api/call',
},
{
'blah': 'foobar',
'blah_url': '/some/non/changed/api/call'
}
])
@istest
def prepare_data_for_view_KO_cannot_decode(self):
self.maxDiff = None
# given
inputs = {
- 'data': 'hé dude!'.encode('utf-8'),
+ 'data': 'hé dude!'.encode('utf8'),
}
actual_result = utils.prepare_data_for_view(inputs, encoding='ascii')
# then
self.assertEquals(actual_result, {
- 'data': "Cannot decode the data bytes, try and set another"
- " encoding in the url or download directly the "
+ 'data': "Cannot decode the data bytes, try and set another "
+ "encoding in the url (e.g. ?encoding=utf8) or "
+ "download directly the "
"content's raw data.",
})
@istest
def filter_field_keys_dict_unknown_keys(self):
# when
actual_res = utils.filter_field_keys(
{'directory': 1, 'file': 2, 'link': 3},
{'directory1', 'file2'})
# then
self.assertEqual(actual_res, {})
@istest
def filter_field_keys_dict(self):
# when
actual_res = utils.filter_field_keys(
{'directory': 1, 'file': 2, 'link': 3},
{'directory', 'link'})
# then
self.assertEqual(actual_res, {'directory': 1, 'link': 3})
@istest
def filter_field_keys_list_unknown_keys(self):
# when
actual_res = utils.filter_field_keys(
[{'directory': 1, 'file': 2, 'link': 3},
{'1': 1, '2': 2, 'link': 3}],
{'d'})
# then
self.assertEqual(actual_res, [{}, {}])
@istest
def filter_field_keys_list(self):
# when
actual_res = utils.filter_field_keys(
[{'directory': 1, 'file': 2, 'link': 3},
{'dir': 1, 'fil': 2, 'lin': 3}],
{'directory', 'dir'})
# then
self.assertEqual(actual_res, [{'directory': 1}, {'dir': 1}])
@istest
def filter_field_keys_other(self):
# given
input_set = {1, 2}
# when
actual_res = utils.filter_field_keys(input_set, {'a', '1'})
# then
self.assertEqual(actual_res, input_set)
@istest
def fmap(self):
self.assertEquals([2, 3, 4],
utils.fmap(lambda x: x+1, [1, 2, 3]))
self.assertEquals([11, 12, 13],
list(utils.fmap(lambda x: x+10,
map(lambda x: x, [1, 2, 3]))))
self.assertEquals({'a': 2, 'b': 4},
utils.fmap(lambda x: x*2, {'a': 1, 'b': 2}))
self.assertEquals(100,
utils.fmap(lambda x: x*10, 10))
self.assertEquals({'a': [2, 6], 'b': 4},
utils.fmap(lambda x: x*2, {'a': [1, 3], 'b': 2}))
@istest
def person_to_string(self):
self.assertEqual(utils.person_to_string(dict(name='raboof',
email='foo@bar')),
'raboof ')
@istest
def parse_timestamp(self):
input_timestamps = [
'2016-01-12',
'2016-01-12T09:19:12+0100',
'Today is January 1, 2047 at 8:21:00AM',
'1452591542',
]
output_dates = [
datetime.datetime(2016, 1, 12, 0, 0),
datetime.datetime(2016, 1, 12, 9, 19, 12,
tzinfo=dateutil.tz.tzoffset(None, 3600)),
datetime.datetime(2047, 1, 1, 8, 21),
datetime.datetime(2016, 1, 12, 10, 39, 2),
]
for ts, exp_date in zip(input_timestamps, output_dates):
self.assertEquals(utils.parse_timestamp(ts), exp_date)
@istest
def enrich_release_0(self):
# when
actual_release = utils.enrich_release({})
# then
self.assertEqual(actual_release, {})
@patch('swh.web.ui.utils.flask')
@istest
def enrich_release_1(self, mock_flask):
# given
mock_flask.url_for.return_value = '/api/1/content/sha1_git:123/'
# when
actual_release = utils.enrich_release({'target': '123',
'target_type': 'content'})
# then
self.assertEqual(actual_release, {
'target': '123',
'target_type': 'content',
'target_url': '/api/1/content/sha1_git:123/'
})
mock_flask.url_for.assert_called_once_with('api_content_metadata',
q='sha1_git:123')
@patch('swh.web.ui.utils.flask')
@istest
def enrich_release_2(self, mock_flask):
# given
mock_flask.url_for.return_value = '/api/1/dir/23/'
# when
actual_release = utils.enrich_release({'target': '23',
'target_type': 'directory'})
# then
self.assertEqual(actual_release, {
'target': '23',
'target_type': 'directory',
'target_url': '/api/1/dir/23/'
})
mock_flask.url_for.assert_called_once_with('api_directory',
q='23')
@patch('swh.web.ui.utils.flask')
@istest
def enrich_release_3(self, mock_flask):
# given
mock_flask.url_for.return_value = '/api/1/rev/3/'
# when
actual_release = utils.enrich_release({'target': '3',
'target_type': 'revision'})
# then
self.assertEqual(actual_release, {
'target': '3',
'target_type': 'revision',
'target_url': '/api/1/rev/3/'
})
mock_flask.url_for.assert_called_once_with('api_revision',
sha1_git='3')
@patch('swh.web.ui.utils.flask')
@istest
def enrich_release_4(self, mock_flask):
# given
mock_flask.url_for.return_value = '/api/1/rev/4/'
# when
actual_release = utils.enrich_release({'target': '4',
'target_type': 'release'})
# then
self.assertEqual(actual_release, {
'target': '4',
'target_type': 'release',
'target_url': '/api/1/rev/4/'
})
mock_flask.url_for.assert_called_once_with('api_release',
sha1_git='4')
@patch('swh.web.ui.utils.flask')
@istest
def enrich_directory_no_type(self, mock_flask):
# when/then
self.assertEqual(utils.enrich_directory({'id': 'dir-id'}),
{'id': 'dir-id'})
# given
mock_flask.url_for.return_value = '/api/content/sha1_git:123/'
# when
actual_directory = utils.enrich_directory({
'id': 'dir-id',
'type': 'file',
'target': '123',
})
# then
self.assertEqual(actual_directory, {
'id': 'dir-id',
'type': 'file',
'target': '123',
'target_url': '/api/content/sha1_git:123/',
})
mock_flask.url_for.assert_called_once_with('api_content_metadata',
q='sha1_git:123')
@patch('swh.web.ui.utils.flask')
@istest
def enrich_directory_with_context_and_type_file(self, mock_flask):
# given
mock_flask.url_for.return_value = '/api/content/sha1_git:123/'
# when
actual_directory = utils.enrich_directory({
'id': 'dir-id',
'type': 'file',
'name': 'hy',
'target': '789',
}, context_url='/api/revision/revsha1/directory/prefix/path/')
# then
self.assertEqual(actual_directory, {
'id': 'dir-id',
'type': 'file',
'name': 'hy',
'target': '789',
'target_url': '/api/content/sha1_git:123/',
'file_url': '/api/revision/revsha1/directory'
'/prefix/path/hy/'
})
mock_flask.url_for.assert_called_once_with('api_content_metadata',
q='sha1_git:789')
@patch('swh.web.ui.utils.flask')
@istest
def enrich_directory_with_context_and_type_dir(self, mock_flask):
# given
mock_flask.url_for.return_value = '/api/directory/456/'
# when
actual_directory = utils.enrich_directory({
'id': 'dir-id',
'type': 'dir',
'name': 'emacs-42',
'target_type': 'file',
'target': '456',
}, context_url='/api/revision/origin/2/directory/some/prefix/path/')
# then
self.assertEqual(actual_directory, {
'id': 'dir-id',
'type': 'dir',
'target_type': 'file',
'name': 'emacs-42',
'target': '456',
'target_url': '/api/directory/456/',
'dir_url': '/api/revision/origin/2/directory'
'/some/prefix/path/emacs-42/'
})
mock_flask.url_for.assert_called_once_with('api_directory',
sha1_git='456')
@istest
def enrich_content_without_sha1(self):
# when/then
self.assertEqual(utils.enrich_content({'id': '123'}),
{'id': '123'})
@patch('swh.web.ui.utils.flask')
@istest
def enrich_content_with_sha1(self, mock_flask):
# given
mock_flask.url_for.return_value = '/api/content/sha1:123/raw/'
# when/then
self.assertEqual(utils.enrich_content(
{'id': '123', 'sha1': 'blahblah'}),
{'id': '123', 'sha1': 'blahblah',
'data_url': '/api/content/sha1:123/raw/'})
mock_flask.url_for.assert_called_once_with('api_content_raw',
q='blahblah')
@istest
def enrich_entity_identity(self):
# when/then
self.assertEqual(utils.enrich_content({'id': '123'}),
{'id': '123'})
@patch('swh.web.ui.utils.flask')
@istest
def enrich_entity_with_sha1(self, mock_flask):
# given
def url_for_test(fn, **entity):
return '/api/entity/' + entity['uuid'] + '/'
mock_flask.url_for.side_effect = url_for_test
# when
actual_entity = utils.enrich_entity({
'uuid': 'uuid-1',
'parent': 'uuid-parent',
'name': 'something'
})
# then
self.assertEqual(actual_entity, {
'uuid': 'uuid-1',
'uuid_url': '/api/entity/uuid-1/',
'parent': 'uuid-parent',
'parent_url': '/api/entity/uuid-parent/',
'name': 'something',
})
mock_flask.url_for.assert_has_calls([call('api_entity_by_uuid',
uuid='uuid-1'),
call('api_entity_by_uuid',
uuid='uuid-parent')])
@patch('swh.web.ui.utils.flask')
@istest
def enrich_revision_without_children_or_parent(self, mock_flask):
# given
def url_for_test(fn, **data):
print(fn, data)
if fn == 'api_revision':
return '/api/revision/' + data['sha1_git'] + '/'
elif fn == 'api_revision_log':
return '/api/revision/' + data['sha1_git'] + '/log/'
elif fn == 'api_directory':
return '/api/directory/' + data['sha1_git'] + '/'
mock_flask.url_for.side_effect = url_for_test
# when
actual_revision = utils.enrich_revision({
'id': 'rev-id',
'directory': '123'
})
# then
self.assertEqual(actual_revision, {
'id': 'rev-id',
'directory': '123',
'url': '/api/revision/rev-id/',
'history_url': '/api/revision/rev-id/log/',
'directory_url': '/api/directory/123/'
})
mock_flask.url_for.assert_has_calls([call('api_revision',
sha1_git='rev-id'),
call('api_revision_log',
sha1_git='rev-id'),
call('api_directory',
sha1_git='123')])
@patch('swh.web.ui.utils.flask')
@istest
def enrich_revision_with_children_and_parent_no_dir(self,
mock_flask):
# given
def url_for_test(fn, **data):
print(fn, data)
if fn == 'api_revision':
return '/api/revision/' + data['sha1_git'] + '/'
elif fn == 'api_revision_log':
return '/api/revision/' + data['sha1_git'] + '/log/'
else:
return '/api/revision/' + data['sha1_git_root'] + '/history/' + data['sha1_git'] + '/' # noqa
mock_flask.url_for.side_effect = url_for_test
# when
actual_revision = utils.enrich_revision({
'id': 'rev-id',
'parents': ['123'],
'children': ['456'],
}, context='sha1_git_root')
# then
self.assertEqual(actual_revision, {
'id': 'rev-id',
'url': '/api/revision/rev-id/',
'history_url': '/api/revision/rev-id/log/',
'parents': ['123'],
'parent_urls': ['/api/revision/sha1_git_root/history/123/'],
'children': ['456'],
'children_urls': ['/api/revision/sha1_git_root/history/456/'],
})
mock_flask.url_for.assert_has_calls(
[call('api_revision',
sha1_git='rev-id'),
call('api_revision_log',
sha1_git='rev-id'),
call('api_revision_history',
sha1_git_root='sha1_git_root',
sha1_git='123'),
call('api_revision_history',
sha1_git_root='sha1_git_root',
sha1_git='456')])
diff --git a/swh/web/ui/utils.py b/swh/web/ui/utils.py
index 31721c07..efda2a0a 100644
--- a/swh/web/ui/utils.py
+++ b/swh/web/ui/utils.py
@@ -1,234 +1,235 @@
# Copyright (C) 2015 The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU Affero General Public License version 3, or any later version
# See top-level LICENSE file for more information
import datetime
import flask
import re
from dateutil import parser
def filter_endpoints(url_map, prefix_url_rule, blacklist=[]):
"""Filter endpoints by prefix url rule.
Args:
- url_map: Url Werkzeug.Map of rules
- prefix_url_rule: prefix url string
- blacklist: blacklist of some url
Returns:
Dictionary of url_rule with values methods and endpoint.
The key is the url, the associated value is a dictionary of
'methods' (possible http methods) and 'endpoint' (python function)
"""
out = {}
for r in url_map:
rule = r['rule']
if rule == prefix_url_rule or rule in blacklist:
continue
if rule.startswith(prefix_url_rule):
out[rule] = {'methods': sorted(map(str, r['methods'])),
'endpoint': r['endpoint']}
return out
def fmap(f, data):
"""Map f to data.
Keep the initial data structure as original but map function f to each
level.
Args:
f: function that expects one argument.
data: data to traverse to apply the f function. list, map, dict or bare
value.
Returns:
The same data-structure with modified values by the f function.
"""
if isinstance(data, map):
return (fmap(f, x) for x in data)
if isinstance(data, list):
return [fmap(f, x) for x in data]
if isinstance(data, dict):
return {k: fmap(f, v) for (k, v) in data.items()}
return f(data)
def prepare_data_for_view(data, encoding='utf-8'):
def prepare_data(s):
# Note: can only be 'data' key with bytes of raw content
if isinstance(s, bytes):
try:
return s.decode(encoding)
except:
- return "Cannot decode the data bytes, try and set another" \
- " encoding in the url or download directly the" \
- " content's raw data."
+ return "Cannot decode the data bytes, try and set another " \
+ "encoding in the url (e.g. ?encoding=utf8) or " \
+ "download directly the " \
+ "content's raw data."
if isinstance(s, str):
return re.sub(r'/api/1/', r'/browse/', s)
return s
return fmap(prepare_data, data)
def filter_field_keys(data, field_keys):
"""Given an object instance (directory or list), and a csv field keys
to filter on.
Return the object instance with filtered keys.
Note: Returns obj as is if it's an instance of types not in (dictionary,
list)
Args:
- data: one object (dictionary, list...) to filter.
- field_keys: csv or set of keys to filter the object on
Returns:
obj filtered on field_keys
"""
if isinstance(data, map):
return (filter_field_keys(x, field_keys) for x in data)
if isinstance(data, list):
return [filter_field_keys(x, field_keys) for x in data]
if isinstance(data, dict):
return {k: v for (k, v) in data.items() if k in field_keys}
return data
def person_to_string(person):
"""Map a person (person, committer, tagger, etc...) to a string.
"""
return ''.join([person['name'], ' <', person['email'], '>'])
def parse_timestamp(timestamp):
"""Given a time or timestamp (as string), parse the result as datetime.
Returns:
datetime result of parsing values.
Samples:
- 2016-01-12
- 2016-01-12T09:19:12+0100
- Today is January 1, 2047 at 8:21:00AM
- 1452591542
"""
try:
res = parser.parse(timestamp, ignoretz=False, fuzzy=True)
except:
res = datetime.datetime.fromtimestamp(float(timestamp))
return res
def enrich_release(release):
"""Enrich a release with link to the 'target' of 'type' revision.
"""
if 'target' in release and 'target_type' in release:
if release['target_type'] == 'revision':
release['target_url'] = flask.url_for('api_revision',
sha1_git=release['target'])
elif release['target_type'] == 'release':
release['target_url'] = flask.url_for('api_release',
sha1_git=release['target'])
elif release['target_type'] == 'content':
release['target_url'] = flask.url_for(
'api_content_metadata',
q='sha1_git:' + release['target'])
elif release['target_type'] == 'directory':
release['target_url'] = flask.url_for('api_directory',
q=release['target'])
return release
def enrich_directory(directory, context_url=None):
"""Enrich directory with url to content or directory.
"""
if 'type' in directory:
target_type = directory['type']
target = directory['target']
if target_type == 'file':
directory['target_url'] = flask.url_for('api_content_metadata',
q='sha1_git:%s' % target)
if context_url:
directory['file_url'] = context_url + directory['name'] + '/'
else:
directory['target_url'] = flask.url_for('api_directory',
sha1_git=target)
if context_url:
directory['dir_url'] = context_url + directory['name'] + '/'
return directory
def enrich_content(content):
"""Enrich content with 'data', a link to its raw content.
"""
if 'sha1' in content:
content['data_url'] = flask.url_for('api_content_raw',
q=content['sha1'])
return content
def enrich_entity(entity):
"""Enrich entity with
"""
if 'uuid' in entity:
entity['uuid_url'] = flask.url_for('api_entity_by_uuid',
uuid=entity['uuid'])
if 'parent' in entity and entity['parent']:
entity['parent_url'] = flask.url_for('api_entity_by_uuid',
uuid=entity['parent'])
return entity
def enrich_revision(revision, context=None):
"""Enrich revision with links where it makes sense (directory, parents).
"""
if not context:
context = revision['id']
revision['url'] = flask.url_for('api_revision', sha1_git=revision['id'])
revision['history_url'] = flask.url_for('api_revision_log',
sha1_git=revision['id'])
if 'directory' in revision:
revision['directory_url'] = flask.url_for(
'api_directory',
sha1_git=revision['directory'])
if 'parents' in revision:
parents = []
for parent in revision['parents']:
parents.append(flask.url_for('api_revision_history',
sha1_git_root=context,
sha1_git=parent))
revision['parent_urls'] = parents
if 'children' in revision:
children = []
for child in revision['children']:
children.append(flask.url_for('api_revision_history',
sha1_git_root=context,
sha1_git=child))
revision['children_urls'] = children
return revision
diff --git a/swh/web/ui/views.py b/swh/web/ui/views.py
index 566d2798..4d3ff291 100644
--- a/swh/web/ui/views.py
+++ b/swh/web/ui/views.py
@@ -1,707 +1,751 @@
# Copyright (C) 2015 The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU Affero General Public License version 3, or any later version
# See top-level LICENSE file for more information
import flask
+from encodings.aliases import aliases
from flask import render_template, request, url_for, redirect
from flask.ext.api.decorators import set_renderers
from flask.ext.api.renderers import HTMLRenderer
from swh.core.hashutil import ALGORITHMS
from swh.web.ui import service, utils, api
from swh.web.ui.exc import BadInputExc, NotFoundExc
from swh.web.ui.main import app
hash_filter_keys = ALGORITHMS
@app.route('/')
@set_renderers(HTMLRenderer)
def homepage():
"""Home page
"""
flask.flash('This Web app is still work in progress, use at your own risk',
'warning')
return render_template('home.html')
@app.route('/about/')
@set_renderers(HTMLRenderer)
def about():
return render_template('about.html')
@app.route('/search/', methods=['GET', 'POST'])
@set_renderers(HTMLRenderer)
def search():
"""Search for hashes in swh-storage.
One form to submit either:
- hash query to look up in swh storage
- some file content to upload, compute its hash and look it up in swh
storage
- both
Returns:
dict representing data to look for in swh storage.
The following keys are returned:
- file: File submitted for upload
- filename: Filename submitted for upload
- q: Query on hash to look for
- message: Message detailing if data has been found or not.
"""
env = {'filename': None,
'q': None,
'file': None}
data = None
q = env['q']
file = env['file']
if request.method == 'GET':
data = request.args
elif request.method == 'POST':
data = request.data
# or hash and search a file
file = request.files.get('filename')
# could either be a query for sha1 hash
q = data.get('q')
messages = []
if q:
env['q'] = q
try:
r = service.lookup_hash(q)
messages.append('Content with hash %s%sfound!' % (
q, ' ' if r.get('found') else ' not '))
except BadInputExc as e:
messages.append(str(e))
if file and file.filename:
env['file'] = file
try:
uploaded_content = service.upload_and_search(file)
filename = uploaded_content['filename']
sha1 = uploaded_content['sha1']
found = uploaded_content['found']
messages.append('File %s with hash %s%sfound!' % (
filename, sha1, ' ' if found else ' not '))
env.update({
'filename': filename,
'sha1': sha1,
})
except BadInputExc as e:
messages.append(str(e))
env['q'] = q if q else ''
env['messages'] = messages
return render_template('upload_and_search.html', **env)
@app.route('/browse/content/')
@app.route('/browse/content//')
@set_renderers(HTMLRenderer)
def browse_content(q='5d448a06f02d9de748b6b0b9620cba1bed8480da'):
"""Given a hash and a checksum, display the content's meta-data.
Args:
q is of the form algo_hash:hash with algo_hash in
(sha1, sha1_git, sha256)
Returns:
Information on one possible origin for such content.
Raises:
BadInputExc in case of unknown algo_hash or bad hash
NotFoundExc if the content is not found.
"""
env = {'q': q,
'message': None,
'content': None}
+
+ encoding = request.args.get('encoding', 'utf8')
+ if encoding not in aliases:
+ env['message'] = 'Encoding %s not supported.' \
+ 'Supported Encodings: %s' % (
+ encoding, list(aliases.keys()))
+ return render_template('content.html', **env)
+
try:
content = api.api_content_metadata(q)
content_raw = service.lookup_content_raw(q)
if content_raw:
content['data'] = content_raw['data']
- env['content'] = utils.prepare_data_for_view(content)
+ env['content'] = utils.prepare_data_for_view(content,
+ encoding=encoding)
except (NotFoundExc, BadInputExc) as e:
env['message'] = str(e)
return render_template('content.html', **env)
@app.route('/browse/content//raw/')
def browse_content_raw(q):
"""Given a hash and a checksum, display the content's raw data.
Args:
q is of the form algo_hash:hash with algo_hash in
(sha1, sha1_git, sha256)
Returns:
Information on one possible origin for such content.
Raises:
BadInputExc in case of unknown algo_hash or bad hash
NotFoundExc if the content is not found.
"""
return redirect(url_for('api_content_raw', q=q))
def _origin_seen(q, data):
"""Given an origin, compute a message string with the right information.
Args:
origin: a dictionary with keys:
- origin: a dictionary with type and url keys
- occurrence: a dictionary with a validity range
Returns:
Message as a string
"""
origin_type = data['origin_type']
origin_url = data['origin_url']
revision = data['revision']
branch = data['branch']
path = data['path']
return """The content with hash %s has been seen on origin with type '%s'
at url '%s'. The revision was identified at '%s' on branch '%s'.
The file's path referenced was '%s'.""" % (q,
origin_type,
origin_url,
revision,
branch,
path)
# @app.route('/browse/content//origin/')
@set_renderers(HTMLRenderer)
def browse_content_with_origin(
q='sha1:4320781056e5a735a39de0b8c229aea224590052'):
"""Show content information.
Args:
- q: query string of the form with
`algo_hash` in sha1, sha1_git, sha256.
This means that several different URLs (at least one per
HASH_ALGO) will point to the same content sha: the sha with
'hash' format
Returns:
The content's information at for a given checksum.
"""
env = {'q': q}
try:
origin = api.api_content_checksum_to_origin(q)
message = _origin_seen(q, origin)
except (NotFoundExc, BadInputExc) as e:
message = str(e)
env['message'] = message
return render_template('content-with-origin.html', **env)
@app.route('/browse/directory/')
@app.route('/browse/directory//')
@set_renderers(HTMLRenderer)
def browse_directory(sha1_git='dcf3289b576b1c8697f2a2d46909d36104208ba3'):
"""Show directory information.
Args:
- sha1_git: the directory's sha1 git identifier.
Returns:
The content's information at sha1_git
"""
env = {'sha1_git': sha1_git,
'files': []}
try:
directory_files = api.api_directory(sha1_git)
env['message'] = "Listing for directory %s:" % sha1_git
env['files'] = utils.prepare_data_for_view(directory_files)
except (NotFoundExc, BadInputExc) as e:
env['message'] = str(e)
return render_template('directory.html', **env)
@app.route('/browse/origin/')
@app.route('/browse/origin//')
@set_renderers(HTMLRenderer)
def browse_origin(origin_id=1):
"""Browse origin with id id.
"""
env = {'origin_id': origin_id,
'origin': None}
try:
env['origin'] = api.api_origin(origin_id)
except (NotFoundExc, BadInputExc) as e:
env['message'] = str(e)
return render_template('origin.html', **env)
@app.route('/browse/person/')
@app.route('/browse/person//')
@set_renderers(HTMLRenderer)
def browse_person(person_id=1):
"""Browse person with id id.
"""
env = {'person_id': person_id,
'person': None,
'message': None}
try:
env['person'] = api.api_person(person_id)
except (NotFoundExc, BadInputExc) as e:
env['message'] = str(e)
return render_template('person.html', **env)
@app.route('/browse/release/')
@app.route('/browse/release//')
@set_renderers(HTMLRenderer)
def browse_release(sha1_git='1e951912027ea6873da6985b91e50c47f645ae1a'):
"""Browse release with sha1_git.
"""
env = {'sha1_git': sha1_git,
'message': None,
'release': None}
try:
rel = api.api_release(sha1_git)
env['release'] = utils.prepare_data_for_view(rel)
except (NotFoundExc, BadInputExc) as e:
env['message'] = str(e)
return render_template('release.html', **env)
@app.route('/browse/revision/')
@app.route('/browse/revision//')
@set_renderers(HTMLRenderer)
def browse_revision(sha1_git='d770e558e21961ad6cfdf0ff7df0eb5d7d4f0754'):
"""Browse revision with sha1_git.
"""
env = {'sha1_git': sha1_git,
'message': None,
'revision': None}
try:
rev = api.api_revision(sha1_git)
env['revision'] = utils.prepare_data_for_view(rev)
except (NotFoundExc, BadInputExc) as e:
env['message'] = str(e)
return render_template('revision.html', **env)
@app.route('/browse/revision//log/')
@set_renderers(HTMLRenderer)
def browse_revision_log(sha1_git):
"""Browse revision with sha1_git.
"""
env = {'sha1_git': sha1_git,
'message': None,
'revisions': []}
try:
revisions = api.api_revision_log(sha1_git)
env['revisions'] = map(utils.prepare_data_for_view, revisions)
except (NotFoundExc, BadInputExc) as e:
env['message'] = str(e)
return render_template('revision-log.html', **env)
@app.route('/browse/revision//history//')
@set_renderers(HTMLRenderer)
def browse_revision_history(sha1_git_root, sha1_git):
"""Display information about revision sha1_git, limited to the
sub-graph of all transitive parents of sha1_git_root.
In other words, sha1_git is an ancestor of sha1_git_root.
Args:
sha1_git_root: latest revision of the browsed history.
sha1_git: one of sha1_git_root's ancestors.
limit: optional query parameter to limit the revisions log
(default to 100). For now, note that this limit could impede the
transitivity conclusion about sha1_git not being an ancestor of
sha1_git_root (even if it is).
Returns:
Information on sha1_git if it is an ancestor of sha1_git_root
including children leading to sha1_git_root.
"""
env = {'sha1_git_root': sha1_git_root,
'sha1_git': sha1_git,
'message': None,
'keys': [],
'revision': None}
if sha1_git == sha1_git_root:
return redirect(url_for('browse_revision',
sha1_git=sha1_git))
try:
revision = api.api_revision_history(sha1_git_root,
sha1_git)
env['revision'] = utils.prepare_data_for_view(revision)
except (BadInputExc, NotFoundExc) as e:
env['message'] = str(e)
return render_template('revision.html', **env)
@app.route('/browse/revision//directory/')
@app.route('/browse/revision//directory//')
@set_renderers(HTMLRenderer)
def browse_revision_directory(sha1_git, path=None):
"""Browse directory from revision with sha1_git.
"""
env = {
'sha1_git': sha1_git,
'path': '.' if not path else path,
'message': None,
'result': None
}
+ encoding = request.args.get('encoding', 'utf8')
+ if encoding not in aliases:
+ env['message'] = 'Encoding %s not supported.' \
+ 'Supported Encodings: %s' % (
+ encoding, list(aliases.keys()))
+ return render_template('revision-directory.html', **env)
+
try:
result = api.api_revision_directory(sha1_git, path, with_data=True)
- result['content'] = utils.prepare_data_for_view(result['content'])
+ result['content'] = utils.prepare_data_for_view(result['content'],
+ encoding=encoding)
env['revision'] = result['revision']
env['result'] = result
except (BadInputExc, NotFoundExc) as e:
env['message'] = str(e)
return render_template('revision-directory.html', **env)
@app.route('/browse/revision/'
'/history/'
'/directory/')
@app.route('/browse/revision/'
'/history/'
'/directory//')
@set_renderers(HTMLRenderer)
def browse_revision_history_directory(sha1_git_root, sha1_git, path=None):
"""Return information about directory pointed to by the revision
defined as: revision sha1_git, limited to the sub-graph of all
transitive parents of sha1_git_root.
Args:
sha1_git_root: latest revision of the browsed history.
sha1_git: one of sha1_git_root's ancestors.
path: optional directory pointed to by that revision.
limit: optional query parameter to limit the revisions log
(default to 100). For now, note that this limit could impede the
transitivity conclusion about sha1_git not being an ancestor of
sha1_git_root (even if it is).
Returns:
Information on the directory pointed to by that revision.
Raises:
BadInputExc in case of unknown algo_hash or bad hash.
NotFoundExc if either revision is not found or if sha1_git is not an
ancestor of sha1_git_root or the path referenced does not exist
"""
env = {
'sha1_git_root': sha1_git_root,
'sha1_git': sha1_git,
'path': '.' if not path else path,
'message': None,
'result': None
}
+ encoding = request.args.get('encoding', 'utf8')
+ if encoding not in aliases:
+ env['message'] = 'Encoding %s not supported.' \
+ 'Supported Encodings: %s' % (
+ encoding, list(aliases.keys()))
+ return render_template('revision-directory.html', **env)
+
if sha1_git == sha1_git_root:
return redirect(url_for('browse_revision_directory',
sha1_git=sha1_git,
- path=path),
+ path=path,
+ encoding=encoding),
code=301)
try:
result = api.api_revision_history_directory(sha1_git_root,
sha1_git,
path,
with_data=True)
env['revision'] = result['revision']
- env['content'] = utils.prepare_data_for_view(result['content'])
+ env['content'] = utils.prepare_data_for_view(result['content'],
+ encoding=encoding)
env['result'] = result
except (BadInputExc, NotFoundExc) as e:
env['message'] = str(e)
return render_template('revision-directory.html', **env)
@app.route('/browse/revision'
'/origin/'
'/history/'
'/directory/')
@app.route('/browse/revision'
'/origin/'
'/history/'
'/directory//')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/history/'
'/directory/')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/history/'
'/directory//')
@app.route('/browse/revision'
'/origin/'
'/ts/'
'/history/'
'/directory/')
@app.route('/browse/revision'
'/origin/'
'/ts/'
'/history/'
'/directory//')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/ts/'
'/history/'
'/directory/')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/ts/'
'/history/'
'/directory//')
@set_renderers(HTMLRenderer)
def browse_directory_through_revision_with_origin_history(
origin_id=1,
branch_name="refs/heads/master",
ts=None,
sha1_git=None,
path=None):
env = {
'origin_id': origin_id,
'branch_name': branch_name,
'ts': ts,
'sha1_git': sha1_git,
'path': '.' if not path else path,
'message': None,
'result': None
}
+ encoding = request.args.get('encoding', 'utf8')
+ if encoding not in aliases:
+ env['message'] = 'Encoding %s not supported.' \
+ 'Supported Encodings: %s' % (
+ encoding, list(aliases.keys()))
+ return render_template('revision-directory.html', **env)
+
try:
result = api.api_directory_through_revision_with_origin_history(
origin_id, branch_name, ts, sha1_git, path, with_data=True)
env['revision'] = result['revision']
- env['content'] = utils.prepare_data_for_view(result['content'])
+ env['content'] = utils.prepare_data_for_view(result['content'],
+ encoding=encoding)
env['result'] = result
except (BadInputExc, NotFoundExc) as e:
env['message'] = str(e)
return render_template('revision-directory.html', **env)
@app.route('/browse/revision'
'/origin/')
@app.route('/browse/revision'
'/origin//')
@app.route('/browse/revision'
'/origin/'
'/branch//')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/ts//')
@app.route('/browse/revision'
'/origin/'
'/ts//')
@set_renderers(HTMLRenderer)
def browse_revision_with_origin(origin_id=1,
branch_name="refs/heads/master",
ts=None):
"""Instead of having to specify a (root) revision by SHA1_GIT, users
might want to specify a place and a time. In SWH a "place" is an
origin; a "time" is a timestamp at which some place has been
observed by SWH crawlers.
Args:
origin_id: origin's identifier (default to 1).
branch_name: the optional branch for the given origin (default
to master).
timestamp: optional timestamp (default to the nearest time
crawl of timestamp).
Returns:
Information on the revision if found.
Raises:
BadInputExc in case of unknown algo_hash or bad hash.
NotFoundExc if the revision is not found.
"""
env = {'message': None,
'revision': None}
try:
revision = api.api_revision_with_origin(origin_id,
branch_name,
ts)
env['revision'] = utils.prepare_data_for_view(revision)
except (ValueError, NotFoundExc, BadInputExc) as e:
env['message'] = str(e)
return render_template('revision.html', **env)
@app.route('/browse/revision'
'/origin/'
'/history//')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/history//')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/ts/'
'/history//')
@set_renderers(HTMLRenderer)
def browse_revision_history_through_origin(origin_id,
branch_name='refs/heads/master',
ts=None,
sha1_git=None):
"""Return information about revision sha1_git, limited to the
sub-graph of all transitive parents of the revision root identified
by (origin_id, branch_name, ts).
Given sha1_git_root such root revision's identifier, in other words,
sha1_git is an ancestor of sha1_git_root.
Args:
origin_id: origin's identifier (default to 1).
branch_name: the optional branch for the given origin (default
to master).
timestamp: optional timestamp (default to the nearest time
crawl of timestamp).
sha1_git: one of sha1_git_root's ancestors.
limit: optional query parameter to limit the revisions log
(default to 100). For now, note that this limit could impede the
transitivity conclusion about sha1_git not being an ancestor of
sha1_git_root (even if it is).
Returns:
Information on sha1_git if it is an ancestor of sha1_git_root
including children leading to sha1_git_root.
"""
env = {'message': None,
'revision': None}
try:
revision = api.api_revision_history_through_origin(
origin_id,
branch_name,
ts,
sha1_git)
env['revision'] = utils.prepare_data_for_view(revision)
except (ValueError, BadInputExc, NotFoundExc) as e:
env['message'] = str(e)
return render_template('revision.html', **env)
@app.route('/browse/revision'
'/origin/'
'/directory/')
@app.route('/browse/revision'
'/origin/'
'/directory/')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/directory/')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/directory//')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/ts/'
'/directory/')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/ts/'
'/directory//')
@set_renderers(HTMLRenderer)
def browse_revision_directory_through_origin(origin_id,
branch_name='refs/heads/master',
ts=None,
path=None):
env = {'message': None,
'origin_id': origin_id,
'ts': ts,
'path': '.' if not path else path,
'result': None}
+
+ encoding = request.args.get('encoding', 'utf8')
+ if encoding not in aliases:
+ env['message'] = 'Encoding %s not supported.' \
+ 'Supported Encodings: %s' % (
+ encoding, list(aliases.keys()))
+ return render_template('revision-directory.html', **env)
+
try:
result = api.api_directory_through_revision_origin(
origin_id,
branch_name,
ts,
path,
with_data=True)
- result['content'] = utils.prepare_data_for_view(result['content'])
+ result['content'] = utils.prepare_data_for_view(result['content'],
+ encoding=encoding)
env['revision'] = result['revision']
env['result'] = result
except (ValueError, BadInputExc, NotFoundExc) as e:
env['message'] = str(e)
return render_template('revision-directory.html', **env)
@app.route('/browse/entity/')
@app.route('/browse/entity//')
@set_renderers(HTMLRenderer)
def browse_entity(uuid='5f4d4c51-498a-4e28-88b3-b3e4e8396cba'):
env = {'entities': [],
'message': None}
try:
entities = api.api_entity_by_uuid(uuid)
env['entities'] = entities
except (NotFoundExc, BadInputExc) as e:
env['message'] = str(e)
return render_template('entity.html', **env)