##// END OF EJS Templates
control mailer debug with the .ini file
marcink -
r1169:f6dca275 beta
parent child Browse files
Show More
@@ -1,409 +1,410
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.celerylib.tasks
3 rhodecode.lib.celerylib.tasks
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 RhodeCode task modules, containing all task that suppose to be run
6 RhodeCode task modules, containing all task that suppose to be run
7 by celery daemon
7 by celery daemon
8
8
9 :created_on: Oct 6, 2010
9 :created_on: Oct 6, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software; you can redistribute it and/or
14 # This program is free software; you can redistribute it and/or
15 # modify it under the terms of the GNU General Public License
15 # modify it under the terms of the GNU General Public License
16 # as published by the Free Software Foundation; version 2
16 # as published by the Free Software Foundation; version 2
17 # of the License or (at your opinion) any later version of the license.
17 # of the License or (at your opinion) any later version of the license.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program; if not, write to the Free Software
25 # along with this program; if not, write to the Free Software
26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
27 # MA 02110-1301, USA.
27 # MA 02110-1301, USA.
28 from celery.decorators import task
28 from celery.decorators import task
29
29
30 import os
30 import os
31 import traceback
31 import traceback
32 import logging
32 import logging
33
33
34 from time import mktime
34 from time import mktime
35 from operator import itemgetter
35 from operator import itemgetter
36
36
37 from pylons import config
37 from pylons import config
38 from pylons.i18n.translation import _
38 from pylons.i18n.translation import _
39
39
40 from rhodecode.lib.celerylib import run_task, locked_task, str2bool
40 from rhodecode.lib.celerylib import run_task, locked_task, str2bool
41 from rhodecode.lib.helpers import person
41 from rhodecode.lib.helpers import person
42 from rhodecode.lib.smtp_mailer import SmtpMailer
42 from rhodecode.lib.smtp_mailer import SmtpMailer
43 from rhodecode.lib.utils import OrderedDict, add_cache
43 from rhodecode.lib.utils import OrderedDict, add_cache
44 from rhodecode.model import init_model
44 from rhodecode.model import init_model
45 from rhodecode.model import meta
45 from rhodecode.model import meta
46 from rhodecode.model.db import RhodeCodeUi
46 from rhodecode.model.db import RhodeCodeUi
47
47
48 from vcs.backends import get_repo
48 from vcs.backends import get_repo
49
49
50 from sqlalchemy import engine_from_config
50 from sqlalchemy import engine_from_config
51
51
52 add_cache(config)
52 add_cache(config)
53
53
54 try:
54 try:
55 import json
55 import json
56 except ImportError:
56 except ImportError:
57 #python 2.5 compatibility
57 #python 2.5 compatibility
58 import simplejson as json
58 import simplejson as json
59
59
60 __all__ = ['whoosh_index', 'get_commits_stats',
60 __all__ = ['whoosh_index', 'get_commits_stats',
61 'reset_user_password', 'send_email']
61 'reset_user_password', 'send_email']
62
62
63 CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
63 CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
64
64
65 def get_session():
65 def get_session():
66 if CELERY_ON:
66 if CELERY_ON:
67 engine = engine_from_config(config, 'sqlalchemy.db1.')
67 engine = engine_from_config(config, 'sqlalchemy.db1.')
68 init_model(engine)
68 init_model(engine)
69 sa = meta.Session()
69 sa = meta.Session()
70 return sa
70 return sa
71
71
72 def get_repos_path():
72 def get_repos_path():
73 sa = get_session()
73 sa = get_session()
74 q = sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
74 q = sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
75 return q.ui_value
75 return q.ui_value
76
76
77 @task(ignore_result=True)
77 @task(ignore_result=True)
78 @locked_task
78 @locked_task
79 def whoosh_index(repo_location, full_index):
79 def whoosh_index(repo_location, full_index):
80 #log = whoosh_index.get_logger()
80 #log = whoosh_index.get_logger()
81 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
81 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
82 index_location = config['index_dir']
82 index_location = config['index_dir']
83 WhooshIndexingDaemon(index_location=index_location,
83 WhooshIndexingDaemon(index_location=index_location,
84 repo_location=repo_location, sa=get_session())\
84 repo_location=repo_location, sa=get_session())\
85 .run(full_index=full_index)
85 .run(full_index=full_index)
86
86
87 @task(ignore_result=True)
87 @task(ignore_result=True)
88 @locked_task
88 @locked_task
89 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
89 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
90 try:
90 try:
91 log = get_commits_stats.get_logger()
91 log = get_commits_stats.get_logger()
92 except:
92 except:
93 log = logging.getLogger(__name__)
93 log = logging.getLogger(__name__)
94
94
95 from rhodecode.model.db import Statistics, Repository
95 from rhodecode.model.db import Statistics, Repository
96
96
97 #for js data compatibilty
97 #for js data compatibilty
98 author_key_cleaner = lambda k: person(k).replace('"', "")
98 author_key_cleaner = lambda k: person(k).replace('"', "")
99
99
100 commits_by_day_author_aggregate = {}
100 commits_by_day_author_aggregate = {}
101 commits_by_day_aggregate = {}
101 commits_by_day_aggregate = {}
102 repos_path = get_repos_path()
102 repos_path = get_repos_path()
103 p = os.path.join(repos_path, repo_name)
103 p = os.path.join(repos_path, repo_name)
104 repo = get_repo(p)
104 repo = get_repo(p)
105
105
106 skip_date_limit = True
106 skip_date_limit = True
107 parse_limit = int(config['app_conf'].get('commit_parse_limit'))
107 parse_limit = int(config['app_conf'].get('commit_parse_limit'))
108 last_rev = 0
108 last_rev = 0
109 last_cs = None
109 last_cs = None
110 timegetter = itemgetter('time')
110 timegetter = itemgetter('time')
111
111
112 sa = get_session()
112 sa = get_session()
113
113
114 dbrepo = sa.query(Repository)\
114 dbrepo = sa.query(Repository)\
115 .filter(Repository.repo_name == repo_name).scalar()
115 .filter(Repository.repo_name == repo_name).scalar()
116 cur_stats = sa.query(Statistics)\
116 cur_stats = sa.query(Statistics)\
117 .filter(Statistics.repository == dbrepo).scalar()
117 .filter(Statistics.repository == dbrepo).scalar()
118
118
119 if cur_stats is not None:
119 if cur_stats is not None:
120 last_rev = cur_stats.stat_on_revision
120 last_rev = cur_stats.stat_on_revision
121
121
122 #return if repo is empty
122 #return if repo is empty
123 if not repo.revisions:
123 if not repo.revisions:
124 return True
124 return True
125
125
126 if last_rev == repo.get_changeset().revision and len(repo.revisions) > 1:
126 if last_rev == repo.get_changeset().revision and len(repo.revisions) > 1:
127 #pass silently without any work if we're not on first revision or
127 #pass silently without any work if we're not on first revision or
128 #current state of parsing revision(from db marker) is the last revision
128 #current state of parsing revision(from db marker) is the last revision
129 return True
129 return True
130
130
131 if cur_stats:
131 if cur_stats:
132 commits_by_day_aggregate = OrderedDict(
132 commits_by_day_aggregate = OrderedDict(
133 json.loads(
133 json.loads(
134 cur_stats.commit_activity_combined))
134 cur_stats.commit_activity_combined))
135 commits_by_day_author_aggregate = json.loads(cur_stats.commit_activity)
135 commits_by_day_author_aggregate = json.loads(cur_stats.commit_activity)
136
136
137 log.debug('starting parsing %s', parse_limit)
137 log.debug('starting parsing %s', parse_limit)
138 lmktime = mktime
138 lmktime = mktime
139
139
140 last_rev = last_rev + 1 if last_rev > 0 else last_rev
140 last_rev = last_rev + 1 if last_rev > 0 else last_rev
141
141
142 for cs in repo[last_rev:last_rev + parse_limit]:
142 for cs in repo[last_rev:last_rev + parse_limit]:
143 last_cs = cs #remember last parsed changeset
143 last_cs = cs #remember last parsed changeset
144 k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1],
144 k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1],
145 cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0])
145 cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0])
146
146
147 if commits_by_day_author_aggregate.has_key(author_key_cleaner(cs.author)):
147 if commits_by_day_author_aggregate.has_key(author_key_cleaner(cs.author)):
148 try:
148 try:
149 l = [timegetter(x) for x in commits_by_day_author_aggregate\
149 l = [timegetter(x) for x in commits_by_day_author_aggregate\
150 [author_key_cleaner(cs.author)]['data']]
150 [author_key_cleaner(cs.author)]['data']]
151 time_pos = l.index(k)
151 time_pos = l.index(k)
152 except ValueError:
152 except ValueError:
153 time_pos = False
153 time_pos = False
154
154
155 if time_pos >= 0 and time_pos is not False:
155 if time_pos >= 0 and time_pos is not False:
156
156
157 datadict = commits_by_day_author_aggregate\
157 datadict = commits_by_day_author_aggregate\
158 [author_key_cleaner(cs.author)]['data'][time_pos]
158 [author_key_cleaner(cs.author)]['data'][time_pos]
159
159
160 datadict["commits"] += 1
160 datadict["commits"] += 1
161 datadict["added"] += len(cs.added)
161 datadict["added"] += len(cs.added)
162 datadict["changed"] += len(cs.changed)
162 datadict["changed"] += len(cs.changed)
163 datadict["removed"] += len(cs.removed)
163 datadict["removed"] += len(cs.removed)
164
164
165 else:
165 else:
166 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
166 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
167
167
168 datadict = {"time":k,
168 datadict = {"time":k,
169 "commits":1,
169 "commits":1,
170 "added":len(cs.added),
170 "added":len(cs.added),
171 "changed":len(cs.changed),
171 "changed":len(cs.changed),
172 "removed":len(cs.removed),
172 "removed":len(cs.removed),
173 }
173 }
174 commits_by_day_author_aggregate\
174 commits_by_day_author_aggregate\
175 [author_key_cleaner(cs.author)]['data'].append(datadict)
175 [author_key_cleaner(cs.author)]['data'].append(datadict)
176
176
177 else:
177 else:
178 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
178 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
179 commits_by_day_author_aggregate[author_key_cleaner(cs.author)] = {
179 commits_by_day_author_aggregate[author_key_cleaner(cs.author)] = {
180 "label":author_key_cleaner(cs.author),
180 "label":author_key_cleaner(cs.author),
181 "data":[{"time":k,
181 "data":[{"time":k,
182 "commits":1,
182 "commits":1,
183 "added":len(cs.added),
183 "added":len(cs.added),
184 "changed":len(cs.changed),
184 "changed":len(cs.changed),
185 "removed":len(cs.removed),
185 "removed":len(cs.removed),
186 }],
186 }],
187 "schema":["commits"],
187 "schema":["commits"],
188 }
188 }
189
189
190 #gather all data by day
190 #gather all data by day
191 if commits_by_day_aggregate.has_key(k):
191 if commits_by_day_aggregate.has_key(k):
192 commits_by_day_aggregate[k] += 1
192 commits_by_day_aggregate[k] += 1
193 else:
193 else:
194 commits_by_day_aggregate[k] = 1
194 commits_by_day_aggregate[k] = 1
195
195
196 overview_data = sorted(commits_by_day_aggregate.items(), key=itemgetter(0))
196 overview_data = sorted(commits_by_day_aggregate.items(), key=itemgetter(0))
197 if not commits_by_day_author_aggregate:
197 if not commits_by_day_author_aggregate:
198 commits_by_day_author_aggregate[author_key_cleaner(repo.contact)] = {
198 commits_by_day_author_aggregate[author_key_cleaner(repo.contact)] = {
199 "label":author_key_cleaner(repo.contact),
199 "label":author_key_cleaner(repo.contact),
200 "data":[0, 1],
200 "data":[0, 1],
201 "schema":["commits"],
201 "schema":["commits"],
202 }
202 }
203
203
204 stats = cur_stats if cur_stats else Statistics()
204 stats = cur_stats if cur_stats else Statistics()
205 stats.commit_activity = json.dumps(commits_by_day_author_aggregate)
205 stats.commit_activity = json.dumps(commits_by_day_author_aggregate)
206 stats.commit_activity_combined = json.dumps(overview_data)
206 stats.commit_activity_combined = json.dumps(overview_data)
207
207
208 log.debug('last revison %s', last_rev)
208 log.debug('last revison %s', last_rev)
209 leftovers = len(repo.revisions[last_rev:])
209 leftovers = len(repo.revisions[last_rev:])
210 log.debug('revisions to parse %s', leftovers)
210 log.debug('revisions to parse %s', leftovers)
211
211
212 if last_rev == 0 or leftovers < parse_limit:
212 if last_rev == 0 or leftovers < parse_limit:
213 log.debug('getting code trending stats')
213 log.debug('getting code trending stats')
214 stats.languages = json.dumps(__get_codes_stats(repo_name))
214 stats.languages = json.dumps(__get_codes_stats(repo_name))
215
215
216 try:
216 try:
217 stats.repository = dbrepo
217 stats.repository = dbrepo
218 stats.stat_on_revision = last_cs.revision if last_cs else 0
218 stats.stat_on_revision = last_cs.revision if last_cs else 0
219 sa.add(stats)
219 sa.add(stats)
220 sa.commit()
220 sa.commit()
221 except:
221 except:
222 log.error(traceback.format_exc())
222 log.error(traceback.format_exc())
223 sa.rollback()
223 sa.rollback()
224 return False
224 return False
225 if len(repo.revisions) > 1:
225 if len(repo.revisions) > 1:
226 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
226 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
227
227
228 return True
228 return True
229
229
230 @task(ignore_result=True)
230 @task(ignore_result=True)
231 def reset_user_password(user_email):
231 def reset_user_password(user_email):
232 try:
232 try:
233 log = reset_user_password.get_logger()
233 log = reset_user_password.get_logger()
234 except:
234 except:
235 log = logging.getLogger(__name__)
235 log = logging.getLogger(__name__)
236
236
237 from rhodecode.lib import auth
237 from rhodecode.lib import auth
238 from rhodecode.model.db import User
238 from rhodecode.model.db import User
239
239
240 try:
240 try:
241 try:
241 try:
242 sa = get_session()
242 sa = get_session()
243 user = sa.query(User).filter(User.email == user_email).scalar()
243 user = sa.query(User).filter(User.email == user_email).scalar()
244 new_passwd = auth.PasswordGenerator().gen_password(8,
244 new_passwd = auth.PasswordGenerator().gen_password(8,
245 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
245 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
246 if user:
246 if user:
247 user.password = auth.get_crypt_password(new_passwd)
247 user.password = auth.get_crypt_password(new_passwd)
248 user.api_key = auth.generate_api_key(user.username)
248 user.api_key = auth.generate_api_key(user.username)
249 sa.add(user)
249 sa.add(user)
250 sa.commit()
250 sa.commit()
251 log.info('change password for %s', user_email)
251 log.info('change password for %s', user_email)
252 if new_passwd is None:
252 if new_passwd is None:
253 raise Exception('unable to generate new password')
253 raise Exception('unable to generate new password')
254
254
255 except:
255 except:
256 log.error(traceback.format_exc())
256 log.error(traceback.format_exc())
257 sa.rollback()
257 sa.rollback()
258
258
259 run_task(send_email, user_email,
259 run_task(send_email, user_email,
260 "Your new rhodecode password",
260 "Your new rhodecode password",
261 'Your new rhodecode password:%s' % (new_passwd))
261 'Your new rhodecode password:%s' % (new_passwd))
262 log.info('send new password mail to %s', user_email)
262 log.info('send new password mail to %s', user_email)
263
263
264
264
265 except:
265 except:
266 log.error('Failed to update user password')
266 log.error('Failed to update user password')
267 log.error(traceback.format_exc())
267 log.error(traceback.format_exc())
268
268
269 return True
269 return True
270
270
271 @task(ignore_result=True)
271 @task(ignore_result=True)
272 def send_email(recipients, subject, body):
272 def send_email(recipients, subject, body):
273 """
273 """
274 Sends an email with defined parameters from the .ini files.
274 Sends an email with defined parameters from the .ini files.
275
275
276
276
277 :param recipients: list of recipients, it this is empty the defined email
277 :param recipients: list of recipients, it this is empty the defined email
278 address from field 'email_to' is used instead
278 address from field 'email_to' is used instead
279 :param subject: subject of the mail
279 :param subject: subject of the mail
280 :param body: body of the mail
280 :param body: body of the mail
281 """
281 """
282 try:
282 try:
283 log = send_email.get_logger()
283 log = send_email.get_logger()
284 except:
284 except:
285 log = logging.getLogger(__name__)
285 log = logging.getLogger(__name__)
286
286
287 email_config = config
287 email_config = config
288
288
289 if not recipients:
289 if not recipients:
290 recipients = [email_config.get('email_to')]
290 recipients = [email_config.get('email_to')]
291
291
292 mail_from = email_config.get('app_email_from')
292 mail_from = email_config.get('app_email_from')
293 user = email_config.get('smtp_username')
293 user = email_config.get('smtp_username')
294 passwd = email_config.get('smtp_password')
294 passwd = email_config.get('smtp_password')
295 mail_server = email_config.get('smtp_server')
295 mail_server = email_config.get('smtp_server')
296 mail_port = email_config.get('smtp_port')
296 mail_port = email_config.get('smtp_port')
297 tls = str2bool(email_config.get('smtp_use_tls'))
297 tls = str2bool(email_config.get('smtp_use_tls'))
298 ssl = str2bool(email_config.get('smtp_use_ssl'))
298 ssl = str2bool(email_config.get('smtp_use_ssl'))
299 debug = str2bool(config.get('debug'))
299
300
300 try:
301 try:
301 m = SmtpMailer(mail_from, user, passwd, mail_server,
302 m = SmtpMailer(mail_from, user, passwd, mail_server,
302 mail_port, ssl, tls)
303 mail_port, ssl, tls, debug=debug)
303 m.send(recipients, subject, body)
304 m.send(recipients, subject, body)
304 except:
305 except:
305 log.error('Mail sending failed')
306 log.error('Mail sending failed')
306 log.error(traceback.format_exc())
307 log.error(traceback.format_exc())
307 return False
308 return False
308 return True
309 return True
309
310
310 @task(ignore_result=True)
311 @task(ignore_result=True)
311 def create_repo_fork(form_data, cur_user):
312 def create_repo_fork(form_data, cur_user):
312 try:
313 try:
313 log = create_repo_fork.get_logger()
314 log = create_repo_fork.get_logger()
314 except:
315 except:
315 log = logging.getLogger(__name__)
316 log = logging.getLogger(__name__)
316
317
317 from rhodecode.model.repo import RepoModel
318 from rhodecode.model.repo import RepoModel
318 from vcs import get_backend
319 from vcs import get_backend
319
320
320 repo_model = RepoModel(get_session())
321 repo_model = RepoModel(get_session())
321 repo_model.create(form_data, cur_user, just_db=True, fork=True)
322 repo_model.create(form_data, cur_user, just_db=True, fork=True)
322 repo_name = form_data['repo_name']
323 repo_name = form_data['repo_name']
323 repos_path = get_repos_path()
324 repos_path = get_repos_path()
324 repo_path = os.path.join(repos_path, repo_name)
325 repo_path = os.path.join(repos_path, repo_name)
325 repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
326 repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
326 alias = form_data['repo_type']
327 alias = form_data['repo_type']
327
328
328 log.info('creating repo fork %s as %s', repo_name, repo_path)
329 log.info('creating repo fork %s as %s', repo_name, repo_path)
329 backend = get_backend(alias)
330 backend = get_backend(alias)
330 backend(str(repo_fork_path), create=True, src_url=str(repo_path))
331 backend(str(repo_fork_path), create=True, src_url=str(repo_path))
331
332
332 def __get_codes_stats(repo_name):
333 def __get_codes_stats(repo_name):
333 LANGUAGES_EXTENSIONS_MAP = {'scm': 'Scheme', 'asmx': 'VbNetAspx', 'Rout':
334 LANGUAGES_EXTENSIONS_MAP = {'scm': 'Scheme', 'asmx': 'VbNetAspx', 'Rout':
334 'RConsole', 'rest': 'Rst', 'abap': 'ABAP', 'go': 'Go', 'phtml': 'HtmlPhp',
335 'RConsole', 'rest': 'Rst', 'abap': 'ABAP', 'go': 'Go', 'phtml': 'HtmlPhp',
335 'ns2': 'Newspeak', 'xml': 'EvoqueXml', 'sh-session': 'BashSession', 'ads':
336 'ns2': 'Newspeak', 'xml': 'EvoqueXml', 'sh-session': 'BashSession', 'ads':
336 'Ada', 'clj': 'Clojure', 'll': 'Llvm', 'ebuild': 'Bash', 'adb': 'Ada',
337 'Ada', 'clj': 'Clojure', 'll': 'Llvm', 'ebuild': 'Bash', 'adb': 'Ada',
337 'ada': 'Ada', 'c++-objdump': 'CppObjdump', 'aspx':
338 'ada': 'Ada', 'c++-objdump': 'CppObjdump', 'aspx':
338 'VbNetAspx', 'ksh': 'Bash', 'coffee': 'CoffeeScript', 'vert': 'GLShader',
339 'VbNetAspx', 'ksh': 'Bash', 'coffee': 'CoffeeScript', 'vert': 'GLShader',
339 'Makefile.*': 'Makefile', 'di': 'D', 'dpatch': 'DarcsPatch', 'rake':
340 'Makefile.*': 'Makefile', 'di': 'D', 'dpatch': 'DarcsPatch', 'rake':
340 'Ruby', 'moo': 'MOOCode', 'erl-sh': 'ErlangShell', 'geo': 'GLShader',
341 'Ruby', 'moo': 'MOOCode', 'erl-sh': 'ErlangShell', 'geo': 'GLShader',
341 'pov': 'Povray', 'bas': 'VbNet', 'bat': 'Batch', 'd': 'D', 'lisp':
342 'pov': 'Povray', 'bas': 'VbNet', 'bat': 'Batch', 'd': 'D', 'lisp':
342 'CommonLisp', 'h': 'C', 'rbx': 'Ruby', 'tcl': 'Tcl', 'c++': 'Cpp', 'md':
343 'CommonLisp', 'h': 'C', 'rbx': 'Ruby', 'tcl': 'Tcl', 'c++': 'Cpp', 'md':
343 'MiniD', '.vimrc': 'Vim', 'xsd': 'Xml', 'ml': 'Ocaml', 'el': 'CommonLisp',
344 'MiniD', '.vimrc': 'Vim', 'xsd': 'Xml', 'ml': 'Ocaml', 'el': 'CommonLisp',
344 'befunge': 'Befunge', 'xsl': 'Xslt', 'pyx': 'Cython', 'cfm':
345 'befunge': 'Befunge', 'xsl': 'Xslt', 'pyx': 'Cython', 'cfm':
345 'ColdfusionHtml', 'evoque': 'Evoque', 'cfg': 'Ini', 'htm': 'Html',
346 'ColdfusionHtml', 'evoque': 'Evoque', 'cfg': 'Ini', 'htm': 'Html',
346 'Makefile': 'Makefile', 'cfc': 'ColdfusionHtml', 'tex': 'Tex', 'cs':
347 'Makefile': 'Makefile', 'cfc': 'ColdfusionHtml', 'tex': 'Tex', 'cs':
347 'CSharp', 'mxml': 'Mxml', 'patch': 'Diff', 'apache.conf': 'ApacheConf',
348 'CSharp', 'mxml': 'Mxml', 'patch': 'Diff', 'apache.conf': 'ApacheConf',
348 'scala': 'Scala', 'applescript': 'AppleScript', 'GNUmakefile': 'Makefile',
349 'scala': 'Scala', 'applescript': 'AppleScript', 'GNUmakefile': 'Makefile',
349 'c-objdump': 'CObjdump', 'lua': 'Lua', 'apache2.conf': 'ApacheConf', 'rb':
350 'c-objdump': 'CObjdump', 'lua': 'Lua', 'apache2.conf': 'ApacheConf', 'rb':
350 'Ruby', 'gemspec': 'Ruby', 'rl': 'RagelObjectiveC', 'vala': 'Vala', 'tmpl':
351 'Ruby', 'gemspec': 'Ruby', 'rl': 'RagelObjectiveC', 'vala': 'Vala', 'tmpl':
351 'Cheetah', 'bf': 'Brainfuck', 'plt': 'Gnuplot', 'G': 'AntlrRuby', 'xslt':
352 'Cheetah', 'bf': 'Brainfuck', 'plt': 'Gnuplot', 'G': 'AntlrRuby', 'xslt':
352 'Xslt', 'flxh': 'Felix', 'asax': 'VbNetAspx', 'Rakefile': 'Ruby', 'S': 'S',
353 'Xslt', 'flxh': 'Felix', 'asax': 'VbNetAspx', 'Rakefile': 'Ruby', 'S': 'S',
353 'wsdl': 'Xml', 'js': 'Javascript', 'autodelegate': 'Myghty', 'properties':
354 'wsdl': 'Xml', 'js': 'Javascript', 'autodelegate': 'Myghty', 'properties':
354 'Ini', 'bash': 'Bash', 'c': 'C', 'g': 'AntlrRuby', 'r3': 'Rebol', 's':
355 'Ini', 'bash': 'Bash', 'c': 'C', 'g': 'AntlrRuby', 'r3': 'Rebol', 's':
355 'Gas', 'ashx': 'VbNetAspx', 'cxx': 'Cpp', 'boo': 'Boo', 'prolog': 'Prolog',
356 'Gas', 'ashx': 'VbNetAspx', 'cxx': 'Cpp', 'boo': 'Boo', 'prolog': 'Prolog',
356 'sqlite3-console': 'SqliteConsole', 'cl': 'CommonLisp', 'cc': 'Cpp', 'pot':
357 'sqlite3-console': 'SqliteConsole', 'cl': 'CommonLisp', 'cc': 'Cpp', 'pot':
357 'Gettext', 'vim': 'Vim', 'pxi': 'Cython', 'yaml': 'Yaml', 'SConstruct':
358 'Gettext', 'vim': 'Vim', 'pxi': 'Cython', 'yaml': 'Yaml', 'SConstruct':
358 'Python', 'diff': 'Diff', 'txt': 'Text', 'cw': 'Redcode', 'pxd': 'Cython',
359 'Python', 'diff': 'Diff', 'txt': 'Text', 'cw': 'Redcode', 'pxd': 'Cython',
359 'plot': 'Gnuplot', 'java': 'Java', 'hrl': 'Erlang', 'py': 'Python',
360 'plot': 'Gnuplot', 'java': 'Java', 'hrl': 'Erlang', 'py': 'Python',
360 'makefile': 'Makefile', 'squid.conf': 'SquidConf', 'asm': 'Nasm', 'toc':
361 'makefile': 'Makefile', 'squid.conf': 'SquidConf', 'asm': 'Nasm', 'toc':
361 'Tex', 'kid': 'Genshi', 'rhtml': 'Rhtml', 'po': 'Gettext', 'pl': 'Prolog',
362 'Tex', 'kid': 'Genshi', 'rhtml': 'Rhtml', 'po': 'Gettext', 'pl': 'Prolog',
362 'pm': 'Perl', 'hx': 'Haxe', 'ascx': 'VbNetAspx', 'ooc': 'Ooc', 'asy':
363 'pm': 'Perl', 'hx': 'Haxe', 'ascx': 'VbNetAspx', 'ooc': 'Ooc', 'asy':
363 'Asymptote', 'hs': 'Haskell', 'SConscript': 'Python', 'pytb':
364 'Asymptote', 'hs': 'Haskell', 'SConscript': 'Python', 'pytb':
364 'PythonTraceback', 'myt': 'Myghty', 'hh': 'Cpp', 'R': 'S', 'aux': 'Tex',
365 'PythonTraceback', 'myt': 'Myghty', 'hh': 'Cpp', 'R': 'S', 'aux': 'Tex',
365 'rst': 'Rst', 'cpp-objdump': 'CppObjdump', 'lgt': 'Logtalk', 'rss': 'Xml',
366 'rst': 'Rst', 'cpp-objdump': 'CppObjdump', 'lgt': 'Logtalk', 'rss': 'Xml',
366 'flx': 'Felix', 'b': 'Brainfuck', 'f': 'Fortran', 'rbw': 'Ruby',
367 'flx': 'Felix', 'b': 'Brainfuck', 'f': 'Fortran', 'rbw': 'Ruby',
367 '.htaccess': 'ApacheConf', 'cxx-objdump': 'CppObjdump', 'j': 'ObjectiveJ',
368 '.htaccess': 'ApacheConf', 'cxx-objdump': 'CppObjdump', 'j': 'ObjectiveJ',
368 'mll': 'Ocaml', 'yml': 'Yaml', 'mu': 'MuPAD', 'r': 'Rebol', 'ASM': 'Nasm',
369 'mll': 'Ocaml', 'yml': 'Yaml', 'mu': 'MuPAD', 'r': 'Rebol', 'ASM': 'Nasm',
369 'erl': 'Erlang', 'mly': 'Ocaml', 'mo': 'Modelica', 'def': 'Modula2', 'ini':
370 'erl': 'Erlang', 'mly': 'Ocaml', 'mo': 'Modelica', 'def': 'Modula2', 'ini':
370 'Ini', 'control': 'DebianControl', 'vb': 'VbNet', 'vapi': 'Vala', 'pro':
371 'Ini', 'control': 'DebianControl', 'vb': 'VbNet', 'vapi': 'Vala', 'pro':
371 'Prolog', 'spt': 'Cheetah', 'mli': 'Ocaml', 'as': 'ActionScript3', 'cmd':
372 'Prolog', 'spt': 'Cheetah', 'mli': 'Ocaml', 'as': 'ActionScript3', 'cmd':
372 'Batch', 'cpp': 'Cpp', 'io': 'Io', 'tac': 'Python', 'haml': 'Haml', 'rkt':
373 'Batch', 'cpp': 'Cpp', 'io': 'Io', 'tac': 'Python', 'haml': 'Haml', 'rkt':
373 'Racket', 'st':'Smalltalk', 'inc': 'Povray', 'pas': 'Delphi', 'cmake':
374 'Racket', 'st':'Smalltalk', 'inc': 'Povray', 'pas': 'Delphi', 'cmake':
374 'CMake', 'csh':'Tcsh', 'hpp': 'Cpp', 'feature': 'Gherkin', 'html': 'Html',
375 'CMake', 'csh':'Tcsh', 'hpp': 'Cpp', 'feature': 'Gherkin', 'html': 'Html',
375 'php':'Php', 'php3':'Php', 'php4':'Php', 'php5':'Php', 'xhtml': 'Html',
376 'php':'Php', 'php3':'Php', 'php4':'Php', 'php5':'Php', 'xhtml': 'Html',
376 'hxx': 'Cpp', 'eclass': 'Bash', 'css': 'Css',
377 'hxx': 'Cpp', 'eclass': 'Bash', 'css': 'Css',
377 'frag': 'GLShader', 'd-objdump': 'DObjdump', 'weechatlog': 'IrcLogs',
378 'frag': 'GLShader', 'd-objdump': 'DObjdump', 'weechatlog': 'IrcLogs',
378 'tcsh': 'Tcsh', 'objdump': 'Objdump', 'pyw': 'Python', 'h++': 'Cpp',
379 'tcsh': 'Tcsh', 'objdump': 'Objdump', 'pyw': 'Python', 'h++': 'Cpp',
379 'py3tb': 'Python3Traceback', 'jsp': 'Jsp', 'sql': 'Sql', 'mak': 'Makefile',
380 'py3tb': 'Python3Traceback', 'jsp': 'Jsp', 'sql': 'Sql', 'mak': 'Makefile',
380 'php': 'Php', 'mao': 'Mako', 'man': 'Groff', 'dylan': 'Dylan', 'sass':
381 'php': 'Php', 'mao': 'Mako', 'man': 'Groff', 'dylan': 'Dylan', 'sass':
381 'Sass', 'cfml': 'ColdfusionHtml', 'darcspatch': 'DarcsPatch', 'tpl':
382 'Sass', 'cfml': 'ColdfusionHtml', 'darcspatch': 'DarcsPatch', 'tpl':
382 'Smarty', 'm': 'ObjectiveC', 'f90': 'Fortran', 'mod': 'Modula2', 'sh':
383 'Smarty', 'm': 'ObjectiveC', 'f90': 'Fortran', 'mod': 'Modula2', 'sh':
383 'Bash', 'lhs': 'LiterateHaskell', 'sources.list': 'SourcesList', 'axd':
384 'Bash', 'lhs': 'LiterateHaskell', 'sources.list': 'SourcesList', 'axd':
384 'VbNetAspx', 'sc': 'Python'}
385 'VbNetAspx', 'sc': 'Python'}
385
386
386 repos_path = get_repos_path()
387 repos_path = get_repos_path()
387 p = os.path.join(repos_path, repo_name)
388 p = os.path.join(repos_path, repo_name)
388 repo = get_repo(p)
389 repo = get_repo(p)
389 tip = repo.get_changeset()
390 tip = repo.get_changeset()
390 code_stats = {}
391 code_stats = {}
391
392
392 def aggregate(cs):
393 def aggregate(cs):
393 for f in cs[2]:
394 for f in cs[2]:
394 ext = f.extension
395 ext = f.extension
395 key = LANGUAGES_EXTENSIONS_MAP.get(ext, ext)
396 key = LANGUAGES_EXTENSIONS_MAP.get(ext, ext)
396 key = key or ext
397 key = key or ext
397 if ext in LANGUAGES_EXTENSIONS_MAP.keys() and not f.is_binary:
398 if ext in LANGUAGES_EXTENSIONS_MAP.keys() and not f.is_binary:
398 if code_stats.has_key(key):
399 if code_stats.has_key(key):
399 code_stats[key] += 1
400 code_stats[key] += 1
400 else:
401 else:
401 code_stats[key] = 1
402 code_stats[key] = 1
402
403
403 map(aggregate, tip.walk('/'))
404 map(aggregate, tip.walk('/'))
404
405
405 return code_stats or {}
406 return code_stats or {}
406
407
407
408
408
409
409
410
@@ -1,143 +1,143
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.smtp_mailer
3 rhodecode.lib.smtp_mailer
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Simple smtp mailer used in RhodeCode
6 Simple smtp mailer used in RhodeCode
7
7
8 :created_on: Sep 13, 2010
8 :created_on: Sep 13, 2010
9 :copyright: (c) 2011 by marcink.
9 :copyright: (c) 2011 by marcink.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
11 """
11 """
12
12
13 import logging
13 import logging
14 import smtplib
14 import smtplib
15 import mimetypes
15 import mimetypes
16 from socket import sslerror
16 from socket import sslerror
17
17
18 from email.mime.multipart import MIMEMultipart
18 from email.mime.multipart import MIMEMultipart
19 from email.mime.image import MIMEImage
19 from email.mime.image import MIMEImage
20 from email.mime.audio import MIMEAudio
20 from email.mime.audio import MIMEAudio
21 from email.mime.base import MIMEBase
21 from email.mime.base import MIMEBase
22 from email.mime.text import MIMEText
22 from email.mime.text import MIMEText
23 from email.utils import formatdate
23 from email.utils import formatdate
24 from email import encoders
24 from email import encoders
25
25
26 class SmtpMailer(object):
26 class SmtpMailer(object):
27 """SMTP mailer class
27 """SMTP mailer class
28
28
29 mailer = SmtpMailer(mail_from, user, passwd, mail_server, mail_port, ssl, tls)
29 mailer = SmtpMailer(mail_from, user, passwd, mail_server, mail_port, ssl, tls)
30 mailer.send(recipients, subject, body, attachment_files)
30 mailer.send(recipients, subject, body, attachment_files)
31
31
32 :param recipients might be a list of string or single string
32 :param recipients might be a list of string or single string
33 :param attachment_files is a dict of {filename:location}
33 :param attachment_files is a dict of {filename:location}
34 it tries to guess the mimetype and attach the file
34 it tries to guess the mimetype and attach the file
35
35
36 """
36 """
37
37
38 def __init__(self, mail_from, user, passwd, mail_server,
38 def __init__(self, mail_from, user, passwd, mail_server,
39 mail_port=None, ssl=False, tls=False):
39 mail_port=None, ssl=False, tls=False, debug=False):
40
40
41 self.mail_from = mail_from
41 self.mail_from = mail_from
42 self.mail_server = mail_server
42 self.mail_server = mail_server
43 self.mail_port = mail_port
43 self.mail_port = mail_port
44 self.user = user
44 self.user = user
45 self.passwd = passwd
45 self.passwd = passwd
46 self.ssl = ssl
46 self.ssl = ssl
47 self.tls = tls
47 self.tls = tls
48 self.debug = False
48 self.debug = debug
49
49
50 def send(self, recipients=[], subject='', body='', attachment_files=None):
50 def send(self, recipients=[], subject='', body='', attachment_files=None):
51
51
52 if isinstance(recipients, basestring):
52 if isinstance(recipients, basestring):
53 recipients = [recipients]
53 recipients = [recipients]
54 if self.ssl:
54 if self.ssl:
55 smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port)
55 smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port)
56 else:
56 else:
57 smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port)
57 smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port)
58
58
59 if self.tls:
59 if self.tls:
60 smtp_serv.ehlo()
60 smtp_serv.ehlo()
61 smtp_serv.starttls()
61 smtp_serv.starttls()
62
62
63 if self.debug:
63 if self.debug:
64 smtp_serv.set_debuglevel(1)
64 smtp_serv.set_debuglevel(1)
65
65
66 smtp_serv.ehlo()
66 smtp_serv.ehlo()
67
67
68 #if server requires authorization you must provide login and password
68 #if server requires authorization you must provide login and password
69 #but only if we have them
69 #but only if we have them
70 if self.user and self.passwd:
70 if self.user and self.passwd:
71 smtp_serv.login(self.user, self.passwd)
71 smtp_serv.login(self.user, self.passwd)
72
72
73
73
74 date_ = formatdate(localtime=True)
74 date_ = formatdate(localtime=True)
75 msg = MIMEMultipart()
75 msg = MIMEMultipart()
76 msg['From'] = self.mail_from
76 msg['From'] = self.mail_from
77 msg['To'] = ','.join(recipients)
77 msg['To'] = ','.join(recipients)
78 msg['Date'] = date_
78 msg['Date'] = date_
79 msg['Subject'] = subject
79 msg['Subject'] = subject
80 msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'
80 msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'
81
81
82 msg.attach(MIMEText(body))
82 msg.attach(MIMEText(body))
83
83
84 if attachment_files:
84 if attachment_files:
85 self.__atach_files(msg, attachment_files)
85 self.__atach_files(msg, attachment_files)
86
86
87 smtp_serv.sendmail(self.mail_from, recipients, msg.as_string())
87 smtp_serv.sendmail(self.mail_from, recipients, msg.as_string())
88 logging.info('MAIL SEND TO: %s' % recipients)
88 logging.info('MAIL SEND TO: %s' % recipients)
89
89
90 try:
90 try:
91 smtp_serv.quit()
91 smtp_serv.quit()
92 except sslerror:
92 except sslerror:
93 # sslerror is raised in tls connections on closing sometimes
93 # sslerror is raised in tls connections on closing sometimes
94 pass
94 pass
95
95
96
96
97
97
98 def __atach_files(self, msg, attachment_files):
98 def __atach_files(self, msg, attachment_files):
99 if isinstance(attachment_files, dict):
99 if isinstance(attachment_files, dict):
100 for f_name, msg_file in attachment_files.items():
100 for f_name, msg_file in attachment_files.items():
101 ctype, encoding = mimetypes.guess_type(f_name)
101 ctype, encoding = mimetypes.guess_type(f_name)
102 logging.info("guessing file %s type based on %s" , ctype, f_name)
102 logging.info("guessing file %s type based on %s" , ctype, f_name)
103 if ctype is None or encoding is not None:
103 if ctype is None or encoding is not None:
104 # No guess could be made, or the file is encoded (compressed), so
104 # No guess could be made, or the file is encoded (compressed), so
105 # use a generic bag-of-bits type.
105 # use a generic bag-of-bits type.
106 ctype = 'application/octet-stream'
106 ctype = 'application/octet-stream'
107 maintype, subtype = ctype.split('/', 1)
107 maintype, subtype = ctype.split('/', 1)
108 if maintype == 'text':
108 if maintype == 'text':
109 # Note: we should handle calculating the charset
109 # Note: we should handle calculating the charset
110 file_part = MIMEText(self.get_content(msg_file),
110 file_part = MIMEText(self.get_content(msg_file),
111 _subtype=subtype)
111 _subtype=subtype)
112 elif maintype == 'image':
112 elif maintype == 'image':
113 file_part = MIMEImage(self.get_content(msg_file),
113 file_part = MIMEImage(self.get_content(msg_file),
114 _subtype=subtype)
114 _subtype=subtype)
115 elif maintype == 'audio':
115 elif maintype == 'audio':
116 file_part = MIMEAudio(self.get_content(msg_file),
116 file_part = MIMEAudio(self.get_content(msg_file),
117 _subtype=subtype)
117 _subtype=subtype)
118 else:
118 else:
119 file_part = MIMEBase(maintype, subtype)
119 file_part = MIMEBase(maintype, subtype)
120 file_part.set_payload(self.get_content(msg_file))
120 file_part.set_payload(self.get_content(msg_file))
121 # Encode the payload using Base64
121 # Encode the payload using Base64
122 encoders.encode_base64(msg)
122 encoders.encode_base64(msg)
123 # Set the filename parameter
123 # Set the filename parameter
124 file_part.add_header('Content-Disposition', 'attachment',
124 file_part.add_header('Content-Disposition', 'attachment',
125 filename=f_name)
125 filename=f_name)
126 file_part.add_header('Content-Type', ctype, name=f_name)
126 file_part.add_header('Content-Type', ctype, name=f_name)
127 msg.attach(file_part)
127 msg.attach(file_part)
128 else:
128 else:
129 raise Exception('Attachment files should be'
129 raise Exception('Attachment files should be'
130 'a dict in format {"filename":"filepath"}')
130 'a dict in format {"filename":"filepath"}')
131
131
132 def get_content(self, msg_file):
132 def get_content(self, msg_file):
133 """Get content based on type, if content is a string do open first
133 """Get content based on type, if content is a string do open first
134 else just read because it's a probably open file object
134 else just read because it's a probably open file object
135
135
136 :param msg_file:
136 :param msg_file:
137 """
137 """
138 if isinstance(msg_file, str):
138 if isinstance(msg_file, str):
139 return open(msg_file, "rb").read()
139 return open(msg_file, "rb").read()
140 else:
140 else:
141 #just for safe seek to 0
141 #just for safe seek to 0
142 msg_file.seek(0)
142 msg_file.seek(0)
143 return msg_file.read()
143 return msg_file.read()
General Comments 0
You need to be logged in to leave comments. Login now