You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by st...@apache.org on 2013/02/27 23:53:38 UTC
git commit: [#5453] Moving userstats into a tool
Updated Branches:
refs/heads/si/5453 2bb9cd618 -> 5fdd681b0
[#5453] Moving userstats into a tool
Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/5fdd681b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/5fdd681b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/5fdd681b
Branch: refs/heads/si/5453
Commit: 5fdd681b0748f7ddab9a7ae7f21e6d5e6f235d5f
Parents: 2bb9cd6
Author: Stefano Invernizzi <st...@apache.org>
Authored: Wed Feb 27 23:47:24 2013 +0100
Committer: Stefano Invernizzi <st...@apache.org>
Committed: Wed Feb 27 23:47:24 2013 +0100
----------------------------------------------------------------------
Allura/allura/controllers/auth.py | 10 -
Allura/allura/controllers/root.py | 4 +-
.../ext/user_profile/templates/user_index.html | 4 +-
Allura/allura/ext/user_profile/user_main.py | 9 +-
Allura/allura/lib/app_globals.py | 2 +-
Allura/allura/lib/plugin.py | 8 -
Allura/allura/lib/widgets/forms.py | 15 -
Allura/allura/model/project.py | 2 +
Allura/allura/nf/allura/css/allura.css | 13 +
Allura/allura/templates/user_preferences.html | 9 -
.../forgeuserstats/controllers/userstats.py | 235 +++++++++------
ForgeUserStats/forgeuserstats/main.py | 90 +++++-
.../forgeuserstats/templates/artifacts.html | 2 +-
.../forgeuserstats/templates/commits.html | 2 +-
ForgeUserStats/forgeuserstats/templates/index.html | 32 +-
.../forgeuserstats/templates/settings.html | 19 ++
.../forgeuserstats/templates/tickets.html | 2 +-
ForgeUserStats/forgeuserstats/widgets/forms.py | 22 ++
ForgeUserStats/setup.py | 6 +-
19 files changed, 311 insertions(+), 175 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5fdd681b/Allura/allura/controllers/auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/auth.py b/Allura/allura/controllers/auth.py
index e7031d0..8b0be12 100644
--- a/Allura/allura/controllers/auth.py
+++ b/Allura/allura/controllers/auth.py
@@ -58,7 +58,6 @@ class F(object):
remove_inactive_period_form = forms.RemoveInactivePeriodForm()
save_skill_form = forms.AddUserSkillForm()
remove_skill_form = forms.RemoveSkillForm()
- set_statistics = forms.StatsPreferencesForm()
class AuthController(BaseController):
@@ -658,15 +657,6 @@ class PreferencesController(BaseController):
@expose()
@require_post()
- @validate(F.set_statistics, error_handler=index)
- def set_statistics(self, **kw):
- require_authenticated()
- c.user.stats.visible = kw.get('visible', True)
- flash('Your preferences about statistics were successfully updated!')
- redirect('.#Statistics')
-
- @expose()
- @require_post()
def upload_sshkey(self, key=None):
ap = plugin.AuthenticationProvider.get(request)
try:
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5fdd681b/Allura/allura/controllers/root.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/root.py b/Allura/allura/controllers/root.py
index a387b98..83aa5e5 100644
--- a/Allura/allura/controllers/root.py
+++ b/Allura/allura/controllers/root.py
@@ -69,9 +69,7 @@ class RootController(WsgiDispatchController):
if n and not n.url_prefix.startswith('//'):
n.bind_controller(self)
self.browse = ProjectBrowseController()
- ep = g.entry_points["stats"].get('userstats')
- if ep and g.show_userstats:
- self.userstats = ep().root
+
super(RootController, self).__init__()
def _setup_request(self):
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5fdd681b/Allura/allura/ext/user_profile/templates/user_index.html
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/user_profile/templates/user_index.html b/Allura/allura/ext/user_profile/templates/user_index.html
index 7371eb6..2b17b79 100644
--- a/Allura/allura/ext/user_profile/templates/user_index.html
+++ b/Allura/allura/ext/user_profile/templates/user_index.html
@@ -236,11 +236,11 @@
</div>
</div>
- {% if statslinkurl %}
+ {% if user.stats.visible %}
<div class="grid-24">
<div class="grid-24" style="margin:0;"><b>User statistics</b></div>
<div class="grid-24" style="margin-top:5px;margin-bottom:5px;">
- <div><a href="{{statslinkurl}}"/>{{statslinkdescription}}</a></div>
+ <div><a href="{{c.project.url()}}userstats"/>Go to the personal statistics of this user</a></div>
</div>
</div>
{% endif %}
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5fdd681b/Allura/allura/ext/user_profile/user_main.py
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/user_profile/user_main.py b/Allura/allura/ext/user_profile/user_main.py
index f3c3331..7754228 100644
--- a/Allura/allura/ext/user_profile/user_main.py
+++ b/Allura/allura/ext/user_profile/user_main.py
@@ -64,14 +64,7 @@ class UserProfileController(BaseController):
user = c.project.user_project_of
if not user:
raise exc.HTTPNotFound()
- if g.show_userstats and user.stats.visible:
- from forgeuserstats.main import ForgeUserStatsApp
- link, description = ForgeUserStatsApp.createlink(user)
- else:
- link, description = None, None
- return dict(user=user,
- statslinkurl = link,
- statslinkdescription = description)
+ return dict(user=user)
# This will be fully implemented in a future iteration
# @expose('jinja:allura.ext.user_profile:templates/user_subscriptions.html')
# def subscriptions(self):
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5fdd681b/Allura/allura/lib/app_globals.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/app_globals.py b/Allura/allura/lib/app_globals.py
index 632a29f..ee2c9b9 100644
--- a/Allura/allura/lib/app_globals.py
+++ b/Allura/allura/lib/app_globals.py
@@ -177,7 +177,7 @@ class Globals(object):
statslisteners = []
for name, ep in self.entry_points['stats'].iteritems():
if config.get('%s.enable' % name,'false')=='true':
- statslisteners.append(ep().listener)
+ statslisteners.append(ep())
self.statsUpdater = PostEvent(statslisteners)
@LazyProperty
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5fdd681b/Allura/allura/lib/plugin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/plugin.py b/Allura/allura/lib/plugin.py
index 4ec29de..dcbc36e 100644
--- a/Allura/allura/lib/plugin.py
+++ b/Allura/allura/lib/plugin.py
@@ -725,14 +725,6 @@ class ThemeProvider(object):
return RemoveInactivePeriodForm()
@LazyProperty
- def statistics_form(self):
- '''
- :return: None, or an easywidgets Form to render on the user preferences page
- '''
- from allura.lib.widgets.forms import StatsPreferencesForm
- return StatsPreferencesForm(action='/auth/prefs/set_statistics')
-
- @LazyProperty
def add_trove_category(self):
'''
:return: None, or an easywidgets Form to render on the page to create a
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5fdd681b/Allura/allura/lib/widgets/forms.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/widgets/forms.py b/Allura/allura/lib/widgets/forms.py
index c1a1d75..96f832d 100644
--- a/Allura/allura/lib/widgets/forms.py
+++ b/Allura/allura/lib/widgets/forms.py
@@ -452,21 +452,6 @@ class RemoveTimeSlotForm(ForgeForm):
return d
-class StatsPreferencesForm(ForgeForm):
- defaults=dict(ForgeForm.defaults)
-
- class fields(ew_core.NameList):
- visible = ew.Checkbox(
- label='Make my personal statistics visible to other users.')
-
- def display(self, **kw):
- if kw.get('user').stats.visible:
- self.fields['visible'].attrs = {'checked':'true'}
- else:
- self.fields['visible'].attrs = {}
- return super(ForgeForm, self).display(**kw)
-
-
class RemoveTroveCategoryForm(ForgeForm):
defaults=dict(ForgeForm.defaults, submit_text=None, show_errors=False)
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5fdd681b/Allura/allura/model/project.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/project.py b/Allura/allura/model/project.py
index 93f8257..da821ad 100644
--- a/Allura/allura/model/project.py
+++ b/Allura/allura/model/project.py
@@ -714,6 +714,8 @@ class Project(MappedClass, ActivityNode, ActivityObject):
('admin', 'admin', 'Admin'),
('search', 'search', 'Search'),
('activity', 'activity', 'Activity')]
+ if g.show_userstats:
+ apps = apps + [('userstats', 'userstats', 'Statistics')]
else:
apps = [('admin', 'admin', 'Admin'),
('search', 'search', 'Search'),
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5fdd681b/Allura/allura/nf/allura/css/allura.css
----------------------------------------------------------------------
diff --git a/Allura/allura/nf/allura/css/allura.css b/Allura/allura/nf/allura/css/allura.css
index 1f32750..f3863b8 100644
--- a/Allura/allura/nf/allura/css/allura.css
+++ b/Allura/allura/nf/allura/css/allura.css
@@ -60,6 +60,11 @@ b.ico.ico-vote-down { background-image: url('../images/vote_down.png'); }
background-repeat: no-repeat;
}
+.ui-icon-tool-userstats {
+ background-image: url("../images/stats_24.png");
+ background-repeat: no-repeat;
+}
+
.ui-icon-tool-admin, .ui-icon-admin {
background-image: url("../images/admin_24.png");
background-repeat: no-repeat;
@@ -118,6 +123,10 @@ b.ico.ico-vote-down { background-image: url('../images/vote_down.png'); }
#top_nav .ui-icon-tool-stats {
background-image: url("../images/stats_32.png");
}
+#top_nav .ui-icon-tool-userstats {
+ background-image: url("../images/stats_32.png");
+}
+
#top_nav .ui-icon-tool-admin, #top_nav .ui-icon-admin {
background-image: url("../images/admin_32.png");
}
@@ -152,6 +161,10 @@ b.ico.ico-vote-down { background-image: url('../images/vote_down.png'); }
.big_icon.ui-icon-tool-stats {
background-image: url("../images/stats_48.png");
}
+.big_icon.ui-icon-tool-userstats {
+ background-image: url("../images/stats_48.png");
+}
+
.big_icon.ui-icon-tool-admin, .big_icon.ui-icon-admin {
background-image: url("../images/admin_48.png");
}
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5fdd681b/Allura/allura/templates/user_preferences.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/user_preferences.html b/Allura/allura/templates/user_preferences.html
index 8d4a118..be4dfe6 100644
--- a/Allura/allura/templates/user_preferences.html
+++ b/Allura/allura/templates/user_preferences.html
@@ -129,15 +129,6 @@
<ul><li><a href="/auth/prefs/user_skills">Click here to check and change your skills list</a></li></ul>
</div>
- {% if g.show_userstats %}
- <a name="Statistics"></a>
- <div class="grid-20">
- <h2>Contribution statistics</h2>
- <ul><li><a href="/userstats/{{c.user.username}}">Click here to check your personal statistics</a></li></ul>
- {{g.theme.statistics_form.display(user=c.user)}}
- </div>
- {% endif %}
-
{% if g.theme.password_change_form %}
<div class="grid-20">
<h2>Change Password</h2>
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5fdd681b/ForgeUserStats/forgeuserstats/controllers/userstats.py
----------------------------------------------------------------------
diff --git a/ForgeUserStats/forgeuserstats/controllers/userstats.py b/ForgeUserStats/forgeuserstats/controllers/userstats.py
index 40ad92c..b93e77b 100644
--- a/ForgeUserStats/forgeuserstats/controllers/userstats.py
+++ b/ForgeUserStats/forgeuserstats/controllers/userstats.py
@@ -1,4 +1,4 @@
-from tg import expose
+from tg import expose, validate, redirect
from tg.decorators import with_trailing_slash
from datetime import datetime
from allura.controllers import BaseController
@@ -6,33 +6,86 @@ import allura.model as M
from allura.lib.graphics.graphic_methods import create_histogram, create_progress_bar
from forgeuserstats.model.stats import UserStats
from pylons import tmpl_context as c
+from allura.lib.security import require_access
+from forgeuserstats.widgets.forms import StatsPreferencesForm
+from allura.lib.decorators import require_post
+from allura.lib import validators as V
-class ForgeUserStatsController(BaseController):
+stats_preferences_form = StatsPreferencesForm()
+
+class ForgeUserStatsCatController(BaseController):
@expose()
- def _lookup(self, part, *remainder):
- user = M.User.query.get(username=part)
+ def _lookup(self, category, *remainder):
+ cat = M.TroveCategory.query.get(shortname=category)
+ return ForgeUserStatsCatController(category = cat), remainder
- if not self.user:
- return ForgeUserStatsController(user=user), remainder
- if part == "category":
- return ForgeUserStatsCatController(self.user, None), remainder
- if part == "metric":
- return ForgeUserStatsMetricController(self.user), remainder
+ def __init__(self, category = None):
+ self.category = category
+ super(ForgeUserStatsCatController, self).__init__()
- def __init__(self, user=None):
- self.user = user
- if self.user:
- if not user.stats:
- UserStats.create(self.user)
+ @expose('jinja:forgeuserstats:templates/index.html')
+ @with_trailing_slash
+ def index(self, **kw):
+ self.user = c.project.user_project_of
+ if not self.user:
+ return None
+ stats = self.user.stats
+ if (not stats.visible) and (c.user != self.user):
+ return dict(user=self.user)
+
+ cat_id = None
+ if self.category:
+ cat_id = self.category._id
+ ret_dict = _getDataForCategory(cat_id, stats)
+ ret_dict['user'] = self.user
+ ret_dict['registration_date'] = stats.registration_date
+ ret_dict['category'] = self.category
+ return ret_dict
- super(ForgeUserStatsController, self).__init__()
+class ForgeUserStatsController(BaseController):
+
+ category = ForgeUserStatsCatController()
+
+ @expose('jinja:forgeuserstats:templates/settings.html')
+ @with_trailing_slash
+ def settings(self, **kw):
+ require_access(c.project, 'admin')
+
+ self.user = c.project.user_project_of
+ if not self.user:
+ return dict(user=None)
+ if not self.user.stats:
+ UserStats.create(self.user)
+ return dict(
+ user = self.user,
+ form = StatsPreferencesForm(
+ action = c.project.url() + 'userstats/change_settings'))
+
+ @expose()
+ @require_post()
+ @validate(stats_preferences_form, error_handler=settings)
+ def change_settings(self, **kw):
+ require_access(c.project, 'admin')
+
+ self.user = c.project.user_project_of
+ if not self.user:
+ return dict(user=None)
+ if not self.user.stats:
+ UserStats.create(self.user)
+ visible = kw.get('visible')
+ self.user.stats.visible = visible
+ redirect(c.project.url() + 'userstats/settings')
@expose('jinja:forgeuserstats:templates/index.html')
@with_trailing_slash
def index(self, **kw):
+ self.user = c.project.user_project_of
if not self.user:
return dict(user=None)
+ if not self.user.stats:
+ UserStats.create(self.user)
+
stats = self.user.stats
if (not stats.visible) and (c.user != self.user):
return dict(user=self.user)
@@ -79,11 +132,71 @@ class ForgeUserStatsController(BaseController):
stats.getMaxAndAverageDiscussionContribution()
ret_dict['maxticketcontrib'], ret_dict['averageticketcontrib'] =\
stats.getMaxAndAverageTicketsSolvingPercentage()
-
+
return ret_dict
+
+ @expose('jinja:forgeuserstats:templates/commits.html')
+ @with_trailing_slash
+ def commits(self, **kw):
+ self.user = c.project.user_project_of
+ if not self.user:
+ return dict(user=None)
+ if not self.user.stats:
+ UserStats.create(self.user)
+ stats = self.user.stats
+
+ if (not stats.visible) and (c.user != self.user):
+ return dict(user=self.user)
+
+ commits = stats.getCommitsByCategory()
+ return dict(
+ user = self.user,
+ data = commits)
+
+ @expose('jinja:forgeuserstats:templates/artifacts.html')
+ @with_trailing_slash
+ def artifacts(self, **kw):
+ self.user = c.project.user_project_of
+ if not self.user:
+ return dict(user=None)
+ if not self.user.stats:
+ UserStats.create(self.user)
+ stats = self.user.stats
+
+ if (not stats.visible) and (c.user != self.user):
+ return dict(user=self.user)
+
+ stats = self.user.stats
+ artifacts = stats.getArtifactsByCategory(detailed=True)
+ return dict(
+ user = self.user,
+ data = artifacts)
+
+ @expose('jinja:forgeuserstats:templates/tickets.html')
+ @with_trailing_slash
+ def tickets(self, **kw):
+ self.user = c.project.user_project_of
+ if not self.user:
+ return dict(user=None)
+ if not self.user.stats:
+ UserStats.create(self.user)
+ stats = self.user.stats
+
+ if (not stats.visible) and (c.user != self.user):
+ return dict(user=self.user)
+
+ artifacts = self.user.stats.getTicketsByCategory()
+ return dict(
+ user=self.user,
+ data=artifacts)
+
@expose()
def categories_graph(self):
+ self.user = c.project.user_project_of
+ if not self.user:
+ return None
+
categories = {}
for p in self.user.my_projects():
for cat in p.trove_topic:
@@ -109,89 +222,27 @@ class ForgeUserStatsController(BaseController):
@expose()
def code_ranking_bar(self):
- return create_progress_bar(self.user.stats.codeRanking())
+ self.user = c.project.user_project_of
+ if not self.user:
+ return None
+ stats = self.user.stats
+ return create_progress_bar(stats.codeRanking())
@expose()
def discussion_ranking_bar(self):
- return create_progress_bar(self.user.stats.discussionRanking())
+ self.user = c.project.user_project_of
+ if not self.user:
+ return None
+ stats = self.user.stats
+ return create_progress_bar(stats.discussionRanking())
@expose()
def tickets_ranking_bar(self):
- return create_progress_bar(self.user.stats.ticketsRanking())
-
-class ForgeUserStatsCatController(BaseController):
- @expose()
- def _lookup(self, category, *remainder):
- cat = M.TroveCategory.query.get(shortname=category)
- return ForgeUserStatsCatController(self.user, cat), remainder
-
- def __init__(self, user, category):
- self.user = user
- self.category = category
- super(ForgeUserStatsCatController, self).__init__()
-
- @expose('jinja:forgeuserstats:templates/index.html')
- @with_trailing_slash
- def index(self, **kw):
- if not self.user:
- return dict(user=None)
- stats = self.user.stats
- if (not stats.visible) and (c.user != self.user):
- return dict(user=self.user)
-
- cat_id = None
- if self.category:
- cat_id = self.category._id
- ret_dict = _getDataForCategory(cat_id, stats)
- ret_dict['user'] = self.user
- ret_dict['registration_date'] = stats.registration_date
- ret_dict['category'] = self.category
-
- return ret_dict
-
-class ForgeUserStatsMetricController(BaseController):
-
- def __init__(self, user):
- self.user = user
- super(ForgeUserStatsMetricController, self).__init__()
-
- @expose('jinja:forgeuserstats:templates/commits.html')
- @with_trailing_slash
- def commits(self, **kw):
- if not self.user:
- return dict(user=None)
- stats = self.user.stats
- if (not stats.visible) and (c.user != self.user):
- return dict(user=self.user)
-
- commits = stats.getCommitsByCategory()
- return dict(user = self.user,
- data = commits)
-
- @expose('jinja:forgeuserstats:templates/artifacts.html')
- @with_trailing_slash
- def artifacts(self, **kw):
- if not self.user:
- return dict(user=None)
- stats = self.user.stats
- if (not stats.visible) and (c.user != self.user):
- return dict(user=self.user)
-
- stats = self.user.stats
- artifacts = stats.getArtifactsByCategory(detailed=True)
- return dict(user = self.user, data = artifacts)
-
- @expose('jinja:forgeuserstats:templates/tickets.html')
- @with_trailing_slash
- def tickets(self, **kw):
+ self.user = c.project.user_project_of
if not self.user:
- return dict(user=None)
+ return None
stats = self.user.stats
- if (not stats.visible) and (c.user != self.user):
- return dict(user=self.user)
-
- artifacts = self.user.stats.getTicketsByCategory()
- return dict(user = self.user, data = artifacts)
+ return create_progress_bar(stats.ticketsRanking())
def _getDataForCategory(category, stats):
totcommits = stats.getCommits(category)
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5fdd681b/ForgeUserStats/forgeuserstats/main.py
----------------------------------------------------------------------
diff --git a/ForgeUserStats/forgeuserstats/main.py b/ForgeUserStats/forgeuserstats/main.py
index 8eeb113..dcecdb9 100644
--- a/ForgeUserStats/forgeuserstats/main.py
+++ b/ForgeUserStats/forgeuserstats/main.py
@@ -1,10 +1,24 @@
+#-*- python -*-
import logging
+from pylons import tmpl_context as c
+import formencode
+from formencode import validators
+from webob import exc
from datetime import datetime
+from allura.app import Application, SitemapEntry
+from allura.lib import helpers as h
+from allura.lib.security import has_access
+from allura import model as M
from allura.eventslistener import EventsListener
from model.stats import UserStats
from controllers.userstats import ForgeUserStatsController
+from forgeuserstats import version
+from forgeuserstats.controllers.userstats import ForgeUserStatsController
+
+from ming.orm import session
+
log = logging.getLogger(__name__)
class UserStatsListener(EventsListener):
@@ -55,12 +69,74 @@ class UserStatsListener(EventsListener):
def newOrganization(self, organization):
pass
-class ForgeUserStatsApp:
+class ForgeUserStatsApp(Application):
+ __version__ = version.__version__
+ tool_label='Statistics'
+ default_mount_label='Statistics'
+ default_mount_point='stats'
+ permissions = ['configure', 'read', 'write',
+ 'unmoderated_post', 'post', 'moderate', 'admin']
+ ordinal=15
+ installable=False
+ config_options = Application.config_options
+ default_external_feeds = []
+ icons={
+ 24:'images/stats_24.png',
+ 32:'images/stats_32.png',
+ 48:'images/stats_48.png'
+ }
root = ForgeUserStatsController()
- listener = UserStatsListener()
- @classmethod
- def createlink(cls, user):
- return (
- "/userstats/%s/" % user.username,
- "%s personal statistcs" % user.display_name)
+ def __init__(self, project, config):
+ Application.__init__(self, project, config)
+ role_admin = M.ProjectRole.by_name('Admin')._id
+ role_anon = M.ProjectRole.by_name('*anonymous')._id
+ self.config.acl = [
+ M.ACE.allow(role_anon, 'read'),
+ M.ACE.allow(role_admin, 'admin')]
+
+ def main_menu(self):
+ return [SitemapEntry(self.config.options.mount_label.title(), '.')]
+
+ @property
+ @h.exceptionless([], log)
+ def sitemap(self):
+ menu_id = self.config.options.mount_label.title()
+ with h.push_config(c, app=self):
+ return [
+ SitemapEntry(menu_id, '.')[self.sidebar_menu()] ]
+
+ @property
+ def show_discussion(self):
+ if 'show_discussion' in self.config.options:
+ return self.config.options['show_discussion']
+ else:
+ return True
+
+ @h.exceptionless([], log)
+ def sidebar_menu(self):
+ base = c.app.url
+ links = [SitemapEntry('Overview', base),
+ SitemapEntry('Commits', base + 'commits'),
+ SitemapEntry('Artifacts', base + 'artifacts'),
+ SitemapEntry('Tickets', base + 'tickets')]
+ return links
+
+ def admin_menu(self):
+ links = [SitemapEntry(
+ 'Settings', c.project.url() + 'userstats/settings')]
+ return links
+
+ def install(self, project):
+ #It doesn't make any sense to install the tool twice on the same
+ #project therefore, if it already exists, it doesn't install it
+ #a second time.
+ for tool in project.app_configs:
+ if tool.tool_name == 'userstats':
+ if self.config.options.mount_point!=tool.options.mount_point:
+ project.uninstall_app(self.config.options.mount_point)
+ return
+
+ def uninstall(self, project):
+ self.config.delete()
+ session(self.config).flush()
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5fdd681b/ForgeUserStats/forgeuserstats/templates/artifacts.html
----------------------------------------------------------------------
diff --git a/ForgeUserStats/forgeuserstats/templates/artifacts.html b/ForgeUserStats/forgeuserstats/templates/artifacts.html
index 013c108..ede6dde 100644
--- a/ForgeUserStats/forgeuserstats/templates/artifacts.html
+++ b/ForgeUserStats/forgeuserstats/templates/artifacts.html
@@ -11,7 +11,7 @@
{% if user and (user.stats.visible or (c.user == user)) %}
<div class="grid-20">
- <ul><li><a href="/userstats/{{user.username}}">Go back to general statistics</a></li></ul>
+ <ul><li><a href="{{c.project.url()}}userstats">Go back to general statistics</a></li></ul>
</div>
{% if data %}
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5fdd681b/ForgeUserStats/forgeuserstats/templates/commits.html
----------------------------------------------------------------------
diff --git a/ForgeUserStats/forgeuserstats/templates/commits.html b/ForgeUserStats/forgeuserstats/templates/commits.html
index 10d1c67..f0aca6f 100644
--- a/ForgeUserStats/forgeuserstats/templates/commits.html
+++ b/ForgeUserStats/forgeuserstats/templates/commits.html
@@ -11,7 +11,7 @@
{% if user and (user.stats.visible or (c.user == user)) %}
<div class="grid-20">
- <ul><li><a href="/userstats/{{user.username}}">Go back to general statistics</a></li></ul>
+ <ul><li><a href="{{c.project.url()}}userstats">Go back to general statistics</a></li></ul>
</div>
{% if data %}
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5fdd681b/ForgeUserStats/forgeuserstats/templates/index.html
----------------------------------------------------------------------
diff --git a/ForgeUserStats/forgeuserstats/templates/index.html b/ForgeUserStats/forgeuserstats/templates/index.html
index 653cd31..ae58cf8 100644
--- a/ForgeUserStats/forgeuserstats/templates/index.html
+++ b/ForgeUserStats/forgeuserstats/templates/index.html
@@ -15,7 +15,7 @@
{% if category %}
<ul>
- <li><a href="/userstats/{{user.username}}">Go back to general statistics</a></li>
+ <li><a href="{{c.project.url()}}userstats">Go back to general statistics</a></li>
</ul>
{% endif %}
<h2>General statistics</h2>
@@ -80,7 +80,7 @@
<tr>
<td>
{% if totcommits.number > 0 %}
- <a href="/userstats/{{user.username}}/metric/commits/">Commits number</a>
+ <a href="{{c.project.url()}}userstats/commits/">Commits number</a>
{% else %}
Commits number
{% endif %}
@@ -103,7 +103,7 @@
<tr>
<td>
{% if totcommits.lines > 0 %}
- <a href="/userstats/{{user.username}}/metric/commits/">Added/modified LOCs</a>
+ <a href="{{c.project.url()}}userstats/commits/">Added/modified LOCs</a>
{% else %}
Added/modified LOCs
{% endif %}
@@ -126,7 +126,7 @@
<tr>
<td>
{% if totartifacts.created > 0 %}
- <a href="/userstats/{{user.username}}/metric/artifacts/">Total number of created artifacts</a>
+ <a href="{{c.project.url()}}userstats/artifacts/">Total number of created artifacts</a>
{% else %}
Total number of created artifacts
{% endif %}
@@ -149,7 +149,7 @@
<tr>
<td>
{% if totartifacts.modified > 0 %}
- <a href="/userstats/{{user.username}}/metric/artifacts/">Total number of edited artifacts</a>
+ <a href="{{c.project.url()}}userstats/artifacts/">Total number of edited artifacts</a>
{% else %}
Total number of edited artifacts
{% endif %}
@@ -174,7 +174,7 @@
<tr>
<td>
{% if value.created > 0 %}
- <a href="/userstats/{{user.username}}/metric/artifacts/">Created {{key}} artifacts</a>
+ <a href="{{c.project.url()}}userstats/artifacts/">Created {{key}} artifacts</a>
{% else %}
Created {{key}} artifacts
{% endif %}
@@ -205,7 +205,7 @@
<tr>
<td>
{% if value.modified > 0 %}
- <a href="/userstats/{{user.username}}/metric/artifacts/">Edited {{key}} artifacts</a>
+ <a href="{{c.project.url()}}userstats/artifacts/">Edited {{key}} artifacts</a>
{% else %}
Edited {{key}} artifacts
{% endif %}
@@ -238,7 +238,7 @@
<tr>
<td>
{% if tottickets.assigned > 0 %}
- <a href="/userstats/{{user.username}}/metric/tickets/">Assigned tickets</a>
+ <a href="{{c.project.url()}}userstats/tickets/">Assigned tickets</a>
{% else %}
Assigned tickets
{% endif %}
@@ -261,7 +261,7 @@
<tr>
<td>
{% if tottickets.revoked > 0 %}
- <a href="/userstats/{{user.username}}/metric/tickets/">Revoked tickets</a>
+ <a href="{{c.project.url()}}userstats/tickets/">Revoked tickets</a>
{% else %}
Revoked tickets
{% endif %}
@@ -284,7 +284,7 @@
<tr>
<td>
{% if tottickets.solved > 0 %}
- <a href="/userstats/{{user.username}}/metric/tickets/">Solved tickets</a>
+ <a href="{{c.project.url()}}userstats/tickets/">Solved tickets</a>
{% else %}
Solved tickets
{% endif %}
@@ -307,7 +307,7 @@
<tr>
<td>
{% if tottickets.averagesolvingtime > 0 %}
- <a href="/userstats/{{user.username}}/metric/tickets/">Average tickets solving time</a>
+ <a href="{{c.project.url()}}userstats/tickets/">Average tickets solving time</a>
{% else %}
Average tickets solving time
{% endif %}
@@ -357,7 +357,7 @@
<tbody>
{% for cat, count in categories %}
<tr>
- <td><a href="/userstats/{{user.username}}/category/{{cat.shortname}}">{{cat.fullname}}</a></td>
+ <td><a href="{{c.project.url()}}userstats/category/{{cat.shortname}}">{{cat.fullname}}</a></td>
<td>{{count}}</td>
</tr>
{% endfor %}
@@ -369,7 +369,7 @@
The same data listed in the previous table is graphically presented by the following histogram.
</p>
<p>
- <img src="/userstats/{{user.username}}/categories_graph"/>
+ <img src="{{c.project.url()}}userstats/categories_graph"/>
</p>
{% endif %}
{% endif %}
@@ -391,21 +391,21 @@
<td>{{codecontribution}} LOC{% if codecontribution != 1 %}s{% endif %}/month</td>
<td>{{averagecodecontrib}} LOC{% if averagecodecontrib != 1 %}s{% endif %}/month</td>
<td>{{maxcodecontrib}} LOC{% if maxcodecontrib != 1 %}s{% endif %}/month</td>
- <td><img src="/userstats/{{user.username}}/code_ranking_bar"/> {{codepercentage}} %</td>
+ <td><img src="{{c.project.url()}}userstats/code_ranking_bar"/> {{codepercentage}} %</td>
</tr>
<tr>
<td>Discussion</td>
<td>{{discussioncontribution}} contr./month</td>
<td>{{averagedisccontrib}} contr./month</td>
<td>{{maxdisccontrib}} contr./month</td>
- <td><img src="/userstats/{{user.username}}/discussion_ranking_bar"/> {{discussionpercentage}} %</td>
+ <td><img src="{{c.project.url()}}userstats/discussion_ranking_bar"/> {{discussionpercentage}} %</td>
</tr>
<tr>
<td>Solved issues</td>
<td>{{ticketcontribution}} %</td>
<td>{{averageticketcontrib}} %</td>
<td>{{maxticketcontrib}} %</td>
- <td><img src="/userstats/{{user.username}}/tickets_ranking_bar"/> {{ticketspercentage}} %</td>
+ <td><img src="{{c.project.url()}}userstats/tickets_ranking_bar"/> {{ticketspercentage}} %</td>
</tr>
</tbody>
</table>
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5fdd681b/ForgeUserStats/forgeuserstats/templates/settings.html
----------------------------------------------------------------------
diff --git a/ForgeUserStats/forgeuserstats/templates/settings.html b/ForgeUserStats/forgeuserstats/templates/settings.html
new file mode 100644
index 0000000..c07301a
--- /dev/null
+++ b/ForgeUserStats/forgeuserstats/templates/settings.html
@@ -0,0 +1,19 @@
+{% set hide_left_bar = True %}
+{% extends g.theme.master %}
+
+{% block title %}User stats – Settings{% endblock %}
+
+{% block header %}
+ Statistics about {{user.display_name}}'s contribution – Settings
+{% endblock %}
+
+{% block content %}
+
+ <div class="grid-20">
+ In this page you can set the visibility of your personal statistics. If you decide to hide your personal statistics to
+ other users, data collected about your contributions to projects hosted on this forge will be available only for your
+ personal use.
+ {{form.display(user = user)}}
+ </div>
+
+{% endblock %}
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5fdd681b/ForgeUserStats/forgeuserstats/templates/tickets.html
----------------------------------------------------------------------
diff --git a/ForgeUserStats/forgeuserstats/templates/tickets.html b/ForgeUserStats/forgeuserstats/templates/tickets.html
index a713b25..0252021 100644
--- a/ForgeUserStats/forgeuserstats/templates/tickets.html
+++ b/ForgeUserStats/forgeuserstats/templates/tickets.html
@@ -11,7 +11,7 @@
{% if user and (user.stats.visible or (c.user == user)) %}
<div class="grid-20">
- <ul><li><a href="/userstats/{{user.username}}">Go back to general statistics</a></li></ul>
+ <ul><li><a href="{{c.project.url()}}userstats">Go back to general statistics</a></li></ul>
</div>
{% if data %}
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5fdd681b/ForgeUserStats/forgeuserstats/widgets/__init__.py
----------------------------------------------------------------------
diff --git a/ForgeUserStats/forgeuserstats/widgets/__init__.py b/ForgeUserStats/forgeuserstats/widgets/__init__.py
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5fdd681b/ForgeUserStats/forgeuserstats/widgets/forms.py
----------------------------------------------------------------------
diff --git a/ForgeUserStats/forgeuserstats/widgets/forms.py b/ForgeUserStats/forgeuserstats/widgets/forms.py
new file mode 100644
index 0000000..c4da4fb
--- /dev/null
+++ b/ForgeUserStats/forgeuserstats/widgets/forms.py
@@ -0,0 +1,22 @@
+from allura.lib import validators as V
+from allura.lib.widgets.forms import ForgeForm
+
+from formencode import validators as fev
+
+import ew as ew_core
+import ew.jinja2_ew as ew
+
+class StatsPreferencesForm(ForgeForm):
+ defaults=dict(ForgeForm.defaults)
+
+ class fields(ew_core.NameList):
+ visible = ew.Checkbox(
+ label='Make my personal statistics visible to other users.')
+
+ def display(self, **kw):
+ if kw.get('user').stats.visible:
+ self.fields['visible'].attrs = {'checked':'true'}
+ else:
+ self.fields['visible'].attrs = {}
+ return super(ForgeForm, self).display(**kw)
+
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5fdd681b/ForgeUserStats/setup.py
----------------------------------------------------------------------
diff --git a/ForgeUserStats/setup.py b/ForgeUserStats/setup.py
index dc2f07b..733ebd8 100644
--- a/ForgeUserStats/setup.py
+++ b/ForgeUserStats/setup.py
@@ -23,7 +23,11 @@ setup(name='ForgeUserStats',
],
entry_points="""
# -*- Entry points: -*-
- [allura.stats]
+ [allura]
userstats=forgeuserstats.main:ForgeUserStatsApp
+
+ [allura.stats]
+ userstats=forgeuserstats.main:UserStatsListener
+
""",
)