##// END OF EJS Templates
Some fixes to summary, and total rewrite of summary graphs implemented more interactive graph....
marcink -
r487:b12ea84f celery
parent child Browse files
Show More
@@ -1,26 +1,30 b''
1 from vcs.utils.lazy import LazyProperty
1 from vcs.utils.lazy import LazyProperty
2 import logging
2 import logging
3 import os
3 import os
4 import sys
4 import sys
5 import traceback
5 import traceback
6
6
7 log = logging.getLogger(__name__)
7 log = logging.getLogger(__name__)
8
8
9 class ResultWrapper(object):
9 class ResultWrapper(object):
10 def __init__(self, task):
10 def __init__(self, task):
11 self.task = task
11 self.task = task
12
12
13 @LazyProperty
13 @LazyProperty
14 def result(self):
14 def result(self):
15 return self.task
15 return self.task
16
16
17 def run_task(task,*args,**kwargs):
17 def run_task(task, *args, **kwargs):
18 try:
18 try:
19 t = task.delay(*args,**kwargs)
19 t = task.delay(*args, **kwargs)
20 log.info('running task %s',t.task_id)
20 log.info('running task %s', t.task_id)
21 return t
21 return t
22 except:
22 except Exception, e:
23 if e.errno == 111:
24 log.debug('Unnable to connect. Sync execution')
25 else:
23 log.error(traceback.format_exc())
26 log.error(traceback.format_exc())
24 #pure sync version
27 #pure sync version
25 return ResultWrapper(task(*args,**kwargs))
28 return ResultWrapper(task(*args, **kwargs))
29
26 No newline at end of file
30
@@ -1,223 +1,229 b''
1 from celery.decorators import task
1 from celery.decorators import task
2 from celery.task.sets import subtask
2 from celery.task.sets import subtask
3 from celeryconfig import PYLONS_CONFIG as config
3 from celeryconfig import PYLONS_CONFIG as config
4 from datetime import datetime, timedelta
4 from datetime import datetime, timedelta
5 from pylons.i18n.translation import _
5 from pylons.i18n.translation import _
6 from pylons_app.lib.celerylib import run_task
6 from pylons_app.lib.celerylib import run_task
7 from pylons_app.lib.helpers import person
7 from pylons_app.lib.helpers import person
8 from pylons_app.lib.smtp_mailer import SmtpMailer
8 from pylons_app.lib.smtp_mailer import SmtpMailer
9 from pylons_app.lib.utils import OrderedDict
9 from pylons_app.lib.utils import OrderedDict
10 from operator import itemgetter
11 from vcs.backends.hg import MercurialRepository
10 from time import mktime
12 from time import mktime
11 from vcs.backends.hg import MercurialRepository
12 import calendar
13 import calendar
13 import traceback
14 import traceback
14 import json
15 import json
15
16
16 __all__ = ['whoosh_index', 'get_commits_stats',
17 __all__ = ['whoosh_index', 'get_commits_stats',
17 'reset_user_password', 'send_email']
18 'reset_user_password', 'send_email']
18
19
19 def get_session():
20 def get_session():
20 from sqlalchemy import engine_from_config
21 from sqlalchemy import engine_from_config
21 from sqlalchemy.orm import sessionmaker, scoped_session
22 from sqlalchemy.orm import sessionmaker, scoped_session
22 engine = engine_from_config(dict(config.items('app:main')), 'sqlalchemy.db1.')
23 engine = engine_from_config(dict(config.items('app:main')), 'sqlalchemy.db1.')
23 sa = scoped_session(sessionmaker(bind=engine))
24 sa = scoped_session(sessionmaker(bind=engine))
24 return sa
25 return sa
25
26
26 def get_hg_settings():
27 def get_hg_settings():
27 from pylons_app.model.db import HgAppSettings
28 from pylons_app.model.db import HgAppSettings
28 try:
29 try:
29 sa = get_session()
30 sa = get_session()
30 ret = sa.query(HgAppSettings).all()
31 ret = sa.query(HgAppSettings).all()
31 finally:
32 finally:
32 sa.remove()
33 sa.remove()
33
34
34 if not ret:
35 if not ret:
35 raise Exception('Could not get application settings !')
36 raise Exception('Could not get application settings !')
36 settings = {}
37 settings = {}
37 for each in ret:
38 for each in ret:
38 settings['hg_app_' + each.app_settings_name] = each.app_settings_value
39 settings['hg_app_' + each.app_settings_name] = each.app_settings_value
39
40
40 return settings
41 return settings
41
42
42 def get_hg_ui_settings():
43 def get_hg_ui_settings():
43 from pylons_app.model.db import HgAppUi
44 from pylons_app.model.db import HgAppUi
44 try:
45 try:
45 sa = get_session()
46 sa = get_session()
46 ret = sa.query(HgAppUi).all()
47 ret = sa.query(HgAppUi).all()
47 finally:
48 finally:
48 sa.remove()
49 sa.remove()
49
50
50 if not ret:
51 if not ret:
51 raise Exception('Could not get application ui settings !')
52 raise Exception('Could not get application ui settings !')
52 settings = {}
53 settings = {}
53 for each in ret:
54 for each in ret:
54 k = each.ui_key
55 k = each.ui_key
55 v = each.ui_value
56 v = each.ui_value
56 if k == '/':
57 if k == '/':
57 k = 'root_path'
58 k = 'root_path'
58
59
59 if k.find('.') != -1:
60 if k.find('.') != -1:
60 k = k.replace('.', '_')
61 k = k.replace('.', '_')
61
62
62 if each.ui_section == 'hooks':
63 if each.ui_section == 'hooks':
63 v = each.ui_active
64 v = each.ui_active
64
65
65 settings[each.ui_section + '_' + k] = v
66 settings[each.ui_section + '_' + k] = v
66
67
67 return settings
68 return settings
68
69
69 @task
70 @task
70 def whoosh_index(repo_location, full_index):
71 def whoosh_index(repo_location, full_index):
71 log = whoosh_index.get_logger()
72 log = whoosh_index.get_logger()
72 from pylons_app.lib.indexers import DaemonLock
73 from pylons_app.lib.indexers import DaemonLock
73 from pylons_app.lib.indexers.daemon import WhooshIndexingDaemon, LockHeld
74 from pylons_app.lib.indexers.daemon import WhooshIndexingDaemon, LockHeld
74 try:
75 try:
75 l = DaemonLock()
76 l = DaemonLock()
76 WhooshIndexingDaemon(repo_location=repo_location)\
77 WhooshIndexingDaemon(repo_location=repo_location)\
77 .run(full_index=full_index)
78 .run(full_index=full_index)
78 l.release()
79 l.release()
79 return 'Done'
80 return 'Done'
80 except LockHeld:
81 except LockHeld:
81 log.info('LockHeld')
82 log.info('LockHeld')
82 return 'LockHeld'
83 return 'LockHeld'
83
84
84 @task
85 @task
85 def get_commits_stats(repo):
86 def get_commits_stats(repo):
86 log = get_commits_stats.get_logger()
87 log = get_commits_stats.get_logger()
87 aggregate = OrderedDict()
88 aggregate = OrderedDict()
88 overview_aggregate = OrderedDict()
89 overview_aggregate = OrderedDict()
89 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
90 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
90 repo = MercurialRepository(repos_path + repo)
91 repo = MercurialRepository(repos_path + repo)
91 #graph range
92 #graph range
92 td = datetime.today() + timedelta(days=1)
93 td = datetime.today() + timedelta(days=1)
93 y, m, d = td.year, td.month, td.day
94 y, m, d = td.year, td.month, td.day
94
95
95 ts_min_y = mktime((y - 1, (td - timedelta(days=calendar.mdays[m])).month,
96 ts_min_y = mktime((y - 1, (td - timedelta(days=calendar.mdays[m])).month,
96 d, 0, 0, 0, 0, 0, 0,))
97 d, 0, 0, 0, 0, 0, 0,))
97 ts_min_m = mktime((y, (td - timedelta(days=calendar.mdays[m])).month,
98 ts_min_m = mktime((y, (td - timedelta(days=calendar.mdays[m])).month,
98 d, 0, 0, 0, 0, 0, 0,))
99 d, 0, 0, 0, 0, 0, 0,))
99
100
100 ts_max_y = mktime((y, m, d, 0, 0, 0, 0, 0, 0,))
101 ts_max_y = mktime((y, m, d, 0, 0, 0, 0, 0, 0,))
102 skip_date_limit = True
101
103
102 def author_key_cleaner(k):
104 def author_key_cleaner(k):
103 k = person(k)
105 k = person(k)
104 k = k.replace('"', "") #for js data compatibilty
106 k = k.replace('"', "") #for js data compatibilty
105 return k
107 return k
106
108
107 for cs in repo[:1000]:#added limit 200 until fix #29 is made
109 for cs in repo[:200]:#added limit 200 until fix #29 is made
108 k = '%s-%s-%s' % (cs.date.timetuple()[0], cs.date.timetuple()[1],
110 k = '%s-%s-%s' % (cs.date.timetuple()[0], cs.date.timetuple()[1],
109 cs.date.timetuple()[2])
111 cs.date.timetuple()[2])
110 timetupple = [int(x) for x in k.split('-')]
112 timetupple = [int(x) for x in k.split('-')]
111 timetupple.extend([0 for _ in xrange(6)])
113 timetupple.extend([0 for _ in xrange(6)])
112 k = mktime(timetupple)
114 k = mktime(timetupple)
113 if aggregate.has_key(author_key_cleaner(cs.author)):
115 if aggregate.has_key(author_key_cleaner(cs.author)):
114 if aggregate[author_key_cleaner(cs.author)].has_key(k):
116 if aggregate[author_key_cleaner(cs.author)].has_key(k):
115 aggregate[author_key_cleaner(cs.author)][k]["commits"] += 1
117 aggregate[author_key_cleaner(cs.author)][k]["commits"] += 1
116 aggregate[author_key_cleaner(cs.author)][k]["added"] += len(cs.added)
118 aggregate[author_key_cleaner(cs.author)][k]["added"] += len(cs.added)
117 aggregate[author_key_cleaner(cs.author)][k]["changed"] += len(cs.changed)
119 aggregate[author_key_cleaner(cs.author)][k]["changed"] += len(cs.changed)
118 aggregate[author_key_cleaner(cs.author)][k]["removed"] += len(cs.removed)
120 aggregate[author_key_cleaner(cs.author)][k]["removed"] += len(cs.removed)
119
121
120 else:
122 else:
121 #aggregate[author_key_cleaner(cs.author)].update(dates_range)
123 #aggregate[author_key_cleaner(cs.author)].update(dates_range)
122 if k >= ts_min_y and k <= ts_max_y:
124 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
123 aggregate[author_key_cleaner(cs.author)][k] = {}
125 aggregate[author_key_cleaner(cs.author)][k] = {}
124 aggregate[author_key_cleaner(cs.author)][k]["commits"] = 1
126 aggregate[author_key_cleaner(cs.author)][k]["commits"] = 1
125 aggregate[author_key_cleaner(cs.author)][k]["added"] = len(cs.added)
127 aggregate[author_key_cleaner(cs.author)][k]["added"] = len(cs.added)
126 aggregate[author_key_cleaner(cs.author)][k]["changed"] = len(cs.changed)
128 aggregate[author_key_cleaner(cs.author)][k]["changed"] = len(cs.changed)
127 aggregate[author_key_cleaner(cs.author)][k]["removed"] = len(cs.removed)
129 aggregate[author_key_cleaner(cs.author)][k]["removed"] = len(cs.removed)
128
130
129 else:
131 else:
130 if k >= ts_min_y and k <= ts_max_y:
132 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
131 aggregate[author_key_cleaner(cs.author)] = OrderedDict()
133 aggregate[author_key_cleaner(cs.author)] = OrderedDict()
132 #aggregate[author_key_cleaner(cs.author)].update(dates_range)
134 #aggregate[author_key_cleaner(cs.author)].update(dates_range)
133 aggregate[author_key_cleaner(cs.author)][k] = {}
135 aggregate[author_key_cleaner(cs.author)][k] = {}
134 aggregate[author_key_cleaner(cs.author)][k]["commits"] = 1
136 aggregate[author_key_cleaner(cs.author)][k]["commits"] = 1
135 aggregate[author_key_cleaner(cs.author)][k]["added"] = len(cs.added)
137 aggregate[author_key_cleaner(cs.author)][k]["added"] = len(cs.added)
136 aggregate[author_key_cleaner(cs.author)][k]["changed"] = len(cs.changed)
138 aggregate[author_key_cleaner(cs.author)][k]["changed"] = len(cs.changed)
137 aggregate[author_key_cleaner(cs.author)][k]["removed"] = len(cs.removed)
139 aggregate[author_key_cleaner(cs.author)][k]["removed"] = len(cs.removed)
138
140
139
141
140 if overview_aggregate.has_key(k):
142 if overview_aggregate.has_key(k):
141 overview_aggregate[k] += 1
143 overview_aggregate[k] += 1
142 else:
144 else:
143 overview_aggregate[k] = 1
145 overview_aggregate[k] = 1
144
146
145 overview_data = []
147 overview_data = []
146 for k, v in overview_aggregate.items():
148 for k, v in overview_aggregate.items():
147 overview_data.append([k, v])
149 overview_data.append([k, v])
150 overview_data = sorted(overview_data, key=itemgetter(0))
148 data = {}
151 data = {}
149 for author in aggregate:
152 for author in aggregate:
150 data[author] = {"label":author,
153 commit_data = sorted([{"time":x,
151 "data":[{"time":x,
152 "commits":aggregate[author][x]['commits'],
154 "commits":aggregate[author][x]['commits'],
153 "added":aggregate[author][x]['added'],
155 "added":aggregate[author][x]['added'],
154 "changed":aggregate[author][x]['changed'],
156 "changed":aggregate[author][x]['changed'],
155 "removed":aggregate[author][x]['removed'],
157 "removed":aggregate[author][x]['removed'],
156 } for x in aggregate[author]],
158 } for x in aggregate[author]],
159 key=itemgetter('time'))
160
161 data[author] = {"label":author,
162 "data":commit_data,
157 "schema":["commits"]
163 "schema":["commits"]
158 }
164 }
159
165
160 if not data:
166 if not data:
161 data[author_key_cleaner(repo.contact)] = {
167 data[author_key_cleaner(repo.contact)] = {
162 "label":author_key_cleaner(repo.contact),
168 "label":author_key_cleaner(repo.contact),
163 "data":[0, 1],
169 "data":[0, 1],
164 "schema":["commits"],
170 "schema":["commits"],
165 }
171 }
166
172
167 return (ts_min_m, ts_max_y, json.dumps(data), json.dumps(overview_data))
173 return (ts_min_m, ts_max_y, json.dumps(data), json.dumps(overview_data))
168
174
169 @task
175 @task
170 def reset_user_password(user_email):
176 def reset_user_password(user_email):
171 log = reset_user_password.get_logger()
177 log = reset_user_password.get_logger()
172 from pylons_app.lib import auth
178 from pylons_app.lib import auth
173 from pylons_app.model.db import User
179 from pylons_app.model.db import User
174
180
175 try:
181 try:
176 try:
182 try:
177 sa = get_session()
183 sa = get_session()
178 user = sa.query(User).filter(User.email == user_email).scalar()
184 user = sa.query(User).filter(User.email == user_email).scalar()
179 new_passwd = auth.PasswordGenerator().gen_password(8,
185 new_passwd = auth.PasswordGenerator().gen_password(8,
180 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
186 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
181 user.password = auth.get_crypt_password(new_passwd)
187 user.password = auth.get_crypt_password(new_passwd)
182 sa.add(user)
188 sa.add(user)
183 sa.commit()
189 sa.commit()
184 log.info('change password for %s', user_email)
190 log.info('change password for %s', user_email)
185 if new_passwd is None:
191 if new_passwd is None:
186 raise Exception('unable to generate new password')
192 raise Exception('unable to generate new password')
187
193
188 except:
194 except:
189 log.error(traceback.format_exc())
195 log.error(traceback.format_exc())
190 sa.rollback()
196 sa.rollback()
191
197
192 run_task(send_email, user_email,
198 run_task(send_email, user_email,
193 "Your new hg-app password",
199 "Your new hg-app password",
194 'Your new hg-app password:%s' % (new_passwd))
200 'Your new hg-app password:%s' % (new_passwd))
195 log.info('send new password mail to %s', user_email)
201 log.info('send new password mail to %s', user_email)
196
202
197
203
198 except:
204 except:
199 log.error('Failed to update user password')
205 log.error('Failed to update user password')
200 log.error(traceback.format_exc())
206 log.error(traceback.format_exc())
201 return True
207 return True
202
208
203 @task
209 @task
204 def send_email(recipients, subject, body):
210 def send_email(recipients, subject, body):
205 log = send_email.get_logger()
211 log = send_email.get_logger()
206 email_config = dict(config.items('DEFAULT'))
212 email_config = dict(config.items('DEFAULT'))
207 mail_from = email_config.get('app_email_from')
213 mail_from = email_config.get('app_email_from')
208 user = email_config.get('smtp_username')
214 user = email_config.get('smtp_username')
209 passwd = email_config.get('smtp_password')
215 passwd = email_config.get('smtp_password')
210 mail_server = email_config.get('smtp_server')
216 mail_server = email_config.get('smtp_server')
211 mail_port = email_config.get('smtp_port')
217 mail_port = email_config.get('smtp_port')
212 tls = email_config.get('smtp_use_tls')
218 tls = email_config.get('smtp_use_tls')
213 ssl = False
219 ssl = False
214
220
215 try:
221 try:
216 m = SmtpMailer(mail_from, user, passwd, mail_server,
222 m = SmtpMailer(mail_from, user, passwd, mail_server,
217 mail_port, ssl, tls)
223 mail_port, ssl, tls)
218 m.send(recipients, subject, body)
224 m.send(recipients, subject, body)
219 except:
225 except:
220 log.error('Mail sending failed')
226 log.error('Mail sending failed')
221 log.error(traceback.format_exc())
227 log.error(traceback.format_exc())
222 return False
228 return False
223 return True
229 return True
@@ -1,356 +1,508 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('Mercurial Repository Overview')}
4 ${_('Mercurial Repository Overview')}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(u'Home',h.url('/'))}
8 ${h.link_to(u'Home',h.url('/'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
11 &raquo;
11 &raquo;
12 ${_('summary')}
12 ${_('summary')}
13 </%def>
13 </%def>
14
14
15 <%def name="page_nav()">
15 <%def name="page_nav()">
16 ${self.menu('summary')}
16 ${self.menu('summary')}
17 </%def>
17 </%def>
18
18
19 <%def name="main()">
19 <%def name="main()">
20 <script type="text/javascript">
20 <script type="text/javascript">
21 var E = YAHOO.util.Event;
21 var E = YAHOO.util.Event;
22 var D = YAHOO.util.Dom;
22 var D = YAHOO.util.Dom;
23
23
24 E.onDOMReady(function(e){
24 E.onDOMReady(function(e){
25 id = 'clone_url';
25 id = 'clone_url';
26 E.addListener(id,'click',function(e){
26 E.addListener(id,'click',function(e){
27 D.get('clone_url').select();
27 D.get('clone_url').select();
28 })
28 })
29 })
29 })
30 </script>
30 </script>
31 <div class="box box-left">
31 <div class="box box-left">
32 <!-- box / title -->
32 <!-- box / title -->
33 <div class="title">
33 <div class="title">
34 ${self.breadcrumbs()}
34 ${self.breadcrumbs()}
35 </div>
35 </div>
36 <!-- end box / title -->
36 <!-- end box / title -->
37 <div class="form">
37 <div class="form">
38 <div class="fields">
38 <div class="fields">
39
39
40 <div class="field">
40 <div class="field">
41 <div class="label">
41 <div class="label">
42 <label>${_('Name')}:</label>
42 <label>${_('Name')}:</label>
43 </div>
43 </div>
44 <div class="input-short">
44 <div class="input-short">
45 <span style="font-size: 1.6em;font-weight: bold">${c.repo_info.name}</span>
45 <span style="font-size: 1.6em;font-weight: bold">${c.repo_info.name}</span>
46 </div>
46 </div>
47 </div>
47 </div>
48
48
49
49
50 <div class="field">
50 <div class="field">
51 <div class="label">
51 <div class="label">
52 <label>${_('Description')}:</label>
52 <label>${_('Description')}:</label>
53 </div>
53 </div>
54 <div class="input-short">
54 <div class="input-short">
55 ${c.repo_info.description}
55 ${c.repo_info.description}
56 </div>
56 </div>
57 </div>
57 </div>
58
58
59
59
60 <div class="field">
60 <div class="field">
61 <div class="label">
61 <div class="label">
62 <label>${_('Contact')}:</label>
62 <label>${_('Contact')}:</label>
63 </div>
63 </div>
64 <div class="input-short">
64 <div class="input-short">
65 <div class="gravatar">
65 <div class="gravatar">
66 <img alt="gravatar" src="${h.gravatar_url(c.repo_info.dbrepo.user.email)}"/>
66 <img alt="gravatar" src="${h.gravatar_url(c.repo_info.dbrepo.user.email)}"/>
67 </div>
67 </div>
68 ${_('Username')}: ${c.repo_info.dbrepo.user.username}<br/>
68 ${_('Username')}: ${c.repo_info.dbrepo.user.username}<br/>
69 ${_('Name')}: ${c.repo_info.dbrepo.user.name} ${c.repo_info.dbrepo.user.lastname}<br/>
69 ${_('Name')}: ${c.repo_info.dbrepo.user.name} ${c.repo_info.dbrepo.user.lastname}<br/>
70 ${_('Email')}: <a href="mailto:${c.repo_info.dbrepo.user.email}">${c.repo_info.dbrepo.user.email}</a>
70 ${_('Email')}: <a href="mailto:${c.repo_info.dbrepo.user.email}">${c.repo_info.dbrepo.user.email}</a>
71 </div>
71 </div>
72 </div>
72 </div>
73
73
74 <div class="field">
74 <div class="field">
75 <div class="label">
75 <div class="label">
76 <label>${_('Last change')}:</label>
76 <label>${_('Last change')}:</label>
77 </div>
77 </div>
78 <div class="input-short">
78 <div class="input-short">
79 ${h.age(c.repo_info.last_change)} - ${h.rfc822date(c.repo_info.last_change)}
79 ${h.age(c.repo_info.last_change)} - ${h.rfc822date(c.repo_info.last_change)}
80 ${_('by')} ${c.repo_info.get_changeset('tip').author}
81
80 </div>
82 </div>
81 </div>
83 </div>
82
84
83 <div class="field">
85 <div class="field">
84 <div class="label">
86 <div class="label">
85 <label>${_('Clone url')}:</label>
87 <label>${_('Clone url')}:</label>
86 </div>
88 </div>
87 <div class="input-short">
89 <div class="input-short">
88 <input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
90 <input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
89 </div>
91 </div>
90 </div>
92 </div>
91
93
92 <div class="field">
94 <div class="field">
93 <div class="label">
95 <div class="label">
94 <label>${_('Download')}:</label>
96 <label>${_('Download')}:</label>
95 </div>
97 </div>
96 <div class="input-short">
98 <div class="input-short">
97 %for cnt,archive in enumerate(c.repo_info._get_archives()):
99 %for cnt,archive in enumerate(c.repo_info._get_archives()):
98 %if cnt >=1:
100 %if cnt >=1:
99 |
101 |
100 %endif
102 %endif
101 ${h.link_to(c.repo_info.name+'.'+archive['type'],
103 ${h.link_to(c.repo_info.name+'.'+archive['type'],
102 h.url('files_archive_home',repo_name=c.repo_info.name,
104 h.url('files_archive_home',repo_name=c.repo_info.name,
103 revision='tip',fileformat=archive['extension']),class_="archive_icon")}
105 revision='tip',fileformat=archive['extension']),class_="archive_icon")}
104 %endfor
106 %endfor
105 </div>
107 </div>
106 </div>
108 </div>
107
109
108 <div class="field">
110 <div class="field">
109 <div class="label">
111 <div class="label">
110 <label>${_('Feeds')}:</label>
112 <label>${_('Feeds')}:</label>
111 </div>
113 </div>
112 <div class="input-short">
114 <div class="input-short">
113 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.repo_info.name),class_='rss_icon')}
115 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.repo_info.name),class_='rss_icon')}
114 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.repo_info.name),class_='atom_icon')}
116 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.repo_info.name),class_='atom_icon')}
115 </div>
117 </div>
116 </div>
118 </div>
117 </div>
119 </div>
118 </div>
120 </div>
119 </div>
121 </div>
120
122
121 <div class="box box-right" style="min-height:455px">
123 <div class="box box-right" style="min-height:455px">
122 <!-- box / title -->
124 <!-- box / title -->
123 <div class="title">
125 <div class="title">
124 <h5>${_('Last month commit activity')}</h5>
126 <h5>${_('Commit activity')}</h5>
125 </div>
127 </div>
126
128
127 <div class="table">
129 <div class="table">
128 <div id="commit_history" style="width:560px;height:300px;float:left"></div>
130 <div id="commit_history" style="width:560px;height:300px;float:left"></div>
129 <div style="clear: both;height: 10px"></div>
131 <div style="clear: both;height: 10px"></div>
130 <div id="overview" style="width:560px;height:100px;float:left"></div>
132 <div id="overview" style="width:560px;height:100px;float:left"></div>
131
133
132 <div id="legend_data" style="clear:both;margin-top:10px;">
134 <div id="legend_data" style="clear:both;margin-top:10px;">
133 <div id="legend_container"></div>
135 <div id="legend_container"></div>
134 <div id="legend_choices">
136 <div id="legend_choices">
135 <table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
137 <table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
136 </div>
138 </div>
137 </div>
139 </div>
138 <script type="text/javascript">
140 <script type="text/javascript">
139
141 /**
140 (function () {
142 * Plots summary graph
141 var datasets = ${c.commit_data|n};
143 *
142 var overview_data = ${c.overview_data|n};
144 * @class SummaryPlot
143
145 * @param {from} initial from for detailed graph
144 var i = 0;
146 * @param {to} initial to for detailed graph
147 * @param {dataset}
148 * @param {overview_dataset}
149 */
150 function SummaryPlot(from,to,dataset,overview_dataset) {
151 var initial_ranges = {
152 "xaxis":{
153 "from":from,
154 "to":to,
155 },
156 };
157 var dataset = dataset;
158 var overview_dataset = [overview_dataset];
145 var choiceContainer = YAHOO.util.Dom.get("legend_choices");
159 var choiceContainer = YAHOO.util.Dom.get("legend_choices");
146 var choiceContainerTable = YAHOO.util.Dom.get("legend_choices_tables");
160 var choiceContainerTable = YAHOO.util.Dom.get("legend_choices_tables");
147 for(var key in datasets) {
161 var plotContainer = YAHOO.util.Dom.get('commit_history');
148 datasets[key].color = i;
162 var overviewContainer = YAHOO.util.Dom.get('overview');
149 i++;
150 choiceContainerTable.innerHTML += '<tr><td>'+
151 '<input type="checkbox" name="' + key +'" checked="checked" />'
152 +datasets[key].label+
153 '</td></tr>';
154 };
155
156 function plotAccordingToChoices() {
157 var data = [];
158
163
159 var inputs = choiceContainer.getElementsByTagName("input");
164 var plot_options = {
160 for(var i=0; i<inputs.length; i++) {
161 var key = inputs[i].name;
162 if (key && datasets[key]){
163 if(!inputs[i].checked){
164 data.push({label:key,data:[[0,1],]});
165 }
166 else{
167 data.push(datasets[key]);
168 }
169 }
170 };
171
172 if (data.length > 0){
173 var options = {
174 bars: {show:true,align:'center',lineWidth:4},
165 bars: {show:true,align:'center',lineWidth:4},
175 legend: {show:true, container:"legend_container"},
166 legend: {show:true, container:"legend_container"},
176 points: {show:true,radius:0,fill:false},
167 points: {show:true,radius:0,fill:false},
177 yaxis: {tickDecimals:0,},
168 yaxis: {tickDecimals:0,},
178 xaxis: {mode: "time",
169 xaxis: {
170 mode: "time",
179 timeformat: "%d/%m",
171 timeformat: "%d/%m",
180 min:${c.ts_min},
172 min:from,
181 max:${c.ts_max},
173 max:to,
182
183 },
174 },
184 grid: {hoverable: true,
175 grid: {
176 hoverable: true,
185 clickable: true,
177 clickable: true,
186 autoHighlight:true,
178 autoHighlight:true,
187 color: "#999"},
179 color: "#999"
180 },
181 //selection: {mode: "x"}
182 };
183 var overview_options = {
184 legend:{show:false},
185 bars: {show:true,barWidth: 2,},
186 shadowSize: 0,
187 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
188 yaxis: {ticks: 3, min: 0,},
189 grid: {color: "#999",},
188 selection: {mode: "x"}
190 selection: {mode: "x"}
189 };
191 };
190
192
191 //main plot
193 /**
192 var plot = YAHOO.widget.Flot("commit_history",data,options);
194 *get dummy data needed in few places
195 */
196 function getDummyData(label){
197 return {"label":label,
198 "data":[{"time":0,
199 "commits":0,
200 "added":0,
201 "changed":0,
202 "removed":0,
203 }],
204 "schema":["commits"],
205 "color":'#ffffff',
206 }
207 }
193
208
194 //overview
209 /**
195 var overview = YAHOO.widget.Flot("overview", [overview_data], {
210 * generate checkboxes accordindly to data
196 legend:{show:false},
211 * @param keys
197 bars: {show:true,
212 * @returns
198 barWidth: 2,
213 */
199 },
214 function generateCheckboxes(data) {
200 shadowSize: 0,
215 //append checkboxes
201 xaxis: {mode: "time",
216 var i = 0;
202 timeformat: "%d/%m/%y",
217 choiceContainerTable.innerHTML = '';
203 },
218 for(var pos in data) {
204 yaxis: {ticks: 3, min: 0,},
205 grid: {color: "#999",},
206 selection: {mode: "x"}
207 });
208
219
209 var ranges = {"xaxis":{"from":${c.ts_min},
220 data[pos].color = i;
210 "to":${c.ts_max},},}
221 i++;
211 overview.setSelection(ranges);
222 if(data[pos].label != ''){
223 choiceContainerTable.innerHTML += '<tr><td>'+
224 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
225 +data[pos].label+
226 '</td></tr>';
227 }
228 }
229 }
212
230
231 /**
232 * ToolTip show
233 */
213 function showTooltip(x, y, contents) {
234 function showTooltip(x, y, contents) {
214 var div=document.getElementById('tooltip');
235 var div=document.getElementById('tooltip');
215 if(!div) {
236 if(!div) {
216 div = document.createElement('div');
237 div = document.createElement('div');
217 div.id="tooltip";
238 div.id="tooltip";
218 div.style.position="absolute";
239 div.style.position="absolute";
219 div.style.border='1px solid #fdd';
240 div.style.border='1px solid #fdd';
220 div.style.padding='2px';
241 div.style.padding='2px';
221 div.style.backgroundColor='#fee';
242 div.style.backgroundColor='#fee';
222 document.body.appendChild(div);
243 document.body.appendChild(div);
223 }
244 }
224 YAHOO.util.Dom.setStyle(div, 'opacity', 0);
245 YAHOO.util.Dom.setStyle(div, 'opacity', 0);
225 div.innerHTML = contents;
246 div.innerHTML = contents;
226 div.style.top=(y + 5) + "px";
247 div.style.top=(y + 5) + "px";
227 div.style.left=(x + 5) + "px";
248 div.style.left=(x + 5) + "px";
228
249
229 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
250 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
230 anim.animate();
251 anim.animate();
231 }
252 }
232
253
254 /**
255 * This function will detect if selected period has some changesets for this user
256 if it does this data is then pushed for displaying
257 Additionally it will only display users that are selected by the checkbox
258 */
259 function getDataAccordingToRanges(ranges) {
260
261 var data = [];
262 var keys = [];
263 for(var key in dataset){
264 var push = false;
265 //method1 slow !!
266 ///*
267 for(var ds in dataset[key].data){
268 commit_data = dataset[key].data[ds];
269 //console.log(key);
270 //console.log(new Date(commit_data.time*1000));
271 //console.log(new Date(ranges.xaxis.from*1000));
272 //console.log(new Date(ranges.xaxis.to*1000));
273 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
274 push = true;
275 break;
276 }
277 }
278 //*/
279 /*//method2 sorted commit data !!!
280 var first_commit = dataset[key].data[0].time;
281 var last_commit = dataset[key].data[dataset[key].data.length-1].time;
282
283 console.log(first_commit);
284 console.log(last_commit);
285
286 if (first_commit >= ranges.xaxis.from && last_commit <= ranges.xaxis.to){
287 push = true;
288 }
289 */
290 if(push){
291 data.push(dataset[key]);
292 }
293 }
294 if(data.length >= 1){
295 return data;
296 }
297 else{
298 //just return dummy data for graph to plot itself
299 return [getDummyData('')];
300 }
301
302 }
303
304 /**
305 * redraw using new checkbox data
306 */
307 function plotchoiced(e,args){
308 var cur_data = args[0];
309 var cur_ranges = args[1];
310
311 var new_data = [];
312 var inputs = choiceContainer.getElementsByTagName("input");
313
314 //show only checked labels
315 for(var i=0; i<inputs.length; i++) {
316 var checkbox_key = inputs[i].name;
317
318 if(inputs[i].checked){
319 for(var d in cur_data){
320 if(cur_data[d].label == checkbox_key){
321 new_data.push(cur_data[d]);
322 }
323 }
324 }
325 else{
326 //push dummy data to not hide the label
327 new_data.push(getDummyData(checkbox_key));
328 }
329 }
330
331 var new_options = YAHOO.lang.merge(plot_options, {
332 xaxis: {
333 min: cur_ranges.xaxis.from,
334 max: cur_ranges.xaxis.to,
335 mode:"time",
336 timeformat: "%d/%m",
337 }
338 });
339 if (!new_data){
340 new_data = [[0,1]];
341 }
342 // do the zooming
343 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
344
345 plot.subscribe("plotselected", plotselected);
346
347 //resubscribe plothover
348 plot.subscribe("plothover", plothover);
349
350 // don't fire event on the overview to prevent eternal loop
351 overview.setSelection(cur_ranges, true);
352
353 }
354
355 /**
356 * plot only selected items from overview
357 * @param ranges
358 * @returns
359 */
360 function plotselected(ranges,cur_data) {
361 //updates the data for new plot
362 data = getDataAccordingToRanges(ranges);
363 generateCheckboxes(data);
364
365 var new_options = YAHOO.lang.merge(plot_options, {
366 xaxis: {
367 min: ranges.xaxis.from,
368 max: ranges.xaxis.to,
369 mode:"time",
370 timeformat: "%d/%m",
371 }
372 });
373 // do the zooming
374 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
375
376 plot.subscribe("plotselected", plotselected);
377
378 //resubscribe plothover
379 plot.subscribe("plothover", plothover);
380
381 // don't fire event on the overview to prevent eternal loop
382 overview.setSelection(ranges, true);
383
384 //resubscribe choiced
385 YAHOO.util.Event.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
386 }
387
233 var previousPoint = null;
388 var previousPoint = null;
234
389
235 function plothover(o) {
390 function plothover(o) {
236 var pos = o.pos;
391 var pos = o.pos;
237 var item = o.item;
392 var item = o.item;
238
393
239 //YAHOO.util.Dom.get("x").innerHTML = pos.x.toFixed(2);
394 //YAHOO.util.Dom.get("x").innerHTML = pos.x.toFixed(2);
240 //YAHOO.util.Dom.get("y").innerHTML = pos.y.toFixed(2);
395 //YAHOO.util.Dom.get("y").innerHTML = pos.y.toFixed(2);
241 if (item) {
396 if (item) {
242 if (previousPoint != item.datapoint) {
397 if (previousPoint != item.datapoint) {
243 previousPoint = item.datapoint;
398 previousPoint = item.datapoint;
244
399
245 var tooltip = YAHOO.util.Dom.get("tooltip");
400 var tooltip = YAHOO.util.Dom.get("tooltip");
246 if(tooltip) {
401 if(tooltip) {
247 tooltip.parentNode.removeChild(tooltip);
402 tooltip.parentNode.removeChild(tooltip);
248 }
403 }
249 var x = item.datapoint.x.toFixed(2);
404 var x = item.datapoint.x.toFixed(2);
250 var y = item.datapoint.y.toFixed(2);
405 var y = item.datapoint.y.toFixed(2);
251
406
252 if (!item.series.label){
407 if (!item.series.label){
253 item.series.label = 'commits';
408 item.series.label = 'commits';
254 }
409 }
255 var d = new Date(x*1000);
410 var d = new Date(x*1000);
256 var fd = d.toDateString()
411 var fd = d.toDateString()
257 var nr_commits = parseInt(y);
412 var nr_commits = parseInt(y);
258
413
259 var cur_data = datasets[item.series.label].data[item.dataIndex];
414 var cur_data = dataset[item.series.label].data[item.dataIndex];
260 var added = cur_data.added;
415 var added = cur_data.added;
261 var changed = cur_data.changed;
416 var changed = cur_data.changed;
262 var removed = cur_data.removed;
417 var removed = cur_data.removed;
263
418
264 var nr_commits_suffix = " ${_('commits')} ";
419 var nr_commits_suffix = " ${_('commits')} ";
265 var added_suffix = " ${_('files added')} ";
420 var added_suffix = " ${_('files added')} ";
266 var changed_suffix = " ${_('files changed')} ";
421 var changed_suffix = " ${_('files changed')} ";
267 var removed_suffix = " ${_('files removed')} ";
422 var removed_suffix = " ${_('files removed')} ";
268
423
269
424
270 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
425 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
271 if(added==1){added_suffix=" ${_('file added')} ";}
426 if(added==1){added_suffix=" ${_('file added')} ";}
272 if(changed==1){changed_suffix=" ${_('file changed')} ";}
427 if(changed==1){changed_suffix=" ${_('file changed')} ";}
273 if(removed==1){removed_suffix=" ${_('file removed')} ";}
428 if(removed==1){removed_suffix=" ${_('file removed')} ";}
274
429
275 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
430 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
276 +'<br/>'+
431 +'<br/>'+
277 nr_commits + nr_commits_suffix+'<br/>'+
432 nr_commits + nr_commits_suffix+'<br/>'+
278 added + added_suffix +'<br/>'+
433 added + added_suffix +'<br/>'+
279 changed + changed_suffix + '<br/>'+
434 changed + changed_suffix + '<br/>'+
280 removed + removed_suffix + '<br/>');
435 removed + removed_suffix + '<br/>');
281 }
436 }
282 }
437 }
283 else {
438 else {
284 var tooltip = YAHOO.util.Dom.get("tooltip");
439 var tooltip = YAHOO.util.Dom.get("tooltip");
285
440
286 if(tooltip) {
441 if(tooltip) {
287 tooltip.parentNode.removeChild(tooltip);
442 tooltip.parentNode.removeChild(tooltip);
288 }
443 }
289 previousPoint = null;
444 previousPoint = null;
290 }
445 }
291 }
446 }
292
447
293 plot.subscribe("plothover", plothover);
448 /**
449 * MAIN EXECUTION
450 */
451
452 var data = getDataAccordingToRanges(initial_ranges);
453 generateCheckboxes(data);
294
454
295 function plotselected(ranges) {
455 //main plot
296 // do the zooming
456 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
297 plot = YAHOO.widget.Flot("commit_history", data,
298 YAHOO.lang.merge(options, {
299 xaxis: { min: ranges.xaxis.from,
300 max: ranges.xaxis.to,
301 mode:"time",
302 timeformat: "%d/%m",
303 }
304 }));
305 plot.subscribe("plotselected", plotselected);
306 plot.subscribe("plothover", plothover);
307
457
308 // don't fire event on the overview to prevent eternal loop
458 //overview
309 overview.setSelection(ranges, true);
459 var overview = YAHOO.widget.Flot(overviewContainer, overview_dataset, overview_options);
310 }
460
461 //show initial selection on overview
462 overview.setSelection(initial_ranges);
463
311 plot.subscribe("plotselected", plotselected);
464 plot.subscribe("plotselected", plotselected);
312
465
313 overview.subscribe("plotselected", function (ranges) {
466 overview.subscribe("plotselected", function (ranges) {
314 plot.setSelection(ranges);
467 plot.setSelection(ranges);
315 });
468 });
316 }
469
317 }
470 plot.subscribe("plothover", plothover);
318
471
319 YAHOO.util.Event.on(choiceContainer.getElementsByTagName("input"), "click", plotAccordingToChoices);
472 YAHOO.util.Event.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
320
473 }
321 plotAccordingToChoices();
474 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
322 })();
323 </script>
475 </script>
324
476
325 </div>
477 </div>
326 </div>
478 </div>
327
479
328 <div class="box">
480 <div class="box">
329 <div class="title">
481 <div class="title">
330 <div class="breadcrumbs">${h.link_to(_('Last ten changes'),h.url('changelog_home',repo_name=c.repo_name))}</div>
482 <div class="breadcrumbs">${h.link_to(_('Last ten changes'),h.url('changelog_home',repo_name=c.repo_name))}</div>
331 </div>
483 </div>
332 <div class="table">
484 <div class="table">
333 <%include file='../shortlog/shortlog_data.html'/>
485 <%include file='../shortlog/shortlog_data.html'/>
334 ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
486 ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
335 </div>
487 </div>
336 </div>
488 </div>
337 <div class="box">
489 <div class="box">
338 <div class="title">
490 <div class="title">
339 <div class="breadcrumbs">${h.link_to(_('Last ten tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
491 <div class="breadcrumbs">${h.link_to(_('Last ten tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
340 </div>
492 </div>
341 <div class="table">
493 <div class="table">
342 <%include file='../tags/tags_data.html'/>
494 <%include file='../tags/tags_data.html'/>
343 ${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
495 ${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
344 </div>
496 </div>
345 </div>
497 </div>
346 <div class="box">
498 <div class="box">
347 <div class="title">
499 <div class="title">
348 <div class="breadcrumbs">${h.link_to(_('Last ten branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
500 <div class="breadcrumbs">${h.link_to(_('Last ten branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
349 </div>
501 </div>
350 <div class="table">
502 <div class="table">
351 <%include file='../branches/branches_data.html'/>
503 <%include file='../branches/branches_data.html'/>
352 ${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
504 ${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
353 </div>
505 </div>
354 </div>
506 </div>
355
507
356 </%def> No newline at end of file
508 </%def>
General Comments 0
You need to be logged in to leave comments. Login now