Show More
@@ -36,7 +36,7 b' BROKER_PASSWORD = "qweqwe"' | |||||
36 | CELERYD_CONCURRENCY = 2 |
|
36 | CELERYD_CONCURRENCY = 2 | |
37 | # CELERYD_LOG_FILE = "celeryd.log" |
|
37 | # CELERYD_LOG_FILE = "celeryd.log" | |
38 | CELERYD_LOG_LEVEL = "DEBUG" |
|
38 | CELERYD_LOG_LEVEL = "DEBUG" | |
39 |
CELERYD_MAX_TASKS_PER_CHILD = |
|
39 | CELERYD_MAX_TASKS_PER_CHILD = 3 | |
40 |
|
40 | |||
41 | #Tasks will never be sent to the queue, but executed locally instead. |
|
41 | #Tasks will never be sent to the queue, but executed locally instead. | |
42 | CELERY_ALWAYS_EAGER = False |
|
42 | CELERY_ALWAYS_EAGER = False |
@@ -35,7 +35,7 b' from datetime import datetime, timedelta' | |||||
35 | from time import mktime |
|
35 | from time import mktime | |
36 | import calendar |
|
36 | import calendar | |
37 | import logging |
|
37 | import logging | |
38 |
|
38 | import json | ||
39 | log = logging.getLogger(__name__) |
|
39 | log = logging.getLogger(__name__) | |
40 |
|
40 | |||
41 | class SummaryController(BaseController): |
|
41 | class SummaryController(BaseController): | |
@@ -79,18 +79,25 b' class SummaryController(BaseController):' | |||||
79 | c.ts_min = ts_min_m |
|
79 | c.ts_min = ts_min_m | |
80 | c.ts_max = ts_max_y |
|
80 | c.ts_max = ts_max_y | |
81 |
|
81 | |||
82 |
|
||||
83 | stats = self.sa.query(Statistics)\ |
|
82 | stats = self.sa.query(Statistics)\ | |
84 | .filter(Statistics.repository == c.repo_info.dbrepo)\ |
|
83 | .filter(Statistics.repository == c.repo_info.dbrepo)\ | |
85 | .scalar() |
|
84 | .scalar() | |
86 |
|
85 | |||
87 |
|
|
86 | ||
|
87 | if stats and stats.languages: | |||
|
88 | lang_stats = json.loads(stats.languages) | |||
88 | c.commit_data = stats.commit_activity |
|
89 | c.commit_data = stats.commit_activity | |
89 | c.overview_data = stats.commit_activity_combined |
|
90 | c.overview_data = stats.commit_activity_combined | |
|
91 | c.trending_languages = json.dumps(OrderedDict( | |||
|
92 | sorted(lang_stats.items(), reverse=True, | |||
|
93 | key=lambda k: k[1])[:2] | |||
|
94 | ) | |||
|
95 | ) | |||
|
96 | print c.trending_languages | |||
90 | else: |
|
97 | else: | |
91 | import json |
|
|||
92 | c.commit_data = json.dumps({}) |
|
98 | c.commit_data = json.dumps({}) | |
93 | c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 0] ]) |
|
99 | c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 0] ]) | |
|
100 | c.trending_languages = json.dumps({}) | |||
94 |
|
101 | |||
95 | return render('summary/summary.html') |
|
102 | return render('summary/summary.html') | |
96 |
|
103 |
@@ -42,7 +42,7 b' def locked_task(func):' | |||||
42 | log.info('running task with lockkey %s', lockkey) |
|
42 | log.info('running task with lockkey %s', lockkey) | |
43 | try: |
|
43 | try: | |
44 | l = DaemonLock(lockkey) |
|
44 | l = DaemonLock(lockkey) | |
45 |
|
|
45 | func(*fargs, **fkwargs) | |
46 | l.release() |
|
46 | l.release() | |
47 | except LockHeld: |
|
47 | except LockHeld: | |
48 | log.info('LockHeld') |
|
48 | log.info('LockHeld') |
@@ -1,16 +1,16 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 operator import itemgetter | |||
4 | from pylons.i18n.translation import _ |
|
5 | from pylons.i18n.translation import _ | |
5 | from pylons_app.lib.celerylib import run_task, locked_task |
|
6 | from pylons_app.lib.celerylib import run_task, locked_task | |
6 | from pylons_app.lib.helpers import person |
|
7 | from pylons_app.lib.helpers import person | |
7 | from pylons_app.lib.smtp_mailer import SmtpMailer |
|
8 | from pylons_app.lib.smtp_mailer import SmtpMailer | |
8 | from pylons_app.lib.utils import OrderedDict |
|
9 | from pylons_app.lib.utils import OrderedDict | |
9 | from operator import itemgetter |
|
10 | from time import mktime | |
10 | from vcs.backends.hg import MercurialRepository |
|
11 | from vcs.backends.hg import MercurialRepository | |
11 | from time import mktime |
|
12 | import json | |
12 | import traceback |
|
13 | import traceback | |
13 | import json |
|
|||
14 |
|
14 | |||
15 | __all__ = ['whoosh_index', 'get_commits_stats', |
|
15 | __all__ = ['whoosh_index', 'get_commits_stats', | |
16 | 'reset_user_password', 'send_email'] |
|
16 | 'reset_user_password', 'send_email'] | |
@@ -75,10 +75,10 b' def whoosh_index(repo_location, full_ind' | |||||
75 | @task |
|
75 | @task | |
76 | @locked_task |
|
76 | @locked_task | |
77 | def get_commits_stats(repo_name, ts_min_y, ts_max_y): |
|
77 | def get_commits_stats(repo_name, ts_min_y, ts_max_y): | |
78 | author_key_cleaner = lambda k: person(k).replace('"', "") #for js data compatibilty |
|
|||
79 |
|
||||
80 | from pylons_app.model.db import Statistics, Repository |
|
78 | from pylons_app.model.db import Statistics, Repository | |
81 | log = get_commits_stats.get_logger() |
|
79 | log = get_commits_stats.get_logger() | |
|
80 | author_key_cleaner = lambda k: person(k).replace('"', "") #for js data compatibilty | |||
|
81 | ||||
82 | commits_by_day_author_aggregate = {} |
|
82 | commits_by_day_author_aggregate = {} | |
83 | commits_by_day_aggregate = {} |
|
83 | commits_by_day_aggregate = {} | |
84 | repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '') |
|
84 | repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '') | |
@@ -99,7 +99,7 b' def get_commits_stats(repo_name, ts_min_' | |||||
99 | if cur_stats: |
|
99 | if cur_stats: | |
100 | last_rev = cur_stats.stat_on_revision |
|
100 | last_rev = cur_stats.stat_on_revision | |
101 |
|
101 | |||
102 | if last_rev == repo.revisions[-1]: |
|
102 | if last_rev == repo.revisions[-1] and len(repo.revisions) > 1: | |
103 | #pass silently without any work |
|
103 | #pass silently without any work | |
104 | return True |
|
104 | return True | |
105 |
|
105 | |||
@@ -109,6 +109,7 b' def get_commits_stats(repo_name, ts_min_' | |||||
109 | cur_stats.commit_activity_combined)) |
|
109 | cur_stats.commit_activity_combined)) | |
110 | commits_by_day_author_aggregate = json.loads(cur_stats.commit_activity) |
|
110 | commits_by_day_author_aggregate = json.loads(cur_stats.commit_activity) | |
111 |
|
111 | |||
|
112 | log.debug('starting parsing %s', parse_limit) | |||
112 | for cnt, rev in enumerate(repo.revisions[last_rev:]): |
|
113 | for cnt, rev in enumerate(repo.revisions[last_rev:]): | |
113 | last_cs = cs = repo.get_changeset(rev) |
|
114 | last_cs = cs = repo.get_changeset(rev) | |
114 | k = '%s-%s-%s' % (cs.date.timetuple()[0], cs.date.timetuple()[1], |
|
115 | k = '%s-%s-%s' % (cs.date.timetuple()[0], cs.date.timetuple()[1], | |
@@ -187,9 +188,16 b' def get_commits_stats(repo_name, ts_min_' | |||||
187 | stats = cur_stats if cur_stats else Statistics() |
|
188 | stats = cur_stats if cur_stats else Statistics() | |
188 | stats.commit_activity = json.dumps(commits_by_day_author_aggregate) |
|
189 | stats.commit_activity = json.dumps(commits_by_day_author_aggregate) | |
189 | stats.commit_activity_combined = json.dumps(overview_data) |
|
190 | stats.commit_activity_combined = json.dumps(overview_data) | |
|
191 | ||||
|
192 | log.debug('last revison %s', last_rev) | |||
|
193 | leftovers = len(repo.revisions[last_rev:]) | |||
|
194 | log.debug('revisions to parse %s', leftovers) | |||
|
195 | ||||
|
196 | if last_rev == 0 or leftovers < parse_limit: | |||
|
197 | stats.languages = json.dumps(__get_codes_stats(repo_name)) | |||
|
198 | ||||
190 | stats.repository = dbrepo |
|
199 | stats.repository = dbrepo | |
191 | stats.stat_on_revision = last_cs.revision |
|
200 | stats.stat_on_revision = last_cs.revision | |
192 | stats.languages = json.dumps({'_TOTAL_':0, '':0}) |
|
|||
193 |
|
201 | |||
194 | try: |
|
202 | try: | |
195 | sa.add(stats) |
|
203 | sa.add(stats) | |
@@ -198,8 +206,8 b' def get_commits_stats(repo_name, ts_min_' | |||||
198 | log.error(traceback.format_exc()) |
|
206 | log.error(traceback.format_exc()) | |
199 | sa.rollback() |
|
207 | sa.rollback() | |
200 | return False |
|
208 | return False | |
201 |
|
209 | if len(repo.revisions) > 1: | ||
202 | run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y) |
|
210 | run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y) | |
203 |
|
211 | |||
204 | return True |
|
212 | return True | |
205 |
|
213 | |||
@@ -259,3 +267,31 b' def send_email(recipients, subject, body' | |||||
259 | log.error(traceback.format_exc()) |
|
267 | log.error(traceback.format_exc()) | |
260 | return False |
|
268 | return False | |
261 | return True |
|
269 | return True | |
|
270 | ||||
|
271 | def __get_codes_stats(repo_name): | |||
|
272 | LANGUAGES_EXTENSIONS = ['action', 'adp', 'ashx', 'asmx', 'aspx', 'asx', 'axd', 'c', | |||
|
273 | 'cfg', 'cfm', 'cpp', 'cs', 'diff', 'do', 'el', 'erl', | |||
|
274 | 'h', 'java', 'js', 'jsp', 'jspx', 'lisp', | |||
|
275 | 'lua', 'm', 'mako', 'ml', 'pas', 'patch', 'php', 'php3', | |||
|
276 | 'php4', 'phtml', 'pm', 'py', 'rb', 'rst', 's', 'sh', | |||
|
277 | 'tpl', 'txt', 'vim', 'wss', 'xhtml', 'xml', 'xsl', 'xslt', | |||
|
278 | 'yaws'] | |||
|
279 | repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '') | |||
|
280 | repo = MercurialRepository(repos_path + repo_name) | |||
|
281 | ||||
|
282 | code_stats = {} | |||
|
283 | for topnode, dirs, files in repo.walk('/', 'tip'): | |||
|
284 | for f in files: | |||
|
285 | k = f.mimetype | |||
|
286 | if f.extension in LANGUAGES_EXTENSIONS: | |||
|
287 | if code_stats.has_key(k): | |||
|
288 | code_stats[k] += 1 | |||
|
289 | else: | |||
|
290 | code_stats[k] = 1 | |||
|
291 | ||||
|
292 | return code_stats or {} | |||
|
293 | ||||
|
294 | ||||
|
295 | ||||
|
296 | ||||
|
297 |
@@ -1,6 +1,8 b'' | |||||
1 | import os, time |
|
1 | import os, time | |
2 | import sys |
|
2 | import sys | |
3 | from warnings import warn |
|
3 | from warnings import warn | |
|
4 | from multiprocessing.util import Finalize | |||
|
5 | import errno | |||
4 |
|
6 | |||
5 | class LockHeld(Exception):pass |
|
7 | class LockHeld(Exception):pass | |
6 |
|
8 | |||
@@ -27,55 +29,67 b' class DaemonLock(object):' | |||||
27 | self.held = False |
|
29 | self.held = False | |
28 | #run the lock automatically ! |
|
30 | #run the lock automatically ! | |
29 | self.lock() |
|
31 | self.lock() | |
30 |
|
32 | self._finalize = Finalize(self, DaemonLock._on_finalize, | ||
31 | def __del__(self): |
|
33 | args=(self, debug), exitpriority=10) | |
32 | if self.held: |
|
|||
33 |
|
34 | |||
34 | # warn("use lock.release instead of del lock", |
|
35 | @staticmethod | |
35 | # category = DeprecationWarning, |
|
36 | def _on_finalize(lock, debug): | |
36 | # stacklevel = 2) |
|
37 | if lock.held: | |
37 |
|
38 | if debug: | ||
38 | # ensure the lock will be removed |
|
39 | print 'leck held finilazing and running lock.release()' | |
39 |
|
|
40 | lock.release() | |
40 |
|
41 | |||
41 |
|
42 | |||
42 | def lock(self): |
|
43 | def lock(self): | |
43 | """locking function, if lock is present it will raise LockHeld exception |
|
44 | """locking function, if lock is present it will raise LockHeld exception | |
44 | """ |
|
45 | """ | |
45 | lockname = '%s' % (os.getpid()) |
|
46 | lockname = '%s' % (os.getpid()) | |
46 |
|
47 | if self.debug: | ||
|
48 | print 'running lock' | |||
47 | self.trylock() |
|
49 | self.trylock() | |
48 | self.makelock(lockname, self.pidfile) |
|
50 | self.makelock(lockname, self.pidfile) | |
49 | return True |
|
51 | return True | |
50 |
|
52 | |||
51 | def trylock(self): |
|
53 | def trylock(self): | |
52 | running_pid = False |
|
54 | running_pid = False | |
|
55 | if self.debug: | |||
|
56 | print 'checking for already running process' | |||
53 | try: |
|
57 | try: | |
54 | pidfile = open(self.pidfile, "r") |
|
58 | pidfile = open(self.pidfile, "r") | |
55 | pidfile.seek(0) |
|
59 | pidfile.seek(0) | |
56 | running_pid = pidfile.readline() |
|
60 | running_pid = int(pidfile.readline()) | |
|
61 | ||||
|
62 | pidfile.close() | |||
|
63 | ||||
57 | if self.debug: |
|
64 | if self.debug: | |
58 | print 'lock file present running_pid: %s, checking for execution'\ |
|
65 | print 'lock file present running_pid: %s, checking for execution'\ | |
59 | % running_pid |
|
66 | % running_pid | |
60 | # Now we check the PID from lock file matches to the current |
|
67 | # Now we check the PID from lock file matches to the current | |
61 | # process PID |
|
68 | # process PID | |
62 | if running_pid: |
|
69 | if running_pid: | |
63 | if os.path.exists("/proc/%s" % running_pid): |
|
70 | try: | |
64 | print "You already have an instance of the program running" |
|
71 | os.kill(running_pid, 0) | |
65 | print "It is running as process %s" % running_pid |
|
72 | except OSError, exc: | |
66 | raise LockHeld |
|
73 | if exc.errno in (errno.ESRCH, errno.EPERM): | |
|
74 | print "Lock File is there but the program is not running" | |||
|
75 | print "Removing lock file for the: %s" % running_pid | |||
|
76 | self.release() | |||
|
77 | raise | |||
67 | else: |
|
78 | else: | |
68 |
|
|
79 | print "You already have an instance of the program running" | |
69 |
|
|
80 | print "It is running as process %s" % running_pid | |
70 |
|
|
81 | raise LockHeld() | |
|
82 | ||||
71 | except IOError, e: |
|
83 | except IOError, e: | |
72 | if e.errno != 2: |
|
84 | if e.errno != 2: | |
73 | raise |
|
85 | raise | |
74 |
|
86 | |||
75 |
|
||||
76 | def release(self): |
|
87 | def release(self): | |
77 | """releases the pid by removing the pidfile |
|
88 | """releases the pid by removing the pidfile | |
78 | """ |
|
89 | """ | |
|
90 | if self.debug: | |||
|
91 | print 'trying to release the pidlock' | |||
|
92 | ||||
79 | if self.callbackfn: |
|
93 | if self.callbackfn: | |
80 | #execute callback function on release |
|
94 | #execute callback function on release | |
81 | if self.debug: |
|
95 | if self.debug: |
@@ -2963,6 +2963,28 b' div.form div.fields div.buttons input' | |||||
2963 | /* ----------------------------------------------------------- |
|
2963 | /* ----------------------------------------------------------- | |
2964 | SUMMARY |
|
2964 | SUMMARY | |
2965 | ----------------------------------------------------------- */ |
|
2965 | ----------------------------------------------------------- */ | |
|
2966 | .trending_language_tbl, .trending_language_tbl td { | |||
|
2967 | margin: 0px !important; | |||
|
2968 | padding: 0px !important; | |||
|
2969 | border: 0 !important; | |||
|
2970 | ||||
|
2971 | } | |||
|
2972 | .trending_language{ | |||
|
2973 | -moz-border-radius-bottomright:4px; | |||
|
2974 | -moz-border-radius-topright:4px; | |||
|
2975 | border-bottom-right-radius: 4px 4px; | |||
|
2976 | border-top-right-radius: 4px 4px; | |||
|
2977 | background-color:#336699; | |||
|
2978 | color:#FFFFFF; | |||
|
2979 | display:block; | |||
|
2980 | min-width:20px; | |||
|
2981 | max-width:400px; | |||
|
2982 | padding:3px; | |||
|
2983 | text-decoration:none; | |||
|
2984 | height: 10px; | |||
|
2985 | margin-bottom: 4px; | |||
|
2986 | margin-left: 5px; | |||
|
2987 | } | |||
2966 |
|
2988 | |||
2967 | #clone_url{ |
|
2989 | #clone_url{ | |
2968 | border: none; |
|
2990 | border: none; |
@@ -90,7 +90,62 b' E.onDOMReady(function(e){' | |||||
90 | <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"/> | |
91 | </div> |
|
91 | </div> | |
92 | </div> |
|
92 | </div> | |
93 |
|
93 | |||
|
94 | <div class="field"> | |||
|
95 | <div class="label"> | |||
|
96 | <label>${_('Trending languages')}:</label> | |||
|
97 | </div> | |||
|
98 | <div class="input-short"> | |||
|
99 | <div id="lang_stats"> | |||
|
100 | ||||
|
101 | </div> | |||
|
102 | <script type="text/javascript"> | |||
|
103 | var data = ${c.trending_languages|n}; | |||
|
104 | var total = 0; | |||
|
105 | var no_data = true; | |||
|
106 | for (k in data){ | |||
|
107 | total += data[k]; | |||
|
108 | no_data = false; | |||
|
109 | } | |||
|
110 | var tbl = document.createElement('table'); | |||
|
111 | tbl.setAttribute('class','trending_language_tbl'); | |||
|
112 | for (k in data){ | |||
|
113 | var tr = document.createElement('tr'); | |||
|
114 | var percentage = Math.round((data[k]/total*100),2); | |||
|
115 | var value = data[k]; | |||
|
116 | var td1 = document.createElement('td'); | |||
|
117 | td1.width=150; | |||
|
118 | var trending_language_label = document.createElement('div'); | |||
|
119 | trending_language_label.innerHTML = k; | |||
|
120 | td1.appendChild(trending_language_label); | |||
|
121 | ||||
|
122 | var td2 = document.createElement('td'); | |||
|
123 | var trending_language = document.createElement('div'); | |||
|
124 | trending_language.title = k; | |||
|
125 | trending_language.innerHTML = "<b>"+value+" ${_('files')} - "+percentage+" %</b>"; | |||
|
126 | trending_language.setAttribute("class", 'trending_language'); | |||
|
127 | trending_language.style.width=percentage+"%"; | |||
|
128 | td2.appendChild(trending_language); | |||
|
129 | ||||
|
130 | tr.appendChild(td1); | |||
|
131 | tr.appendChild(td2); | |||
|
132 | tbl.appendChild(tr); | |||
|
133 | //YAHOO.util.Dom.get('lang_stats').appendChild(trending_language_label); | |||
|
134 | ||||
|
135 | } | |||
|
136 | if(no_data){ | |||
|
137 | var tr = document.createElement('tr'); | |||
|
138 | var td1 = document.createElement('td'); | |||
|
139 | td1.innerHTML = "${_('No data loaded yet...')}"; | |||
|
140 | tr.appendChild(td1); | |||
|
141 | tbl.appendChild(tr); | |||
|
142 | } | |||
|
143 | YAHOO.util.Dom.get('lang_stats').appendChild(tbl); | |||
|
144 | </script> | |||
|
145 | ||||
|
146 | </div> | |||
|
147 | </div> | |||
|
148 | ||||
94 | <div class="field"> |
|
149 | <div class="field"> | |
95 | <div class="label"> |
|
150 | <div class="label"> | |
96 | <label>${_('Download')}:</label> |
|
151 | <label>${_('Download')}:</label> |
General Comments 0
You need to be logged in to leave comments.
Login now