Show More
@@ -1,33 +1,45 b'' | |||||
1 | # List of modules to import when celery starts. |
|
1 | # List of modules to import when celery starts. | |
2 | import sys |
|
2 | import sys | |
3 | import os |
|
3 | import os | |
|
4 | import ConfigParser | |||
|
5 | ||||
|
6 | PYLONS_CONFIG_NAME = 'test.ini' | |||
|
7 | ||||
|
8 | root = os.getcwd() | |||
|
9 | config = ConfigParser.ConfigParser({'here':root}) | |||
|
10 | config.read('%s/%s' % (root, PYLONS_CONFIG_NAME)) | |||
|
11 | PYLONS_CONFIG = config | |||
|
12 | ||||
|
13 | ||||
|
14 | print config.items('app:main') | |||
|
15 | ||||
4 | sys.path.append(os.getcwd()) |
|
16 | sys.path.append(os.getcwd()) | |
5 |
CELERY_IMPORTS = ("pylons_app.lib.celerylib.tasks", |
|
17 | CELERY_IMPORTS = ("pylons_app.lib.celerylib.tasks",) | |
6 |
|
18 | |||
7 | ## Result store settings. |
|
19 | ## Result store settings. | |
8 | CELERY_RESULT_BACKEND = "database" |
|
20 | CELERY_RESULT_BACKEND = "database" | |
9 | CELERY_RESULT_DBURI = "sqlite:///hg_app.db" |
|
21 | CELERY_RESULT_DBURI = "sqlite:///hg_app.db" | |
10 |
|
22 | |||
11 | BROKER_CONNECTION_MAX_RETRIES = 30 |
|
23 | BROKER_CONNECTION_MAX_RETRIES = 30 | |
12 |
|
24 | |||
13 | ## Broker settings. |
|
25 | ## Broker settings. | |
14 | BROKER_HOST = "localhost" |
|
26 | BROKER_HOST = "localhost" | |
15 | BROKER_PORT = 5672 |
|
27 | BROKER_PORT = 5672 | |
16 | BROKER_VHOST = "rabbitmqhost" |
|
28 | BROKER_VHOST = "rabbitmqhost" | |
17 | BROKER_USER = "rabbitmq" |
|
29 | BROKER_USER = "rabbitmq" | |
18 | BROKER_PASSWORD = "qweqwe" |
|
30 | BROKER_PASSWORD = "qweqwe" | |
19 |
|
31 | |||
20 | ## Worker settings |
|
32 | ## Worker settings | |
21 | ## If you're doing mostly I/O you can have more processes, |
|
33 | ## If you're doing mostly I/O you can have more processes, | |
22 | ## but if mostly spending CPU, try to keep it close to the |
|
34 | ## but if mostly spending CPU, try to keep it close to the | |
23 | ## number of CPUs on your machine. If not set, the number of CPUs/cores |
|
35 | ## number of CPUs on your machine. If not set, the number of CPUs/cores | |
24 | ## available will be used. |
|
36 | ## available will be used. | |
25 | CELERYD_CONCURRENCY = 2 |
|
37 | CELERYD_CONCURRENCY = 2 | |
26 | # CELERYD_LOG_FILE = "celeryd.log" |
|
38 | # CELERYD_LOG_FILE = "celeryd.log" | |
27 | CELERYD_LOG_LEVEL = "DEBUG" |
|
39 | CELERYD_LOG_LEVEL = "DEBUG" | |
28 | CELERYD_MAX_TASKS_PER_CHILD = 1 |
|
40 | CELERYD_MAX_TASKS_PER_CHILD = 1 | |
29 |
|
41 | |||
30 | #CELERY_ALWAYS_EAGER = True |
|
42 | #CELERY_ALWAYS_EAGER = True | |
31 | #rabbitmqctl add_user rabbitmq qweqwe |
|
43 | #rabbitmqctl add_user rabbitmq qweqwe | |
32 | #rabbitmqctl add_vhost rabbitmqhost |
|
44 | #rabbitmqctl add_vhost rabbitmqhost | |
33 | #rabbitmqctl set_permissions -p rabbitmqhost rabbitmq ".*" ".*" ".*" No newline at end of file |
|
45 | #rabbitmqctl set_permissions -p rabbitmqhost rabbitmq ".*" ".*" ".*" |
@@ -1,215 +1,208 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 datetime import datetime, timedelta |
|
4 | from datetime import datetime, timedelta | |
4 | from os.path import dirname as dn |
|
|||
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 time import mktime |
|
10 | from time import mktime | |
11 | from vcs.backends.hg import MercurialRepository |
|
11 | from vcs.backends.hg import MercurialRepository | |
12 | import ConfigParser |
|
|||
13 | import calendar |
|
12 | import calendar | |
14 | import os |
|
|||
15 | import traceback |
|
13 | import traceback | |
16 |
|
14 | |||
17 |
|
||||
18 | root = dn(dn(dn(dn(os.path.realpath(__file__))))) |
|
|||
19 | config = ConfigParser.ConfigParser({'here':root}) |
|
|||
20 | config.read('%s/development.ini' % root) |
|
|||
21 |
|
||||
22 | __all__ = ['whoosh_index', 'get_commits_stats', |
|
15 | __all__ = ['whoosh_index', 'get_commits_stats', | |
23 | 'reset_user_password', 'send_email'] |
|
16 | 'reset_user_password', 'send_email'] | |
24 |
|
17 | |||
25 | def get_session(): |
|
18 | def get_session(): | |
26 | from sqlalchemy import engine_from_config |
|
19 | from sqlalchemy import engine_from_config | |
27 | from sqlalchemy.orm import sessionmaker, scoped_session |
|
20 | from sqlalchemy.orm import sessionmaker, scoped_session | |
28 | engine = engine_from_config(dict(config.items('app:main')), 'sqlalchemy.db1.') |
|
21 | engine = engine_from_config(dict(config.items('app:main')), 'sqlalchemy.db1.') | |
29 | sa = scoped_session(sessionmaker(bind=engine)) |
|
22 | sa = scoped_session(sessionmaker(bind=engine)) | |
30 | return sa |
|
23 | return sa | |
31 |
|
24 | |||
32 | def get_hg_settings(): |
|
25 | def get_hg_settings(): | |
33 | from pylons_app.model.db import HgAppSettings |
|
26 | from pylons_app.model.db import HgAppSettings | |
34 | try: |
|
27 | try: | |
35 | sa = get_session() |
|
28 | sa = get_session() | |
36 | ret = sa.query(HgAppSettings).all() |
|
29 | ret = sa.query(HgAppSettings).all() | |
37 | finally: |
|
30 | finally: | |
38 | sa.remove() |
|
31 | sa.remove() | |
39 |
|
32 | |||
40 | if not ret: |
|
33 | if not ret: | |
41 | raise Exception('Could not get application settings !') |
|
34 | raise Exception('Could not get application settings !') | |
42 | settings = {} |
|
35 | settings = {} | |
43 | for each in ret: |
|
36 | for each in ret: | |
44 | settings['hg_app_' + each.app_settings_name] = each.app_settings_value |
|
37 | settings['hg_app_' + each.app_settings_name] = each.app_settings_value | |
45 |
|
38 | |||
46 | return settings |
|
39 | return settings | |
47 |
|
40 | |||
48 | def get_hg_ui_settings(): |
|
41 | def get_hg_ui_settings(): | |
49 | from pylons_app.model.db import HgAppUi |
|
42 | from pylons_app.model.db import HgAppUi | |
50 | try: |
|
43 | try: | |
51 | sa = get_session() |
|
44 | sa = get_session() | |
52 | ret = sa.query(HgAppUi).all() |
|
45 | ret = sa.query(HgAppUi).all() | |
53 | finally: |
|
46 | finally: | |
54 | sa.remove() |
|
47 | sa.remove() | |
55 |
|
48 | |||
56 | if not ret: |
|
49 | if not ret: | |
57 | raise Exception('Could not get application ui settings !') |
|
50 | raise Exception('Could not get application ui settings !') | |
58 | settings = {} |
|
51 | settings = {} | |
59 | for each in ret: |
|
52 | for each in ret: | |
60 | k = each.ui_key |
|
53 | k = each.ui_key | |
61 | v = each.ui_value |
|
54 | v = each.ui_value | |
62 | if k == '/': |
|
55 | if k == '/': | |
63 | k = 'root_path' |
|
56 | k = 'root_path' | |
64 |
|
57 | |||
65 | if k.find('.') != -1: |
|
58 | if k.find('.') != -1: | |
66 | k = k.replace('.', '_') |
|
59 | k = k.replace('.', '_') | |
67 |
|
60 | |||
68 | if each.ui_section == 'hooks': |
|
61 | if each.ui_section == 'hooks': | |
69 | v = each.ui_active |
|
62 | v = each.ui_active | |
70 |
|
63 | |||
71 | settings[each.ui_section + '_' + k] = v |
|
64 | settings[each.ui_section + '_' + k] = v | |
72 |
|
65 | |||
73 | return settings |
|
66 | return settings | |
74 |
|
67 | |||
75 | @task |
|
68 | @task | |
76 | def whoosh_index(repo_location, full_index): |
|
69 | def whoosh_index(repo_location, full_index): | |
77 | log = whoosh_index.get_logger() |
|
70 | log = whoosh_index.get_logger() | |
78 | from pylons_app.lib.indexers import DaemonLock |
|
71 | from pylons_app.lib.indexers import DaemonLock | |
79 | from pylons_app.lib.indexers.daemon import WhooshIndexingDaemon, LockHeld |
|
72 | from pylons_app.lib.indexers.daemon import WhooshIndexingDaemon, LockHeld | |
80 | try: |
|
73 | try: | |
81 | l = DaemonLock() |
|
74 | l = DaemonLock() | |
82 | WhooshIndexingDaemon(repo_location=repo_location)\ |
|
75 | WhooshIndexingDaemon(repo_location=repo_location)\ | |
83 | .run(full_index=full_index) |
|
76 | .run(full_index=full_index) | |
84 | l.release() |
|
77 | l.release() | |
85 | return 'Done' |
|
78 | return 'Done' | |
86 | except LockHeld: |
|
79 | except LockHeld: | |
87 | log.info('LockHeld') |
|
80 | log.info('LockHeld') | |
88 | return 'LockHeld' |
|
81 | return 'LockHeld' | |
89 |
|
82 | |||
90 | @task |
|
83 | @task | |
91 | def get_commits_stats(repo): |
|
84 | def get_commits_stats(repo): | |
92 | log = get_commits_stats.get_logger() |
|
85 | log = get_commits_stats.get_logger() | |
93 | aggregate = OrderedDict() |
|
86 | aggregate = OrderedDict() | |
94 | repos_path = get_hg_ui_settings()['paths_root_path'].replace('*','') |
|
87 | repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '') | |
95 | repo = MercurialRepository(repos_path + repo) |
|
88 | repo = MercurialRepository(repos_path + repo) | |
96 | #graph range |
|
89 | #graph range | |
97 | td = datetime.today() + timedelta(days=1) |
|
90 | td = datetime.today() + timedelta(days=1) | |
98 | y, m, d = td.year, td.month, td.day |
|
91 | y, m, d = td.year, td.month, td.day | |
99 | ts_min = mktime((y, (td - timedelta(days=calendar.mdays[m])).month, |
|
92 | ts_min = mktime((y, (td - timedelta(days=calendar.mdays[m])).month, | |
100 | d, 0, 0, 0, 0, 0, 0,)) |
|
93 | d, 0, 0, 0, 0, 0, 0,)) | |
101 | ts_max = mktime((y, m, d, 0, 0, 0, 0, 0, 0,)) |
|
94 | ts_max = mktime((y, m, d, 0, 0, 0, 0, 0, 0,)) | |
102 |
|
95 | |||
103 | def author_key_cleaner(k): |
|
96 | def author_key_cleaner(k): | |
104 | k = person(k) |
|
97 | k = person(k) | |
105 | k = k.replace('"', "'") #for js data compatibilty |
|
98 | k = k.replace('"', "'") #for js data compatibilty | |
106 | return k |
|
99 | return k | |
107 |
|
100 | |||
108 | for cs in repo[:200]:#added limit 200 until fix #29 is made |
|
101 | for cs in repo[:200]:#added limit 200 until fix #29 is made | |
109 | k = '%s-%s-%s' % (cs.date.timetuple()[0], cs.date.timetuple()[1], |
|
102 | k = '%s-%s-%s' % (cs.date.timetuple()[0], cs.date.timetuple()[1], | |
110 | cs.date.timetuple()[2]) |
|
103 | cs.date.timetuple()[2]) | |
111 | timetupple = [int(x) for x in k.split('-')] |
|
104 | timetupple = [int(x) for x in k.split('-')] | |
112 | timetupple.extend([0 for _ in xrange(6)]) |
|
105 | timetupple.extend([0 for _ in xrange(6)]) | |
113 | k = mktime(timetupple) |
|
106 | k = mktime(timetupple) | |
114 | if aggregate.has_key(author_key_cleaner(cs.author)): |
|
107 | if aggregate.has_key(author_key_cleaner(cs.author)): | |
115 | if aggregate[author_key_cleaner(cs.author)].has_key(k): |
|
108 | if aggregate[author_key_cleaner(cs.author)].has_key(k): | |
116 | aggregate[author_key_cleaner(cs.author)][k]["commits"] += 1 |
|
109 | aggregate[author_key_cleaner(cs.author)][k]["commits"] += 1 | |
117 | aggregate[author_key_cleaner(cs.author)][k]["added"] += len(cs.added) |
|
110 | aggregate[author_key_cleaner(cs.author)][k]["added"] += len(cs.added) | |
118 | aggregate[author_key_cleaner(cs.author)][k]["changed"] += len(cs.changed) |
|
111 | aggregate[author_key_cleaner(cs.author)][k]["changed"] += len(cs.changed) | |
119 | aggregate[author_key_cleaner(cs.author)][k]["removed"] += len(cs.removed) |
|
112 | aggregate[author_key_cleaner(cs.author)][k]["removed"] += len(cs.removed) | |
120 |
|
113 | |||
121 | else: |
|
114 | else: | |
122 | #aggregate[author_key_cleaner(cs.author)].update(dates_range) |
|
115 | #aggregate[author_key_cleaner(cs.author)].update(dates_range) | |
123 | if k >= ts_min and k <= ts_max: |
|
116 | if k >= ts_min and k <= ts_max: | |
124 | aggregate[author_key_cleaner(cs.author)][k] = {} |
|
117 | aggregate[author_key_cleaner(cs.author)][k] = {} | |
125 | aggregate[author_key_cleaner(cs.author)][k]["commits"] = 1 |
|
118 | aggregate[author_key_cleaner(cs.author)][k]["commits"] = 1 | |
126 | aggregate[author_key_cleaner(cs.author)][k]["added"] = len(cs.added) |
|
119 | aggregate[author_key_cleaner(cs.author)][k]["added"] = len(cs.added) | |
127 | aggregate[author_key_cleaner(cs.author)][k]["changed"] = len(cs.changed) |
|
120 | aggregate[author_key_cleaner(cs.author)][k]["changed"] = len(cs.changed) | |
128 | aggregate[author_key_cleaner(cs.author)][k]["removed"] = len(cs.removed) |
|
121 | aggregate[author_key_cleaner(cs.author)][k]["removed"] = len(cs.removed) | |
129 |
|
122 | |||
130 | else: |
|
123 | else: | |
131 | if k >= ts_min and k <= ts_max: |
|
124 | if k >= ts_min and k <= ts_max: | |
132 | aggregate[author_key_cleaner(cs.author)] = OrderedDict() |
|
125 | aggregate[author_key_cleaner(cs.author)] = OrderedDict() | |
133 | #aggregate[author_key_cleaner(cs.author)].update(dates_range) |
|
126 | #aggregate[author_key_cleaner(cs.author)].update(dates_range) | |
134 | aggregate[author_key_cleaner(cs.author)][k] = {} |
|
127 | aggregate[author_key_cleaner(cs.author)][k] = {} | |
135 | aggregate[author_key_cleaner(cs.author)][k]["commits"] = 1 |
|
128 | aggregate[author_key_cleaner(cs.author)][k]["commits"] = 1 | |
136 | aggregate[author_key_cleaner(cs.author)][k]["added"] = len(cs.added) |
|
129 | aggregate[author_key_cleaner(cs.author)][k]["added"] = len(cs.added) | |
137 | aggregate[author_key_cleaner(cs.author)][k]["changed"] = len(cs.changed) |
|
130 | aggregate[author_key_cleaner(cs.author)][k]["changed"] = len(cs.changed) | |
138 | aggregate[author_key_cleaner(cs.author)][k]["removed"] = len(cs.removed) |
|
131 | aggregate[author_key_cleaner(cs.author)][k]["removed"] = len(cs.removed) | |
139 |
|
132 | |||
140 | d = '' |
|
133 | d = '' | |
141 | tmpl0 = u""""%s":%s""" |
|
134 | tmpl0 = u""""%s":%s""" | |
142 | tmpl1 = u"""{label:"%s",data:%s,schema:["commits"]},""" |
|
135 | tmpl1 = u"""{label:"%s",data:%s,schema:["commits"]},""" | |
143 | for author in aggregate: |
|
136 | for author in aggregate: | |
144 |
|
137 | |||
145 | d += tmpl0 % (author, |
|
138 | d += tmpl0 % (author, | |
146 | tmpl1 \ |
|
139 | tmpl1 \ | |
147 | % (author, |
|
140 | % (author, | |
148 | [{"time":x, |
|
141 | [{"time":x, | |
149 | "commits":aggregate[author][x]['commits'], |
|
142 | "commits":aggregate[author][x]['commits'], | |
150 | "added":aggregate[author][x]['added'], |
|
143 | "added":aggregate[author][x]['added'], | |
151 | "changed":aggregate[author][x]['changed'], |
|
144 | "changed":aggregate[author][x]['changed'], | |
152 | "removed":aggregate[author][x]['removed'], |
|
145 | "removed":aggregate[author][x]['removed'], | |
153 | } for x in aggregate[author]])) |
|
146 | } for x in aggregate[author]])) | |
154 | if d == '': |
|
147 | if d == '': | |
155 | d = '"%s":{label:"%s",data:[[0,1],]}' \ |
|
148 | d = '"%s":{label:"%s",data:[[0,1],]}' \ | |
156 | % (author_key_cleaner(repo.contact), |
|
149 | % (author_key_cleaner(repo.contact), | |
157 | author_key_cleaner(repo.contact)) |
|
150 | author_key_cleaner(repo.contact)) | |
158 | return (ts_min, ts_max, d) |
|
151 | return (ts_min, ts_max, d) | |
159 |
|
152 | |||
160 | @task |
|
153 | @task | |
161 | def reset_user_password(user_email): |
|
154 | def reset_user_password(user_email): | |
162 | log = reset_user_password.get_logger() |
|
155 | log = reset_user_password.get_logger() | |
163 | from pylons_app.lib import auth |
|
156 | from pylons_app.lib import auth | |
164 | from pylons_app.model.db import User |
|
157 | from pylons_app.model.db import User | |
165 |
|
158 | |||
166 | try: |
|
159 | try: | |
167 |
|
160 | |||
168 | try: |
|
161 | try: | |
169 | sa = get_session() |
|
162 | sa = get_session() | |
170 | user = sa.query(User).filter(User.email == user_email).scalar() |
|
163 | user = sa.query(User).filter(User.email == user_email).scalar() | |
171 | new_passwd = auth.PasswordGenerator().gen_password(8, |
|
164 | new_passwd = auth.PasswordGenerator().gen_password(8, | |
172 | auth.PasswordGenerator.ALPHABETS_BIG_SMALL) |
|
165 | auth.PasswordGenerator.ALPHABETS_BIG_SMALL) | |
173 | user.password = auth.get_crypt_password(new_passwd) |
|
166 | user.password = auth.get_crypt_password(new_passwd) | |
174 | sa.add(user) |
|
167 | sa.add(user) | |
175 | sa.commit() |
|
168 | sa.commit() | |
176 | log.info('change password for %s', user_email) |
|
169 | log.info('change password for %s', user_email) | |
177 | if new_passwd is None: |
|
170 | if new_passwd is None: | |
178 | raise Exception('unable to generate new password') |
|
171 | raise Exception('unable to generate new password') | |
179 |
|
172 | |||
180 | except: |
|
173 | except: | |
181 | log.error(traceback.format_exc()) |
|
174 | log.error(traceback.format_exc()) | |
182 | sa.rollback() |
|
175 | sa.rollback() | |
183 |
|
176 | |||
184 | run_task(send_email, user_email, |
|
177 | run_task(send_email, user_email, | |
185 | "Your new hg-app password", |
|
178 | "Your new hg-app password", | |
186 | 'Your new hg-app password:%s' % (new_passwd)) |
|
179 | 'Your new hg-app password:%s' % (new_passwd)) | |
187 | log.info('send new password mail to %s', user_email) |
|
180 | log.info('send new password mail to %s', user_email) | |
188 |
|
181 | |||
189 |
|
182 | |||
190 | except: |
|
183 | except: | |
191 | log.error('Failed to update user password') |
|
184 | log.error('Failed to update user password') | |
192 | log.error(traceback.format_exc()) |
|
185 | log.error(traceback.format_exc()) | |
193 | return True |
|
186 | return True | |
194 |
|
187 | |||
195 | @task |
|
188 | @task | |
196 | def send_email(recipients, subject, body): |
|
189 | def send_email(recipients, subject, body): | |
197 | log = send_email.get_logger() |
|
190 | log = send_email.get_logger() | |
198 | email_config = dict(config.items('DEFAULT')) |
|
191 | email_config = dict(config.items('DEFAULT')) | |
199 | mail_from = email_config.get('app_email_from') |
|
192 | mail_from = email_config.get('app_email_from') | |
200 | user = email_config.get('smtp_username') |
|
193 | user = email_config.get('smtp_username') | |
201 | passwd = email_config.get('smtp_password') |
|
194 | passwd = email_config.get('smtp_password') | |
202 | mail_server = email_config.get('smtp_server') |
|
195 | mail_server = email_config.get('smtp_server') | |
203 | mail_port = email_config.get('smtp_port') |
|
196 | mail_port = email_config.get('smtp_port') | |
204 | tls = email_config.get('smtp_use_tls') |
|
197 | tls = email_config.get('smtp_use_tls') | |
205 | ssl = False |
|
198 | ssl = False | |
206 |
|
199 | |||
207 | try: |
|
200 | try: | |
208 |
m = SmtpMailer(mail_from, user, passwd, mail_server, |
|
201 | m = SmtpMailer(mail_from, user, passwd, mail_server, | |
209 | mail_port, ssl, tls) |
|
202 | mail_port, ssl, tls) | |
210 | m.send(recipients, subject, body) |
|
203 | m.send(recipients, subject, body) | |
211 | except: |
|
204 | except: | |
212 | log.error('Mail sending failed') |
|
205 | log.error('Mail sending failed') | |
213 | log.error(traceback.format_exc()) |
|
206 | log.error(traceback.format_exc()) | |
214 | return False |
|
207 | return False | |
215 | return True |
|
208 | return True |
@@ -1,226 +1,238 b'' | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | # encoding: utf-8 |
|
2 | # encoding: utf-8 | |
3 | # whoosh indexer daemon for hg-app |
|
3 | # whoosh indexer daemon for hg-app | |
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> |
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |
5 | # |
|
5 | # | |
6 | # This program is free software; you can redistribute it and/or |
|
6 | # This program is free software; you can redistribute it and/or | |
7 | # modify it under the terms of the GNU General Public License |
|
7 | # modify it under the terms of the GNU General Public License | |
8 | # as published by the Free Software Foundation; version 2 |
|
8 | # as published by the Free Software Foundation; version 2 | |
9 | # of the License or (at your opinion) any later version of the license. |
|
9 | # of the License or (at your opinion) any later version of the license. | |
10 | # |
|
10 | # | |
11 | # This program is distributed in the hope that it will be useful, |
|
11 | # This program is distributed in the hope that it will be useful, | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | # GNU General Public License for more details. |
|
14 | # GNU General Public License for more details. | |
15 | # |
|
15 | # | |
16 | # You should have received a copy of the GNU General Public License |
|
16 | # You should have received a copy of the GNU General Public License | |
17 | # along with this program; if not, write to the Free Software |
|
17 | # along with this program; if not, write to the Free Software | |
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
19 | # MA 02110-1301, USA. |
|
19 | # MA 02110-1301, USA. | |
20 | """ |
|
20 | """ | |
21 | Created on Jan 26, 2010 |
|
21 | Created on Jan 26, 2010 | |
22 |
|
22 | |||
23 | @author: marcink |
|
23 | @author: marcink | |
24 | A deamon will read from task table and run tasks |
|
24 | A deamon will read from task table and run tasks | |
25 | """ |
|
25 | """ | |
26 | import sys |
|
26 | import sys | |
27 | import os |
|
27 | import os | |
28 | from os.path import dirname as dn |
|
28 | from os.path import dirname as dn | |
29 | from os.path import join as jn |
|
29 | from os.path import join as jn | |
30 |
|
30 | |||
31 | #to get the pylons_app import |
|
31 | #to get the pylons_app import | |
32 | project_path = dn(dn(dn(dn(os.path.realpath(__file__))))) |
|
32 | project_path = dn(dn(dn(dn(os.path.realpath(__file__))))) | |
33 | sys.path.append(project_path) |
|
33 | sys.path.append(project_path) | |
34 |
|
34 | |||
35 | from pidlock import LockHeld, DaemonLock |
|
35 | from pidlock import LockHeld, DaemonLock | |
36 | import traceback |
|
|||
37 | from pylons_app.config.environment import load_environment |
|
|||
38 | from pylons_app.model.hg_model import HgModel |
|
36 | from pylons_app.model.hg_model import HgModel | |
39 | from pylons_app.lib.helpers import safe_unicode |
|
37 | from pylons_app.lib.helpers import safe_unicode | |
40 | from whoosh.index import create_in, open_dir |
|
38 | from whoosh.index import create_in, open_dir | |
41 | from shutil import rmtree |
|
39 | from shutil import rmtree | |
42 |
from pylons_app.lib.indexers import |
|
40 | from pylons_app.lib.indexers import INDEX_EXTENSIONS, IDX_LOCATION, SCHEMA, IDX_NAME | |
43 | SCHEMA, IDX_NAME |
|
|||
44 |
|
41 | |||
45 | import logging |
|
42 | import logging | |
46 | import logging.config |
|
43 | ||
47 | logging.config.fileConfig(jn(project_path, 'development.ini')) |
|
|||
48 | log = logging.getLogger('whooshIndexer') |
|
44 | log = logging.getLogger('whooshIndexer') | |
|
45 | # create logger | |||
|
46 | log.setLevel(logging.DEBUG) | |||
|
47 | ||||
|
48 | # create console handler and set level to debug | |||
|
49 | ch = logging.StreamHandler() | |||
|
50 | ch.setLevel(logging.DEBUG) | |||
|
51 | ||||
|
52 | # create formatter | |||
|
53 | formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") | |||
|
54 | ||||
|
55 | # add formatter to ch | |||
|
56 | ch.setFormatter(formatter) | |||
|
57 | ||||
|
58 | # add ch to logger | |||
|
59 | log.addHandler(ch) | |||
49 |
|
60 | |||
50 | def scan_paths(root_location): |
|
61 | def scan_paths(root_location): | |
51 | return HgModel.repo_scan('/', root_location, None, True) |
|
62 | return HgModel.repo_scan('/', root_location, None, True) | |
52 |
|
63 | |||
53 | class WhooshIndexingDaemon(object): |
|
64 | class WhooshIndexingDaemon(object): | |
54 | """Deamon for atomic jobs""" |
|
65 | """Deamon for atomic jobs""" | |
55 |
|
66 | |||
56 | def __init__(self, indexname='HG_INDEX', repo_location=None): |
|
67 | def __init__(self, indexname='HG_INDEX', repo_location=None): | |
57 | self.indexname = indexname |
|
68 | self.indexname = indexname | |
58 | self.repo_location = repo_location |
|
69 | self.repo_location = repo_location | |
59 | self.initial = False |
|
70 | self.initial = False | |
60 | if not os.path.isdir(IDX_LOCATION): |
|
71 | if not os.path.isdir(IDX_LOCATION): | |
61 | os.mkdir(IDX_LOCATION) |
|
72 | os.mkdir(IDX_LOCATION) | |
62 | log.info('Cannot run incremental index since it does not' |
|
73 | log.info('Cannot run incremental index since it does not' | |
63 | ' yet exist running full build') |
|
74 | ' yet exist running full build') | |
64 | self.initial = True |
|
75 | self.initial = True | |
65 |
|
76 | |||
66 | def get_paths(self, root_dir): |
|
77 | def get_paths(self, root_dir): | |
67 | """recursive walk in root dir and return a set of all path in that dir |
|
78 | """recursive walk in root dir and return a set of all path in that dir | |
68 | excluding files in .hg dir""" |
|
79 | excluding files in .hg dir""" | |
69 | index_paths_ = set() |
|
80 | index_paths_ = set() | |
70 | for path, dirs, files in os.walk(root_dir): |
|
81 | for path, dirs, files in os.walk(root_dir): | |
71 | if path.find('.hg') == -1: |
|
82 | if path.find('.hg') == -1: | |
72 | for f in files: |
|
83 | for f in files: | |
73 | index_paths_.add(jn(path, f)) |
|
84 | index_paths_.add(jn(path, f)) | |
74 |
|
85 | |||
75 | return index_paths_ |
|
86 | return index_paths_ | |
76 |
|
87 | |||
77 | def add_doc(self, writer, path, repo): |
|
88 | def add_doc(self, writer, path, repo): | |
78 | """Adding doc to writer""" |
|
89 | """Adding doc to writer""" | |
79 |
|
90 | |||
80 | ext = unicode(path.split('/')[-1].split('.')[-1].lower()) |
|
91 | ext = unicode(path.split('/')[-1].split('.')[-1].lower()) | |
81 | #we just index the content of choosen files |
|
92 | #we just index the content of choosen files | |
82 | if ext in INDEX_EXTENSIONS: |
|
93 | if ext in INDEX_EXTENSIONS: | |
83 | log.debug(' >> %s [WITH CONTENT]' % path) |
|
94 | log.debug(' >> %s [WITH CONTENT]' % path) | |
84 | fobj = open(path, 'rb') |
|
95 | fobj = open(path, 'rb') | |
85 | content = fobj.read() |
|
96 | content = fobj.read() | |
86 | fobj.close() |
|
97 | fobj.close() | |
87 | u_content = safe_unicode(content) |
|
98 | u_content = safe_unicode(content) | |
88 | else: |
|
99 | else: | |
89 | log.debug(' >> %s' % path) |
|
100 | log.debug(' >> %s' % path) | |
90 | #just index file name without it's content |
|
101 | #just index file name without it's content | |
91 | u_content = u'' |
|
102 | u_content = u'' | |
92 |
|
103 | |||
93 |
|
104 | |||
94 |
|
105 | |||
95 | try: |
|
106 | try: | |
96 | os.stat(path) |
|
107 | os.stat(path) | |
97 | writer.add_document(owner=unicode(repo.contact), |
|
108 | writer.add_document(owner=unicode(repo.contact), | |
98 | repository=u"%s" % repo.name, |
|
109 | repository=u"%s" % repo.name, | |
99 | path=u"%s" % path, |
|
110 | path=u"%s" % path, | |
100 | content=u_content, |
|
111 | content=u_content, | |
101 | modtime=os.path.getmtime(path), |
|
112 | modtime=os.path.getmtime(path), | |
102 | extension=ext) |
|
113 | extension=ext) | |
103 | except OSError, e: |
|
114 | except OSError, e: | |
104 | import errno |
|
115 | import errno | |
105 | if e.errno == errno.ENOENT: |
|
116 | if e.errno == errno.ENOENT: | |
106 | log.debug('path %s does not exist or is a broken symlink' % path) |
|
117 | log.debug('path %s does not exist or is a broken symlink' % path) | |
107 | else: |
|
118 | else: | |
108 | raise e |
|
119 | raise e | |
109 |
|
120 | |||
110 |
|
121 | |||
111 | def build_index(self): |
|
122 | def build_index(self): | |
112 | if os.path.exists(IDX_LOCATION): |
|
123 | if os.path.exists(IDX_LOCATION): | |
113 | log.debug('removing previos index') |
|
124 | log.debug('removing previos index') | |
114 | rmtree(IDX_LOCATION) |
|
125 | rmtree(IDX_LOCATION) | |
115 |
|
126 | |||
116 | if not os.path.exists(IDX_LOCATION): |
|
127 | if not os.path.exists(IDX_LOCATION): | |
117 | os.mkdir(IDX_LOCATION) |
|
128 | os.mkdir(IDX_LOCATION) | |
118 |
|
129 | |||
119 | idx = create_in(IDX_LOCATION, SCHEMA, indexname=IDX_NAME) |
|
130 | idx = create_in(IDX_LOCATION, SCHEMA, indexname=IDX_NAME) | |
120 | writer = idx.writer() |
|
131 | writer = idx.writer() | |
121 |
|
132 | |||
122 | for cnt, repo in enumerate(scan_paths(self.repo_location).values()): |
|
133 | for cnt, repo in enumerate(scan_paths(self.repo_location).values()): | |
123 | log.debug('building index @ %s' % repo.path) |
|
134 | log.debug('building index @ %s' % repo.path) | |
124 |
|
135 | |||
125 | for idx_path in self.get_paths(repo.path): |
|
136 | for idx_path in self.get_paths(repo.path): | |
126 | self.add_doc(writer, idx_path, repo) |
|
137 | self.add_doc(writer, idx_path, repo) | |
127 | writer.commit(merge=True) |
|
138 | writer.commit(merge=True) | |
128 |
|
139 | |||
129 | log.debug('>>> FINISHED BUILDING INDEX <<<') |
|
140 | log.debug('>>> FINISHED BUILDING INDEX <<<') | |
130 |
|
141 | |||
131 |
|
142 | |||
132 | def update_index(self): |
|
143 | def update_index(self): | |
133 | log.debug('STARTING INCREMENTAL INDEXING UPDATE') |
|
144 | log.debug('STARTING INCREMENTAL INDEXING UPDATE') | |
134 |
|
145 | |||
135 | idx = open_dir(IDX_LOCATION, indexname=self.indexname) |
|
146 | idx = open_dir(IDX_LOCATION, indexname=self.indexname) | |
136 | # The set of all paths in the index |
|
147 | # The set of all paths in the index | |
137 | indexed_paths = set() |
|
148 | indexed_paths = set() | |
138 | # The set of all paths we need to re-index |
|
149 | # The set of all paths we need to re-index | |
139 | to_index = set() |
|
150 | to_index = set() | |
140 |
|
151 | |||
141 | reader = idx.reader() |
|
152 | reader = idx.reader() | |
142 | writer = idx.writer() |
|
153 | writer = idx.writer() | |
143 |
|
154 | |||
144 | # Loop over the stored fields in the index |
|
155 | # Loop over the stored fields in the index | |
145 | for fields in reader.all_stored_fields(): |
|
156 | for fields in reader.all_stored_fields(): | |
146 | indexed_path = fields['path'] |
|
157 | indexed_path = fields['path'] | |
147 | indexed_paths.add(indexed_path) |
|
158 | indexed_paths.add(indexed_path) | |
148 |
|
159 | |||
149 | if not os.path.exists(indexed_path): |
|
160 | if not os.path.exists(indexed_path): | |
150 | # This file was deleted since it was indexed |
|
161 | # This file was deleted since it was indexed | |
151 | log.debug('removing from index %s' % indexed_path) |
|
162 | log.debug('removing from index %s' % indexed_path) | |
152 | writer.delete_by_term('path', indexed_path) |
|
163 | writer.delete_by_term('path', indexed_path) | |
153 |
|
164 | |||
154 | else: |
|
165 | else: | |
155 | # Check if this file was changed since it |
|
166 | # Check if this file was changed since it | |
156 | # was indexed |
|
167 | # was indexed | |
157 | indexed_time = fields['modtime'] |
|
168 | indexed_time = fields['modtime'] | |
158 |
|
169 | |||
159 | mtime = os.path.getmtime(indexed_path) |
|
170 | mtime = os.path.getmtime(indexed_path) | |
160 |
|
171 | |||
161 | if mtime > indexed_time: |
|
172 | if mtime > indexed_time: | |
162 |
|
173 | |||
163 | # The file has changed, delete it and add it to the list of |
|
174 | # The file has changed, delete it and add it to the list of | |
164 | # files to reindex |
|
175 | # files to reindex | |
165 | log.debug('adding to reindex list %s' % indexed_path) |
|
176 | log.debug('adding to reindex list %s' % indexed_path) | |
166 | writer.delete_by_term('path', indexed_path) |
|
177 | writer.delete_by_term('path', indexed_path) | |
167 | to_index.add(indexed_path) |
|
178 | to_index.add(indexed_path) | |
168 | #writer.commit() |
|
179 | #writer.commit() | |
169 |
|
180 | |||
170 | # Loop over the files in the filesystem |
|
181 | # Loop over the files in the filesystem | |
171 | # Assume we have a function that gathers the filenames of the |
|
182 | # Assume we have a function that gathers the filenames of the | |
172 | # documents to be indexed |
|
183 | # documents to be indexed | |
173 | for repo in scan_paths(self.repo_location).values(): |
|
184 | for repo in scan_paths(self.repo_location).values(): | |
174 | for path in self.get_paths(repo.path): |
|
185 | for path in self.get_paths(repo.path): | |
175 | if path in to_index or path not in indexed_paths: |
|
186 | if path in to_index or path not in indexed_paths: | |
176 | # This is either a file that's changed, or a new file |
|
187 | # This is either a file that's changed, or a new file | |
177 | # that wasn't indexed before. So index it! |
|
188 | # that wasn't indexed before. So index it! | |
178 | self.add_doc(writer, path, repo) |
|
189 | self.add_doc(writer, path, repo) | |
179 | log.debug('reindexing %s' % path) |
|
190 | log.debug('reindexing %s' % path) | |
180 |
|
191 | |||
181 | writer.commit(merge=True) |
|
192 | writer.commit(merge=True) | |
182 | #idx.optimize() |
|
193 | #idx.optimize() | |
183 | log.debug('>>> FINISHED <<<') |
|
194 | log.debug('>>> FINISHED <<<') | |
184 |
|
195 | |||
185 | def run(self, full_index=False): |
|
196 | def run(self, full_index=False): | |
186 | """Run daemon""" |
|
197 | """Run daemon""" | |
187 | if full_index or self.initial: |
|
198 | if full_index or self.initial: | |
188 | self.build_index() |
|
199 | self.build_index() | |
189 | else: |
|
200 | else: | |
190 | self.update_index() |
|
201 | self.update_index() | |
191 |
|
202 | |||
192 | if __name__ == "__main__": |
|
203 | if __name__ == "__main__": | |
193 | arg = sys.argv[1:] |
|
204 | arg = sys.argv[1:] | |
194 | if len(arg) != 2: |
|
205 | if len(arg) != 2: | |
195 | sys.stderr.write('Please specify indexing type [full|incremental]' |
|
206 | sys.stderr.write('Please specify indexing type [full|incremental]' | |
196 | 'and path to repositories as script args \n') |
|
207 | 'and path to repositories as script args \n') | |
197 | sys.exit() |
|
208 | sys.exit() | |
198 |
|
209 | |||
199 |
|
210 | |||
200 | if arg[0] == 'full': |
|
211 | if arg[0] == 'full': | |
201 | full_index = True |
|
212 | full_index = True | |
202 | elif arg[0] == 'incremental': |
|
213 | elif arg[0] == 'incremental': | |
203 | # False means looking just for changes |
|
214 | # False means looking just for changes | |
204 | full_index = False |
|
215 | full_index = False | |
205 | else: |
|
216 | else: | |
206 | sys.stdout.write('Please use [full|incremental]' |
|
217 | sys.stdout.write('Please use [full|incremental]' | |
207 | ' as script first arg \n') |
|
218 | ' as script first arg \n') | |
208 | sys.exit() |
|
219 | sys.exit() | |
209 |
|
220 | |||
210 | if not os.path.isdir(arg[1]): |
|
221 | if not os.path.isdir(arg[1]): | |
211 | sys.stderr.write('%s is not a valid path \n' % arg[1]) |
|
222 | sys.stderr.write('%s is not a valid path \n' % arg[1]) | |
212 | sys.exit() |
|
223 | sys.exit() | |
213 | else: |
|
224 | else: | |
214 | if arg[1].endswith('/'): |
|
225 | if arg[1].endswith('/'): | |
215 | repo_location = arg[1] + '*' |
|
226 | repo_location = arg[1] + '*' | |
216 | else: |
|
227 | else: | |
217 | repo_location = arg[1] + '/*' |
|
228 | repo_location = arg[1] + '/*' | |
218 |
|
229 | |||
219 | try: |
|
230 | try: | |
220 | l = DaemonLock() |
|
231 | l = DaemonLock() | |
221 | WhooshIndexingDaemon(repo_location=repo_location)\ |
|
232 | WhooshIndexingDaemon(repo_location=repo_location)\ | |
222 | .run(full_index=full_index) |
|
233 | .run(full_index=full_index) | |
223 | l.release() |
|
234 | l.release() | |
|
235 | reload(logging) | |||
224 | except LockHeld: |
|
236 | except LockHeld: | |
225 | sys.exit(1) |
|
237 | sys.exit(1) | |
226 |
|
238 |
@@ -1,49 +1,70 b'' | |||||
1 | """Pylons application test package |
|
1 | """Pylons application test package | |
2 |
|
2 | |||
3 | This package assumes the Pylons environment is already loaded, such as |
|
3 | This package assumes the Pylons environment is already loaded, such as | |
4 | when this script is imported from the `nosetests --with-pylons=test.ini` |
|
4 | when this script is imported from the `nosetests --with-pylons=test.ini` | |
5 | command. |
|
5 | command. | |
6 |
|
6 | |||
7 | This module initializes the application via ``websetup`` (`paster |
|
7 | This module initializes the application via ``websetup`` (`paster | |
8 | setup-app`) and provides the base testing objects. |
|
8 | setup-app`) and provides the base testing objects. | |
9 | """ |
|
9 | """ | |
10 | from unittest import TestCase |
|
10 | from unittest import TestCase | |
11 |
|
11 | |||
12 | from paste.deploy import loadapp |
|
12 | from paste.deploy import loadapp | |
13 | from paste.script.appinstall import SetupCommand |
|
13 | from paste.script.appinstall import SetupCommand | |
14 | from pylons import config, url |
|
14 | from pylons import config, url | |
15 | from routes.util import URLGenerator |
|
15 | from routes.util import URLGenerator | |
16 | from webtest import TestApp |
|
16 | from webtest import TestApp | |
17 | import os |
|
17 | import os | |
18 | from pylons_app.model import meta |
|
18 | from pylons_app.model import meta | |
|
19 | from pylons_app.lib.indexers import IDX_LOCATION | |||
19 | import logging |
|
20 | import logging | |
|
21 | import shutil | |||
20 | log = logging.getLogger(__name__) |
|
22 | log = logging.getLogger(__name__) | |
21 |
|
23 | |||
22 | import pylons.test |
|
24 | import pylons.test | |
23 |
|
25 | |||
24 | __all__ = ['environ', 'url', 'TestController'] |
|
26 | __all__ = ['environ', 'url', 'TestController'] | |
25 |
|
27 | |||
26 | # Invoke websetup with the current config file |
|
28 | # Invoke websetup with the current config file | |
27 | #SetupCommand('setup-app').run([pylons.test.pylonsapp.config['__file__']]) |
|
29 | #SetupCommand('setup-app').run([pylons.test.pylonsapp.config['__file__']]) | |
|
30 | def create_index(repo_location, full_index): | |||
|
31 | from pylons_app.lib.indexers import daemon | |||
|
32 | from pylons_app.lib.indexers.daemon import WhooshIndexingDaemon | |||
|
33 | from pylons_app.lib.indexers.pidlock import DaemonLock, LockHeld | |||
|
34 | ||||
|
35 | try: | |||
|
36 | l = DaemonLock() | |||
|
37 | WhooshIndexingDaemon(repo_location=repo_location)\ | |||
|
38 | .run(full_index=full_index) | |||
|
39 | l.release() | |||
|
40 | except LockHeld: | |||
|
41 | pass | |||
|
42 | ||||
|
43 | if os.path.exists(IDX_LOCATION): | |||
|
44 | shutil.rmtree(IDX_LOCATION) | |||
|
45 | ||||
|
46 | create_index('/tmp/*', True) | |||
28 |
|
47 | |||
29 | environ = {} |
|
48 | environ = {} | |
30 |
|
49 | |||
31 | class TestController(TestCase): |
|
50 | class TestController(TestCase): | |
32 |
|
51 | |||
33 | def __init__(self, *args, **kwargs): |
|
52 | def __init__(self, *args, **kwargs): | |
34 | wsgiapp = pylons.test.pylonsapp |
|
53 | wsgiapp = pylons.test.pylonsapp | |
35 | config = wsgiapp.config |
|
54 | config = wsgiapp.config | |
36 | self.app = TestApp(wsgiapp) |
|
55 | self.app = TestApp(wsgiapp) | |
37 | url._push_object(URLGenerator(config['routes.map'], environ)) |
|
56 | url._push_object(URLGenerator(config['routes.map'], environ)) | |
38 | self.sa = meta.Session |
|
57 | self.sa = meta.Session | |
|
58 | ||||
39 | TestCase.__init__(self, *args, **kwargs) |
|
59 | TestCase.__init__(self, *args, **kwargs) | |
40 |
|
60 | |||
41 |
|
61 | |||
42 | def log_user(self): |
|
62 | def log_user(self): | |
43 | response = self.app.post(url(controller='login', action='index'), |
|
63 | response = self.app.post(url(controller='login', action='index'), | |
44 | {'username':'test_admin', |
|
64 | {'username':'test_admin', | |
45 | 'password':'test'}) |
|
65 | 'password':'test'}) | |
46 | assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status |
|
66 | assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status | |
47 | assert response.session['hg_app_user'].username == 'test_admin', 'wrong logged in user' |
|
67 | assert response.session['hg_app_user'].username == 'test_admin', 'wrong logged in user' | |
48 | return response.follow() |
|
68 | return response.follow() | |
49 | No newline at end of file |
|
69 | ||
|
70 |
@@ -1,139 +1,139 b'' | |||||
1 | from pylons_app.tests import * |
|
1 | from pylons_app.tests import * | |
2 | from pylons_app.model.db import User |
|
2 | from pylons_app.model.db import User | |
3 | from pylons_app.lib.auth import check_password |
|
3 | from pylons_app.lib.auth import check_password | |
4 |
|
4 | |||
5 |
|
5 | |||
6 | class TestLoginController(TestController): |
|
6 | class TestLoginController(TestController): | |
7 |
|
7 | |||
8 | def test_index(self): |
|
8 | def test_index(self): | |
9 | response = self.app.get(url(controller='login', action='index')) |
|
9 | response = self.app.get(url(controller='login', action='index')) | |
10 | assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status |
|
10 | assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status | |
11 | # Test response... |
|
11 | # Test response... | |
12 |
|
12 | |||
13 | def test_login_admin_ok(self): |
|
13 | def test_login_admin_ok(self): | |
14 | response = self.app.post(url(controller='login', action='index'), |
|
14 | response = self.app.post(url(controller='login', action='index'), | |
15 | {'username':'test_admin', |
|
15 | {'username':'test_admin', | |
16 | 'password':'test'}) |
|
16 | 'password':'test'}) | |
17 | assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status |
|
17 | assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status | |
18 | assert response.session['hg_app_user'].username == 'test_admin', 'wrong logged in user' |
|
18 | assert response.session['hg_app_user'].username == 'test_admin', 'wrong logged in user' | |
19 | response = response.follow() |
|
19 | response = response.follow() | |
20 | assert 'auto description for vcs_test' in response.body |
|
20 | assert 'auto description for vcs_test' in response.body | |
21 |
|
21 | |||
22 | def test_login_regular_ok(self): |
|
22 | def test_login_regular_ok(self): | |
23 | response = self.app.post(url(controller='login', action='index'), |
|
23 | response = self.app.post(url(controller='login', action='index'), | |
24 | {'username':'test_regular', |
|
24 | {'username':'test_regular', | |
25 | 'password':'test'}) |
|
25 | 'password':'test'}) | |
26 | assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status |
|
26 | assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status | |
27 | assert response.session['hg_app_user'].username == 'test_regular', 'wrong logged in user' |
|
27 | assert response.session['hg_app_user'].username == 'test_regular', 'wrong logged in user' | |
28 | response = response.follow() |
|
28 | response = response.follow() | |
29 | assert 'auto description for vcs_test' in response.body |
|
29 | assert 'auto description for vcs_test' in response.body | |
30 | assert '<a title="Admin" href="/_admin">' not in response.body |
|
30 | assert '<a title="Admin" href="/_admin">' not in response.body | |
31 |
|
31 | |||
32 | def test_login_ok_came_from(self): |
|
32 | def test_login_ok_came_from(self): | |
33 | test_came_from = '/_admin/users' |
|
33 | test_came_from = '/_admin/users' | |
34 | response = self.app.post(url(controller='login', action='index', came_from=test_came_from), |
|
34 | response = self.app.post(url(controller='login', action='index', came_from=test_came_from), | |
35 | {'username':'test_admin', |
|
35 | {'username':'test_admin', | |
36 | 'password':'test'}) |
|
36 | 'password':'test'}) | |
37 | assert response.status == '302 Found', 'Wrong response code from came from redirection' |
|
37 | assert response.status == '302 Found', 'Wrong response code from came from redirection' | |
38 | response = response.follow() |
|
38 | response = response.follow() | |
39 |
|
39 | |||
40 | assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status |
|
40 | assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status | |
41 | assert 'Users administration' in response.body, 'No proper title in response' |
|
41 | assert 'Users administration' in response.body, 'No proper title in response' | |
42 |
|
42 | |||
43 |
|
43 | |||
44 | def test_login_wrong(self): |
|
44 | def test_login_wrong(self): | |
45 | response = self.app.post(url(controller='login', action='index'), |
|
45 | response = self.app.post(url(controller='login', action='index'), | |
46 | {'username':'error', |
|
46 | {'username':'error', | |
47 | 'password':'test'}) |
|
47 | 'password':'test'}) | |
48 | assert response.status == '200 OK', 'Wrong response from login page' |
|
48 | assert response.status == '200 OK', 'Wrong response from login page' | |
49 |
|
49 | |||
50 | assert 'invalid user name' in response.body, 'No error username message in response' |
|
50 | assert 'invalid user name' in response.body, 'No error username message in response' | |
51 | assert 'invalid password' in response.body, 'No error password message in response' |
|
51 | assert 'invalid password' in response.body, 'No error password message in response' | |
52 |
|
52 | |||
53 |
|
53 | |||
54 | def test_register(self): |
|
54 | def test_register(self): | |
55 | response = self.app.get(url(controller='login', action='register')) |
|
55 | response = self.app.get(url(controller='login', action='register')) | |
56 | assert 'Sign Up to hg-app' in response.body, 'wrong page for user registration' |
|
56 | assert 'Sign Up to hg-app' in response.body, 'wrong page for user registration' | |
57 |
|
57 | |||
58 | def test_register_err_same_username(self): |
|
58 | def test_register_err_same_username(self): | |
59 | response = self.app.post(url(controller='login', action='register'), |
|
59 | response = self.app.post(url(controller='login', action='register'), | |
60 | {'username':'test_admin', |
|
60 | {'username':'test_admin', | |
61 | 'password':'test', |
|
61 | 'password':'test', | |
62 | 'email':'goodmail@domain.com', |
|
62 | 'email':'goodmail@domain.com', | |
63 | 'name':'test', |
|
63 | 'name':'test', | |
64 | 'lastname':'test'}) |
|
64 | 'lastname':'test'}) | |
65 |
|
65 | |||
66 | assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status |
|
66 | assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status | |
67 | assert 'This username already exists' in response.body |
|
67 | assert 'This username already exists' in response.body | |
68 |
|
68 | |||
69 | def test_register_err_wrong_data(self): |
|
69 | def test_register_err_wrong_data(self): | |
70 | response = self.app.post(url(controller='login', action='register'), |
|
70 | response = self.app.post(url(controller='login', action='register'), | |
71 | {'username':'xs', |
|
71 | {'username':'xs', | |
72 | 'password':'', |
|
72 | 'password':'', | |
73 | 'email':'goodmailm', |
|
73 | 'email':'goodmailm', | |
74 | 'name':'test', |
|
74 | 'name':'test', | |
75 | 'lastname':'test'}) |
|
75 | 'lastname':'test'}) | |
76 |
|
76 | |||
77 | assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status |
|
77 | assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status | |
78 | assert 'An email address must contain a single @' in response.body |
|
78 | assert 'An email address must contain a single @' in response.body | |
79 | assert 'Enter a value 3 characters long or more' in response.body |
|
79 | assert 'Enter a value 3 characters long or more' in response.body | |
80 | assert 'Please enter a value<' in response.body |
|
80 | assert 'Please enter a value<' in response.body | |
81 |
|
81 | |||
82 |
|
82 | |||
83 |
|
83 | |||
84 | def test_register_ok(self): |
|
84 | def test_register_ok(self): | |
85 | username = 'test_regular4' |
|
85 | username = 'test_regular4' | |
86 | password = 'qweqwe' |
|
86 | password = 'qweqwe' | |
87 |
email = 'marcin@ |
|
87 | email = 'marcin@test.com' | |
88 | name = 'testname' |
|
88 | name = 'testname' | |
89 | lastname = 'testlastname' |
|
89 | lastname = 'testlastname' | |
90 |
|
90 | |||
91 | response = self.app.post(url(controller='login', action='register'), |
|
91 | response = self.app.post(url(controller='login', action='register'), | |
92 | {'username':username, |
|
92 | {'username':username, | |
93 | 'password':password, |
|
93 | 'password':password, | |
94 | 'email':email, |
|
94 | 'email':email, | |
95 | 'name':name, |
|
95 | 'name':name, | |
96 | 'lastname':lastname}) |
|
96 | 'lastname':lastname}) | |
97 | print response.body |
|
97 | print response.body | |
98 | assert response.status == '302 Found', 'Wrong response from register page got %s' % response.status |
|
98 | assert response.status == '302 Found', 'Wrong response from register page got %s' % response.status | |
99 | assert 'You have successfully registered into hg-app' in response.session['flash'][0], 'No flash message about user registration' |
|
99 | assert 'You have successfully registered into hg-app' in response.session['flash'][0], 'No flash message about user registration' | |
100 |
|
100 | |||
101 | ret = self.sa.query(User).filter(User.username == 'test_regular4').one() |
|
101 | ret = self.sa.query(User).filter(User.username == 'test_regular4').one() | |
102 | assert ret.username == username , 'field mismatch %s %s' % (ret.username, username) |
|
102 | assert ret.username == username , 'field mismatch %s %s' % (ret.username, username) | |
103 | assert check_password(password,ret.password) == True , 'password mismatch' |
|
103 | assert check_password(password, ret.password) == True , 'password mismatch' | |
104 | assert ret.email == email , 'field mismatch %s %s' % (ret.email, email) |
|
104 | assert ret.email == email , 'field mismatch %s %s' % (ret.email, email) | |
105 | assert ret.name == name , 'field mismatch %s %s' % (ret.name, name) |
|
105 | assert ret.name == name , 'field mismatch %s %s' % (ret.name, name) | |
106 | assert ret.lastname == lastname , 'field mismatch %s %s' % (ret.lastname, lastname) |
|
106 | assert ret.lastname == lastname , 'field mismatch %s %s' % (ret.lastname, lastname) | |
107 |
|
107 | |||
108 |
|
108 | |||
109 | def test_forgot_password_wrong_mail(self): |
|
109 | def test_forgot_password_wrong_mail(self): | |
110 | response = self.app.post(url(controller='login', action='password_reset'), |
|
110 | response = self.app.post(url(controller='login', action='password_reset'), | |
111 | {'email':'marcin@wrongmail.org',}) |
|
111 | {'email':'marcin@wrongmail.org', }) | |
112 |
|
112 | |||
113 | assert "That e-mail address doesn't exist" in response.body,'Missing error message about wrong email' |
|
113 | assert "That e-mail address doesn't exist" in response.body, 'Missing error message about wrong email' | |
114 |
|
114 | |||
115 | def test_forgot_password(self): |
|
115 | def test_forgot_password(self): | |
116 | response = self.app.get(url(controller='login', action='password_reset')) |
|
116 | response = self.app.get(url(controller='login', action='password_reset')) | |
117 | assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status |
|
117 | assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status | |
118 |
|
118 | |||
119 | username = 'test_password_reset_1' |
|
119 | username = 'test_password_reset_1' | |
120 | password = 'qweqwe' |
|
120 | password = 'qweqwe' | |
121 | email = 'marcin@python-works.com' |
|
121 | email = 'marcin@python-works.com' | |
122 | name = 'passwd' |
|
122 | name = 'passwd' | |
123 | lastname = 'reset' |
|
123 | lastname = 'reset' | |
124 |
|
124 | |||
125 | response = self.app.post(url(controller='login', action='register'), |
|
125 | response = self.app.post(url(controller='login', action='register'), | |
126 | {'username':username, |
|
126 | {'username':username, | |
127 | 'password':password, |
|
127 | 'password':password, | |
128 | 'email':email, |
|
128 | 'email':email, | |
129 | 'name':name, |
|
129 | 'name':name, | |
130 | 'lastname':lastname}) |
|
130 | 'lastname':lastname}) | |
131 | #register new user for email test |
|
131 | #register new user for email test | |
132 | response = self.app.post(url(controller='login', action='password_reset'), |
|
132 | response = self.app.post(url(controller='login', action='password_reset'), | |
133 | {'email':email,}) |
|
133 | {'email':email, }) | |
134 | print response.session['flash'] |
|
134 | print response.session['flash'] | |
135 | assert 'You have successfully registered into hg-app' in response.session['flash'][0], 'No flash message about user registration' |
|
135 | assert 'You have successfully registered into hg-app' in response.session['flash'][0], 'No flash message about user registration' | |
136 | assert 'Your new password was sent' in response.session['flash'][1], 'No flash message about password reset' |
|
136 | assert 'Your new password was sent' in response.session['flash'][1], 'No flash message about password reset' | |
137 |
|
137 | |||
138 |
|
138 | |||
139 |
|
139 |
@@ -1,29 +1,38 b'' | |||||
1 | from pylons_app.tests import * |
|
1 | from pylons_app.tests import * | |
2 | from pylons_app.lib.indexers import IDX_LOCATION |
|
2 | from pylons_app.lib.indexers import IDX_LOCATION | |
3 | import os |
|
3 | import os | |
4 | from nose.plugins.skip import SkipTest |
|
4 | from nose.plugins.skip import SkipTest | |
5 |
|
5 | |||
6 | class TestSearchController(TestController): |
|
6 | class TestSearchController(TestController): | |
7 |
|
7 | |||
8 | def test_index(self): |
|
8 | def test_index(self): | |
9 | self.log_user() |
|
9 | self.log_user() | |
10 | response = self.app.get(url(controller='search', action='index')) |
|
10 | response = self.app.get(url(controller='search', action='index')) | |
11 | print response.body |
|
11 | print response.body | |
12 | assert 'class="small" id="q" name="q" type="text"' in response.body,'Search box content error' |
|
12 | assert 'class="small" id="q" name="q" type="text"' in response.body, 'Search box content error' | |
13 | # Test response... |
|
13 | # Test response... | |
14 |
|
14 | |||
15 | def test_empty_search(self): |
|
15 | def test_empty_search(self): | |
16 |
|
16 | |||
17 | if os.path.isdir(IDX_LOCATION): |
|
17 | if os.path.isdir(IDX_LOCATION): | |
18 | raise SkipTest('skipped due to existing index') |
|
18 | raise SkipTest('skipped due to existing index') | |
19 | else: |
|
19 | else: | |
20 | self.log_user() |
|
20 | self.log_user() | |
21 | response = self.app.get(url(controller='search', action='index'),{'q':'vcs_test'}) |
|
21 | response = self.app.get(url(controller='search', action='index'), {'q':'vcs_test'}) | |
22 | assert 'There is no index to search in. Please run whoosh indexer' in response.body,'No error message about empty index' |
|
22 | assert 'There is no index to search in. Please run whoosh indexer' in response.body, 'No error message about empty index' | |
23 |
|
23 | |||
24 | def test_normal_search(self): |
|
24 | def test_normal_search(self): | |
25 | self.log_user() |
|
25 | self.log_user() | |
26 |
response = self.app.get(url(controller='search', action='index'),{'q':'def |
|
26 | response = self.app.get(url(controller='search', action='index'), {'q':'def repo'}) | |
27 | print response.body |
|
27 | print response.body | |
28 |
assert ' |
|
28 | assert '10 results' in response.body, 'no message about proper search results' | |
|
29 | assert 'Permission denied' not in response.body, 'Wrong permissions settings for that repo and user' | |||
29 |
|
30 | |||
|
31 | ||||
|
32 | def test_repo_search(self): | |||
|
33 | self.log_user() | |||
|
34 | response = self.app.get(url(controller='search', action='index'), {'q':'repository:vcs_test def test'}) | |||
|
35 | print response.body | |||
|
36 | assert '4 results' in response.body, 'no message about proper search results' | |||
|
37 | assert 'Permission denied' not in response.body, 'Wrong permissions settings for that repo and user' | |||
|
38 |
General Comments 0
You need to be logged in to leave comments.
Login now