diff --git a/pagure/internal/__init__.py b/pagure/internal/__init__.py index 2df97c2..ee156b2 100644 --- a/pagure/internal/__init__.py +++ b/pagure/internal/__init__.py @@ -27,6 +27,7 @@ import pagure.exceptions # noqa: E402 import pagure.forms # noqa: E402 import pagure.lib # noqa: E402 import pagure.lib.git # noqa: E402 +import pagure.lib.tasks # noqa: E402 import pagure.ui.fork # noqa: E402 @@ -570,3 +571,59 @@ def get_branches_head(): 'heads': heads, } ) + + +@PV.route('/task/', methods=['GET']) +def task_info(taskid): + """ Return the results of the specified task or a 418 if the task is + still being processed. + """ + task = pagure.lib.tasks.get_result(taskid) + + if task.ready(): + result = task.get(timeout=0, propagate=False) + return flask.jsonify({'results': result}) + else: + flask.abort(418) + + +@PV.route('/stats/commits/authors', methods=['POST']) +def get_stats_commits(): + """ Return statistics about the commits made on the specified repo. + + """ + form = pagure.forms.ConfirmationForm() + if not form.validate_on_submit(): + response = flask.jsonify({ + 'code': 'ERROR', + 'message': 'Invalid input submitted', + }) + response.status_code = 400 + return response + + repo = pagure.get_authorized_project( + pagure.SESSION, + flask.request.form.get('repo', '').strip() or None, + namespace=flask.request.form.get('namespace', '').strip() or None, + user=flask.request.form.get('repouser', '').strip() or None) + + if not repo: + response = flask.jsonify({ + 'code': 'ERROR', + 'message': 'No repo found with the information provided', + }) + response.status_code = 404 + return response + + repopath = os.path.join(pagure.APP.config['GIT_FOLDER'], repo.path) + + task = pagure.lib.tasks.commits_author_stats.delay(repopath) + + return flask.jsonify( + { + 'code': 'OK', + 'message': 'Stats asked', + 'url': flask.url_for('internal_ns.task_info', taskid=task.id), + 'task_id': task.id, + } + ) diff --git a/pagure/lib/tasks.py b/pagure/lib/tasks.py index 5fda99d..c70e0f5 100644 --- a/pagure/lib/tasks.py +++ b/pagure/lib/tasks.py @@ -8,6 +8,7 @@ """ +import collections import gc import hashlib import os @@ -722,3 +723,31 @@ def update_checksums_file(folder, filenames): for algo in algos: stream.write('%s (%s) = %s\n' % ( algo.upper(), filename, algos[algo].hexdigest())) + + +@conn.task +def commits_author_stats(repopath): + """ Returns some statistics about commits made against the specified + git repository. + """ + if not os.path.exists(repopath): + raise ValueError('Git repository not found.') + + repo_obj = pygit2.Repository(repopath) + + stats = collections.defaultdict(int) + cnt = 0 + authors_email = set() + for commit in repo_obj.walk( + repo_obj.head.get_object().oid.hex, pygit2.GIT_SORT_TIME): + cnt += 1 + email = commit.author.email + author = commit.author.name + stats[(author, email)] += 1 + authors_email.add(email) + + out_stats = collections.defaultdict(list) + for authors, val in stats.items(): + out_stats[val].append(authors) + + return (cnt, out_stats, len(authors_email), commit.commit_time) diff --git a/pagure/static/issues_stats.js b/pagure/static/issues_stats.js index 4724c64..847913e 100644 --- a/pagure/static/issues_stats.js +++ b/pagure/static/issues_stats.js @@ -58,3 +58,45 @@ issues_history_stats_plot = function(url, _b, _s) { }); }; + +wait_for_task = function(url, callback){ + $.get(url) + .done(function(data){ + callback(data); + }) + .fail(function(){ + window.setTimeout(wait_for_task(url, callback), 1000); + }) +} + +show_commits_authors = function(data) { + var _b = $("#data_stats"); + var _s = $("#data_stats_spinner"); + var html = '

Since ' + data.results[3] + ' there has been ' + + data.results[0] + ' commits found in this repo, from ' + + data.results[2] + ' contributors

\n' + + '
\n'; + for (key in data.results[1]){ + for (key2 in data.results[1][key]){ + entry = data.results[1][key][key2] + html += ' ' + + entry[0] + + '
' + key + ' commits
' + + '
\n'; + } + } + html += '
'; + _b.html(html); + _b.show(); + _s.hide(); +} + +commits_authors = function(url, _data) { + $.post( url, _data ) + .done(function(data) { + wait_for_task(data.url, show_commits_authors); + }) + .fail(function(data) { + }) +}; diff --git a/pagure/templates/repo_stats.html b/pagure/templates/repo_stats.html new file mode 100644 index 0000000..f22e2ca --- /dev/null +++ b/pagure/templates/repo_stats.html @@ -0,0 +1,106 @@ +{% extends "repo_master.html" %} + +{% block title %}{{ select.capitalize() }} - {{ + g.repo.namespace + '/' if g.repo.namespace }}{{ g.repo.name }}{% endblock %} +{% set tag = "home" %} + + +{% block repo %} + +
+ +
+
+ + +
+
+
+{% endblock %} + +{% block jscripts %} +{{ super() }} + + + + +{% endblock %} diff --git a/pagure/ui/repo.py b/pagure/ui/repo.py index ae6ab16..9b4b514 100644 --- a/pagure/ui/repo.py +++ b/pagure/ui/repo.py @@ -2806,3 +2806,23 @@ def project_dowait(repo, username=None, namespace=None): name=repo, namespace=namespace, user=username).id return pagure.wait_for_task(taskid) + + +@APP.route('//stats/') +@APP.route('//stats') +@APP.route('///stats/') +@APP.route('///stats') +@APP.route('/fork///stats/') +@APP.route('/fork///stats') +@APP.route('/fork////stats/') +@APP.route('/fork////stats') +def view_stats(repo, username=None, namespace=None): + """ Displays some statistics about the specified repo. + """ + return flask.render_template( + 'repo_stats.html', + select='stats', + username=username, + repo=flask.g.repo, + form=pagure.forms.ConfirmationForm(), + )