• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

allura


Commit MetaInfo

Revisión6f8e88b094634f1992a9412c5f56a720425d7104 (tree)
Tiempo2012-04-19 03:33:45
AutorTim Van Steenburgh <tvansteenburgh@geek...>
CommiterTim Van Steenburgh

Log Message

Merge branch 'dev' of git://sfi-engr-scm-1/forge into dev

Cambiar Resumen

Diferencia incremental

--- a/Allura/allura/controllers/project.py
+++ b/Allura/allura/controllers/project.py
@@ -1,10 +1,10 @@
11 import re
22 import logging
3-
4-from bson import ObjectId
3+from datetime import datetime, timedelta
54 from urllib import unquote
65 from itertools import chain, islice
76
7+from bson import ObjectId
88 from tg import expose, flash, redirect, validate, request, response
99 from tg.decorators import with_trailing_slash, without_trailing_slash
1010 from pylons import c, g
@@ -400,17 +400,19 @@ class NeighborhoodAdminController(object):
400400 def __init__(self, neighborhood):
401401 self.neighborhood = neighborhood
402402 self.awards = NeighborhoodAwardsController(self.neighborhood)
403+ self.stats = NeighborhoodStatsController(self.neighborhood)
404+
405+ def _check_security(self):
406+ require_access(self.neighborhood, 'admin')
403407
404408 @with_trailing_slash
405409 @expose()
406410 def index(self, **kw):
407- require_access(self.neighborhood, 'admin')
408411 utils.permanent_redirect('overview')
409412
410413 @without_trailing_slash
411414 @expose('jinja:allura:templates/neighborhood_admin_overview.html')
412415 def overview(self, **kw):
413- require_access(self.neighborhood, 'admin')
414416 set_nav(self.neighborhood)
415417 c.overview_form = W.neighborhood_overview_form
416418 return dict(neighborhood=self.neighborhood)
@@ -418,13 +420,11 @@ class NeighborhoodAdminController(object):
418420 @without_trailing_slash
419421 @expose('jinja:allura:templates/neighborhood_admin_permissions.html')
420422 def permissions(self):
421- require_access(self.neighborhood, 'admin')
422423 set_nav(self.neighborhood)
423424 return dict(neighborhood=self.neighborhood)
424425
425426 @expose('json:')
426427 def project_search(self, term=''):
427- require_access(self.neighborhood, 'admin')
428428 if len(term) < 3:
429429 raise exc.HTTPBadRequest('"term" param must be at least length 3')
430430 project_regex = re.compile('(?i)%s' % re.escape(term))
@@ -442,7 +442,6 @@ class NeighborhoodAdminController(object):
442442 @without_trailing_slash
443443 @expose('jinja:allura:templates/neighborhood_admin_accolades.html')
444444 def accolades(self):
445- require_access(self.neighborhood, 'admin')
446445 set_nav(self.neighborhood)
447446 awards = M.Award.query.find(dict(created_by_neighborhood_id=self.neighborhood._id)).all()
448447 awards_count = len(awards)
@@ -460,7 +459,6 @@ class NeighborhoodAdminController(object):
460459 @require_post()
461460 @validate(W.neighborhood_overview_form, error_handler=overview)
462461 def update(self, name=None, css=None, homepage=None, project_template=None, icon=None, **kw):
463- require_access(self.neighborhood, 'admin')
464462 self.neighborhood.name = name
465463 self.neighborhood.redirect = kw.pop('redirect', '')
466464 self.neighborhood.homepage = homepage
@@ -485,6 +483,77 @@ class NeighborhoodAdminController(object):
485483 neighborhood=self.neighborhood,
486484 )
487485
486+class NeighborhoodStatsController(object):
487+
488+ def __init__(self, neighborhood):
489+ self.neighborhood = neighborhood
490+
491+ @with_trailing_slash
492+ @expose('jinja:allura:templates/neighborhood_stats.html')
493+ def index(self, **kw):
494+ delete_count = M.Project.query.find(dict(neighborhood_id=self.neighborhood._id, deleted=True)).count()
495+ public_count = 0
496+ private_count = 0
497+ last_updated_30 = 0
498+ last_updated_60 = 0
499+ last_updated_90 = 0
500+ today_date = datetime.today()
501+ if M.Project.query.find(dict(neighborhood_id=self.neighborhood._id, deleted=False)).count() < 20000: # arbitrary limit for efficiency
502+ for p in M.Project.query.find(dict(neighborhood_id=self.neighborhood._id, deleted=False)):
503+ if p.private:
504+ private_count = private_count + 1
505+ else:
506+ public_count = public_count + 1
507+ if today_date - p.last_updated < timedelta(days=30):
508+ last_updated_30 = last_updated_30 + 1
509+ if today_date - p.last_updated < timedelta(days=60):
510+ last_updated_60 = last_updated_60 + 1
511+ if today_date - p.last_updated < timedelta(days=90):
512+ last_updated_90 = last_updated_90 + 1
513+
514+ set_nav(self.neighborhood)
515+ return dict(
516+ delete_count = delete_count,
517+ public_count = public_count,
518+ private_count = private_count,
519+ last_updated_30 = last_updated_30,
520+ last_updated_60 = last_updated_60,
521+ last_updated_90 = last_updated_90,
522+ neighborhood = self.neighborhood,
523+ )
524+
525+ @without_trailing_slash
526+ @expose('jinja:allura:templates/neighborhood_stats_adminlist.html')
527+ def adminlist(self, sort='alpha', limit=25, page=0, **kw):
528+ limit, page, start = g.handle_paging(limit, page)
529+
530+ pq = M.Project.query.find(dict(neighborhood_id=self.neighborhood._id, deleted=False))
531+ if sort=='alpha':
532+ pq.sort('name')
533+ else:
534+ pq.sort('last_updated', pymongo.DESCENDING)
535+ count = pq.count()
536+ projects = pq.skip(start).limit(int(limit)).all()
537+
538+ entries = []
539+ for proj in projects:
540+ admin_role = M.ProjectRole.query.get(project_id=proj.root_project._id,name='Admin')
541+ if admin_role is None:
542+ continue
543+ user_role_list = M.ProjectRole.query.find(dict(project_id=proj.root_project._id, name=None)).all()
544+ for ur in user_role_list:
545+ if ur.user is not None and admin_role._id in ur.roles:
546+ entries.append({'project': proj, 'user': ur.user})
547+
548+ set_nav(self.neighborhood)
549+ return dict(entries=entries,
550+ sort=sort,
551+ limit=limit, page=page, count=count,
552+ page_list=W.page_list,
553+ neighborhood=self.neighborhood,
554+ )
555+
556+
488557 class NeighborhoodModerateController(object):
489558
490559 def __init__(self, neighborhood):
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -1,7 +1,9 @@
11 import logging
22 from collections import defaultdict
3-from datetime import datetime
3+from datetime import datetime, timedelta
44
5+import Image
6+import pymongo
57 import pkg_resources
68 from pylons import c, g, request
79 from paste.deploy.converters import asbool
@@ -40,6 +42,7 @@ class W:
4042 screenshot_list = ProjectScreenshots()
4143 metadata_admin = aw.MetadataAdmin()
4244 audit = aw.AuditLog()
45+ page_list=ffw.PageList()
4346
4447 class AdminWidgets(WidgetController):
4548 widgets=['users', 'tool_status']
@@ -138,6 +141,7 @@ class AdminApp(Application):
138141 links.append(SitemapEntry('Invitation(s)', admin_url+'invitations'))
139142 links.append(SitemapEntry('Audit Trail', admin_url+ 'audit/'))
140143 if c.project.shortname == '--init--':
144+ links.append(SitemapEntry('Statistics', nbhd_admin_url+ 'stats/'))
141145 links.append(None)
142146 links.append(SitemapEntry('Help', nbhd_admin_url+ 'help/'))
143147 return links
@@ -728,6 +732,7 @@ class AuditController(BaseController):
728732 page=page,
729733 count=count)
730734
735+
731736 class AdminAppAdminController(DefaultAdminController):
732737 '''Administer the admin app'''
733738 pass
--- a/Allura/allura/lib/helpers.py
+++ b/Allura/allura/lib/helpers.py
@@ -26,6 +26,7 @@ from tg.decorators import before_validate
2626 from formencode.variabledecode import variable_decode
2727 import formencode
2828 from jinja2 import Markup
29+from paste.deploy.converters import asbool
2930
3031 from webhelpers import date, feedgenerator, html, number, misc, text
3132
--- /dev/null
+++ b/Allura/allura/templates/neighborhood_stats.html
@@ -0,0 +1,33 @@
1+{% extends g.theme.master %}
2+
3+{% block top_nav %}
4+ {% include 'allura:templates/jinja_master/neigh_top_nav.html' %}
5+{% endblock %}
6+
7+{% block title %}Neighborhood Statistics{% endblock %}
8+
9+{% block header %}Neighborhood Statistics{% endblock %}
10+
11+{% block content %}
12+ <div class="grid-9">
13+ <b>Number of projects that are...</b>
14+ <ul>
15+ <li>Public: {{ public_count }}</li>
16+ <li>Private: {{ private_count }}</li>
17+ {% if h.asbool(config.get('allow_project_delete', True)) %}
18+ <li>Deleted: {{ delete_count }}</li>
19+ {% endif %}
20+ </ul>
21+ </div>
22+ <div class="grid-10">
23+ <b>Number of projects updated in the last...</b>
24+ <ul>
25+ <li>30 days: {{ last_updated_30 }}</li>
26+ <li>60 days: {{ last_updated_60 }}</li>
27+ <li>90 days: {{ last_updated_90 }}</li>
28+ </ul>
29+ </div>
30+ <div class="grid-19">
31+ <a href="adminlist">View a list of project admins</a>
32+ </div>
33+{% endblock %}
--- /dev/null
+++ b/Allura/allura/templates/neighborhood_stats_adminlist.html
@@ -0,0 +1,37 @@
1+{% extends g.theme.master %}
2+
3+{% block title %}{{c.project.name}} / Statistics / Admins list{% endblock %}
4+
5+{% block header %}Admins list{% endblock %}
6+
7+{% block top_nav %}
8+ {% include 'allura:templates/jinja_master/neigh_top_nav.html' %}
9+{% endblock %}
10+
11+{% block content %}
12+<table>
13+ <thead>
14+ <tr>
15+ <th>Project</th>
16+ <th>Username</th>
17+ <th>E-mail</th>
18+ <th>Name</th>
19+ </tr>
20+ </thead>
21+ <tbody>
22+ {% for entry in entries %}
23+ <tr>
24+ <td style="white-space: nowrap"><a href="{{entry.project.url()}}">{{ entry.project.name }}</a></td>
25+ <td>{{ entry.user.username }}</td>
26+ <td>{{ entry.user.preferences.email_address or 'Unknown' }}</td>
27+ <td>{{ entry.user.display_name }}</td>
28+ </tr>
29+ {% endfor %}
30+ </tbody>
31+</table>
32+
33+<div class="grid-15" style="clear:both">
34+ {{page_list.display(limit=limit, page=page, count=count)}}
35+</div>
36+
37+{% endblock %}
--- a/Allura/allura/tests/functional/test_neighborhood.py
+++ b/Allura/allura/tests/functional/test_neighborhood.py
@@ -2,10 +2,12 @@ import json
22 import os
33 from cStringIO import StringIO
44 from nose.tools import assert_raises
5+from datetime import datetime, timedelta
56
67 import Image
78 from tg import config
89 from nose.tools import assert_equal
10+from ming.orm.ormsession import ThreadLocalORMSession
911
1012 import allura
1113 from allura import model as M
@@ -44,6 +46,44 @@ class TestNeighborhood(TestController):
4446 params=dict(project_template='{'),
4547 extra_environ=dict(username='root'))
4648 assert 'Invalid JSON' in r
49+
50+ def test_admin_stats_del_count(self):
51+ neighborhood = M.Neighborhood.query.get(name='Adobe')
52+ proj = M.Project.query.get(neighborhood_id=neighborhood._id)
53+ proj.deleted = True
54+ ThreadLocalORMSession.flush_all()
55+ r = self.app.get('/adobe/_admin/stats/', extra_environ=dict(username='root'))
56+ assert 'Deleted: 1' in r
57+ assert 'Private: 0' in r
58+
59+ def test_admin_stats_priv_count(self):
60+ neighborhood = M.Neighborhood.query.get(name='Adobe')
61+ proj = M.Project.query.get(neighborhood_id=neighborhood._id)
62+ proj.deleted = False
63+ proj.private = True
64+ ThreadLocalORMSession.flush_all()
65+ r = self.app.get('/adobe/_admin/stats/', extra_environ=dict(username='root'))
66+ assert 'Deleted: 0' in r
67+ assert 'Private: 1' in r
68+
69+ def test_admin_stats_adminlist(self):
70+ neighborhood = M.Neighborhood.query.get(name='Adobe')
71+ proj = M.Project.query.get(neighborhood_id=neighborhood._id)
72+ proj.private = False
73+ ThreadLocalORMSession.flush_all()
74+ r = self.app.get('/adobe/_admin/stats/adminlist', extra_environ=dict(username='root'))
75+ pq = M.Project.query.find(dict(neighborhood_id=neighborhood._id, deleted=False))
76+ pq.sort('name')
77+ projects = pq.skip(0).limit(int(25)).all()
78+ for proj in projects:
79+ admin_role = M.ProjectRole.query.get(project_id=proj.root_project._id,name='Admin')
80+ if admin_role is None:
81+ continue
82+ user_role_list = M.ProjectRole.query.find(dict(project_id=proj.root_project._id, name=None)).all()
83+ for ur in user_role_list:
84+ if ur.user is not None and admin_role._id in ur.roles:
85+ assert proj.name in r
86+ assert ur.user.username in r
4787
4888 def test_icon(self):
4989 file_name = 'neo-icon-set-454545-256x350.png'
@@ -532,4 +572,4 @@ class TestNeighborhood(TestController):
532572
533573 def test_help(self):
534574 r = self.app.get('/p/_admin/help/', extra_environ=dict(username='root'))
535- assert 'macro' in r
\ No newline at end of file
575+ assert 'macro' in r