def test_05_app_stats_index(self): '''Test PRIVACY project stats privacy is respected''' # As Anonymou user admin, user, owner = UserFactory.create_batch(3) task = TaskFactory.create(n_answers=3) TaskRunFactory.create_batch(3, task=task) url = '/project/%s/stats' % task.project.short_name update_stats(task.project.id) res = self.app.get(url, follow_redirects=True) dom = BeautifulSoup(res.data) err_msg = 'Project Stats page should not be shown to anonymous users' assert dom.find(id='enforce_privacy') is not None, res.data # As Authenticated user but NOT ADMIN res = self.app.get(url + '?api_key=%s' % user.api_key, follow_redirects=True) dom = BeautifulSoup(res.data) err_msg = 'Project Stats page should not be shown to authenticated users' assert dom.find(id='enforce_privacy') is not None, err_msg self.signout # As Authenticated user but ADMIN res = self.app.get(url + '?api_key=%s' % admin.api_key, follow_redirects=True) dom = BeautifulSoup(res.data) err_msg = 'Project Stats page should be shown to admin users' assert dom.find(id='enforce_privacy') is None, err_msg self.signout()
def test_05_app_stats_index(self): '''Test PRIVACY project stats privacy is respected''' # As Anonymou user admin, user, owner = UserFactory.create_batch(3) task = TaskFactory.create(n_answers=3) TaskRunFactory.create_batch(3, task=task) url = '/project/%s/stats' % task.project.short_name update_stats(task.project.id) res = self.app.get(url, follow_redirects=True) assert 'This feature requires being logged in' in res.data, res.data # As Authenticated user but NOT ADMIN self.set_proj_passwd_cookie(task.project, user) res = self.app.get(url + '?api_key=%s' % user.api_key, follow_redirects=True) dom = BeautifulSoup(res.data) err_msg = 'Project Stats page should not be shown to authenticated users' assert dom.find(id='enforce_privacy') is not None, err_msg self.signout # As Authenticated user but ADMIN res = self.app.get(url + '?api_key=%s' % admin.api_key, follow_redirects=True) dom = BeautifulSoup(res.data) err_msg = 'Project Stats page should be shown to admin users' assert dom.find(id='enforce_privacy') is None, err_msg self.signout()
def test_07_user_public_profile_json(self): '''Test PRIVACY user public profile privacy is respected for API access''' # As Anonymous user admin, user, owner = UserFactory.create_batch(3) project = ProjectFactory.create(owner=owner) TaskRunFactory.create_batch(30, project=project) TaskRunFactory.create(user=owner) update_stats(project.id) url = '/account/%s' % owner.name # Use a full url to avoid redirection on API access. full_url = 'http://localhost%s/' % url res = self.app.get(full_url, content_type='application/json') data = json.loads(res.data) print(list(data.keys())) # this data should be public visible in user err_msg = 'name should be public' assert data['user']['name'] == owner.name, err_msg err_msg = 'fullname should be public' assert data['user']['fullname'] == owner.fullname, err_msg err_msg = 'rank should be public' assert 'rank' in data['user'], err_msg err_msg = 'score should be public' assert 'score' in data['user'], err_msg # this data should not be public in user err_msg = 'id should not be public' assert 'id' not in data['user'], err_msg err_msg = 'api_key should not be public' assert 'api_key' not in data['user'], err_msg err_msg = 'confirmation_email_sent should not be public' assert 'confirmation_email_sent' not in data['user'], err_msg err_msg = 'email_addr should not be public' assert 'email_addr' not in data['user'], err_msg err_msg = 'valid_email should not be public' assert 'valid_email' not in data['user'], err_msg # public projects data project = data['projects'][0] err_msg = 'info should be public' assert 'info' in project, err_msg err_msg = 'description should be public' assert 'description' in project, err_msg err_msg = 'short_name should be public' assert 'short_name' in project, err_msg err_msg = 'n_tasks should be public' assert 'n_tasks' in project, err_msg err_msg = 'n_volunteers should be public' assert 'n_volunteers' in project, err_msg err_msg = 'overall_progress should be public' assert 'overall_progress' in project, err_msg err_msg = 'name should be public' assert 'name' in project, err_msg # non public projects data # err_msg = 'id should not be public' # assert 'id' not in project, err_msg err_msg = 'secret_key should not be public' assert 'secret_key' not in project, err_msg err_msg = 'results should not be public' assert 'results' not in project['info'], err_msg err_msg = 'onesignal should not be public' assert 'onesignal' not in project['info'], err_msg
def get_project_stats(_id, short_name): # pragma: no cover """Get stats for project.""" import pybossa.cache.projects as cached_projects import pybossa.cache.project_stats as stats from flask import current_app # cached_projects.get_project(short_name) stats.update_stats(_id, current_app.config.get('GEO'))
def get_project_stats(_id, short_name): # pragma: no cover """Get stats for project.""" import pybossa.cache.projects as cached_projects import pybossa.cache.project_stats as stats from flask import current_app # cached_projects.get_project(short_name) stats.update_stats(_id, current_app.config.get('GEO'))
def test_check_contributing_state_completed_user_not_contributed(self): """Test check_contributing_state returns 'completed' for a project with all tasks completed even if the user has not contributed to it""" project = ProjectFactory.create() task = TaskFactory.create(project=project, n_answers=2) TaskRunFactory.create_batch(2, task=task) user = UserFactory.create() update_stats(project.id) contributing_state = helpers.check_contributing_state(project=project, user_id=user.id) assert task.state == 'completed', task.state assert contributing_state == 'completed', contributing_state
def send_weekly_stats_project(project_id): from pybossa.cache.project_stats import update_stats, get_stats from pybossa.core import project_repo from datetime import datetime project = project_repo.get(project_id) if project.owner.subscribed is False: return "Owner does not want updates by email" update_stats(project_id) dates_stats, hours_stats, users_stats = get_stats(project_id, geo=True, period='1 week') subject = "Weekly Update: %s" % project.name timeout = current_app.config.get('TIMEOUT') # Max number of completed tasks n_completed_tasks = 0 xy = zip(*dates_stats[3]['values']) n_completed_tasks = max(xy[1]) # Most active day xy = zip(*dates_stats[0]['values']) active_day = [xy[0][xy[1].index(max(xy[1]))], max(xy[1])] active_day[0] = datetime.fromtimestamp(active_day[0] / 1000).strftime('%A') body = render_template('/account/email/weeklystats.md', project=project, dates_stats=dates_stats, hours_stats=hours_stats, users_stats=users_stats, n_completed_tasks=n_completed_tasks, active_day=active_day, config=current_app.config) html = render_template('/account/email/weeklystats.html', project=project, dates_stats=dates_stats, hours_stats=hours_stats, users_stats=users_stats, active_day=active_day, n_completed_tasks=n_completed_tasks, config=current_app.config) mail_dict = dict(recipients=[project.owner.email_addr], subject=subject, body=body, html=html) job = dict(name=send_mail, args=[mail_dict], kwargs={}, timeout=timeout, queue='high') enqueue_job(job)
def send_weekly_stats_project(project_id): from pybossa.cache.project_stats import update_stats, get_stats from pybossa.core import project_repo from datetime import datetime project = project_repo.get(project_id) if project.owner.subscribed is False: return "Owner does not want updates by email" update_stats(project_id) dates_stats, hours_stats, users_stats = get_stats(project_id, geo=True, period='1 week') subject = "Weekly Update: %s" % project.name timeout = current_app.config.get('TIMEOUT') # Max number of completed tasks n_completed_tasks = 0 xy = zip(*dates_stats[3]['values']) n_completed_tasks = max(xy[1]) # Most active day xy = zip(*dates_stats[0]['values']) active_day = [xy[0][xy[1].index(max(xy[1]))], max(xy[1])] active_day[0] = datetime.fromtimestamp(active_day[0]/1000).strftime('%A') body = render_template('/account/email/weeklystats.md', project=project, dates_stats=dates_stats, hours_stats=hours_stats, users_stats=users_stats, n_completed_tasks=n_completed_tasks, active_day=active_day, config=current_app.config) html = render_template('/account/email/weeklystats.html', project=project, dates_stats=dates_stats, hours_stats=hours_stats, users_stats=users_stats, active_day=active_day, n_completed_tasks=n_completed_tasks, config=current_app.config) mail_dict = dict(recipients=[project.owner.email_addr], subject=subject, body=body, html=html) job = dict(name=send_mail, args=[mail_dict], kwargs={}, timeout=timeout, queue='high') enqueue_job(job)
def warm_project(_id, short_name, featured=False): if _id not in projects_cached: #cached_projects.get_project(short_name) #cached_projects.n_tasks(_id) #n_task_runs = cached_projects.n_task_runs(_id) #cached_projects.overall_progress(_id) #cached_projects.last_activity(_id) #cached_projects.n_completed_tasks(_id) #cached_projects.n_volunteers(_id) #cached_projects.browse_tasks(_id) #if n_task_runs >= 1000 or featured: # # print ("Getting stats for %s as it has %s task runs" % # # (short_name, n_task_runs)) stats.update_stats(_id, app.config.get('GEO')) projects_cached.append(_id)
def warm_project(_id, short_name, featured=False): if _id not in projects_cached: #cached_projects.get_project(short_name) #cached_projects.n_tasks(_id) #n_task_runs = cached_projects.n_task_runs(_id) #cached_projects.overall_progress(_id) #cached_projects.last_activity(_id) #cached_projects.n_completed_tasks(_id) #cached_projects.n_volunteers(_id) #cached_projects.browse_tasks(_id) #if n_task_runs >= 1000 or featured: # # print ("Getting stats for %s as it has %s task runs" % # # (short_name, n_task_runs)) stats.update_stats(_id, app.config.get('GEO')) projects_cached.append(_id)
def test_average_contribution_time_returns_average_contribution_time(self): project = ProjectFactory.create() task = TaskFactory.create(project=project) first_task_time = datetime.timedelta(0, 5) second_task_time = datetime.timedelta(0, 7) expected_average_time = datetime.timedelta(0, 6) now = datetime.datetime.utcnow() TaskRunFactory.create(task=task, created=now, finish_time=now + first_task_time) TaskRunFactory.create(task=task, created=now, finish_time=now + second_task_time) update_stats(project.id) average_time = cached_projects.average_contribution_time(project.id) assert average_time == expected_average_time.total_seconds( ), average_time
def prepare_stats(self, n=1): project = ProjectFactory.create() TaskFactory.create_batch(n, project=project) stats.update_stats(project.id) return stats.get_stats(project.id, full=True)
def test_03_stats(self): """Test STATS stats method works""" self.prepare_data() today = unicode(datetime.date.today()) hour = int(datetime.datetime.utcnow().strftime('%H')) date_ms = time.mktime(time.strptime(today, "%Y-%m-%d")) * 1000 anon = 0 auth = 0 tr1 = TaskRunFactory.create(task=self.project.tasks[0]) tr2 = TaskRunFactory.create(task=self.project.tasks[1]) user = user_repo.get(tr1.user_id) user.restrict = True user_repo.update(user) stats.update_stats(self.project.id) dates_stats, hours_stats, user_stats = stats.get_stats(self.project.id) for item in dates_stats: if item['label'] == 'Anon + Auth': assert item['values'][-1][0] == date_ms, item['values'][0][0] assert item['values'][-1][1] == 10, "There should be 10 answers" if item['label'] == 'Anonymous': assert item['values'][-1][0] == date_ms, item['values'][0][0] anon = item['values'][-1][1] if item['label'] == 'Authenticated': assert item['values'][-1][0] == date_ms, item['values'][0][0] auth = item['values'][-1][1] if item['label'] == 'Total Tasks': assert item['values'][-1][0] == date_ms, item['values'][0][0] assert item['values'][-1][1] == 4, "There should be 4 tasks" if item['label'] == 'Expected Answers': assert item['values'][0][0] == date_ms, item['values'][0][0] for i in item['values']: assert i[1] == 100, "Each date should have 100 answers" assert item['values'][0][1] == 100, "There should be 10 answers" assert auth + anon == 10, "date stats sum of auth and anon should be 10" max_hours = 0 for item in hours_stats: if item['label'] == 'Anon + Auth': max_hours = item['max'] print item assert item['max'] == 10, item['max'] assert item['max'] == 10, "Max hours value should be 10" for i in item['values']: if i[0] == hour: assert i[1] == 10, "There should be 10 answers" assert i[2] == 5, "The size of the bubble should be 5" else: assert i[1] == 0, "There should be 0 answers" assert i[2] == 0, "The size of the buggle should be 0" if item['label'] == 'Anonymous': anon = item['max'] for i in item['values']: if i[0] == hour: assert i[1] == anon, "There should be anon answers" assert i[2] == (anon * 5) / max_hours, "The size of the bubble should be 5" else: assert i[1] == 0, "There should be 0 answers" assert i[2] == 0, "The size of the buggle should be 0" if item['label'] == 'Authenticated': auth = item['max'] for i in item['values']: if i[0] == hour: assert i[1] == auth, "There should be anon answers" assert i[2] == (auth * 5) / max_hours, "The size of the bubble should be 5" else: assert i[1] == 0, "There should be 0 answers" assert i[2] == 0, "The size of the buggle should be 0" assert auth + anon == 10, "date stats sum of auth and anon should be 8" err_msg = "user stats sum of auth and anon should be 7" assert user_stats['n_anon'] + user_stats['n_auth'] == 7, err_msg for u in user_stats['auth']['top5']: assert u['restrict'] is False, u
def test_query_projectstats(self): """Test API query for project stats endpoint works""" project_stats = [] projects = ProjectFactory.create_batch(3) for project in projects: for task in TaskFactory.create_batch(4, project=project, n_answers=3): TaskRunFactory.create(task=task) stats.update_stats(project.id) ps = stats.get_stats(project.id, full=True) project_stats.append(ps) extra_stat_types = ['hours_stats', 'dates_stats', 'users_stats'] # As anon url = '/api/projectstats' res = self.app_get_json(url) data = json.loads(res.data) assert len(data) == 3, data # Limits res = self.app.get(url + "?limit=1") data = json.loads(res.data) assert len(data) == 1, data # Keyset pagination res = self.app.get(url + '?limit=1&last_id=' + str(projects[1].id)) data = json.loads(res.data) assert len(data) == 1, len(data) assert data[0]['id'] == project.id # Errors res = self.app.get(url + "?something") err = json.loads(res.data) err_msg = "AttributeError exception should be raised" res.status_code == 415, err_msg assert res.status_code == 415, err_msg assert err['action'] == 'GET', err_msg assert err['status'] == 'failed', err_msg assert err['exception_cls'] == 'AttributeError', err_msg # Desc filter url = "/api/projectstats?orderby=wrongattribute" res = self.app.get(url) data = json.loads(res.data) err_msg = "It should be 415." assert data['status'] == 'failed', data assert data['status_code'] == 415, data assert 'has no attribute' in data['exception_msg'], data # Order by url = "/api/projectstats?orderby=id" res = self.app.get(url) data = json.loads(res.data) err_msg = "It should get the last item first." ps_by_id = sorted(project_stats, key=lambda x: x.id, reverse=False) for i in range(len(project_stats)): assert ps_by_id[i].id == data[i]['id'] # Desc filter url = "/api/projectstats?orderby=id&desc=true" res = self.app.get(url) data = json.loads(res.data) err_msg = "It should get the last item first." ps_by_id = sorted(project_stats, key=lambda x: x.id, reverse=True) for i in range(len(project_stats)): assert ps_by_id[i].id == data[i]['id'] # Without full filter url = "/api/projectstats" res = self.app.get(url) data = json.loads(res.data) err_msg = "It should not return the full stats." extra = [ row['info'].get(_type) for _type in extra_stat_types for row in data if row['info'].get(_type) ] assert not extra # With full filter url = "/api/projectstats?full=1" res = self.app.get(url) data = json.loads(res.data) err_msg = "It should return full stats." for i, row in enumerate(data): for _type in extra_stat_types: assert row['info'][_type] == project_stats[i].info[_type]
def test_03_stats(self): """Test STATS stats method works""" self.prepare_data() today = str(datetime.date.today()) hour = int(datetime.datetime.utcnow().strftime('%H')) date_ms = time.mktime(time.strptime(today, "%Y-%m-%d")) * 1000 anon = 0 auth = 0 tr1 = TaskRunFactory.create(task=self.project.tasks[0]) tr2 = TaskRunFactory.create(task=self.project.tasks[1]) user = user_repo.get(tr1.user_id) user.restrict = True user_repo.update(user) stats.update_stats(self.project.id) dates_stats, hours_stats, user_stats = stats.get_stats(self.project.id) for item in dates_stats: if item['label'] == 'Anon + Auth': assert item['values'][-1][0] == date_ms, item['values'][0][0] assert item['values'][-1][ 1] == 10, "There should be 10 answers" if item['label'] == 'Anonymous': assert item['values'][-1][0] == date_ms, item['values'][0][0] anon = item['values'][-1][1] if item['label'] == 'Authenticated': assert item['values'][-1][0] == date_ms, item['values'][0][0] auth = item['values'][-1][1] if item['label'] == 'Total Tasks': assert item['values'][-1][0] == date_ms, item['values'][0][0] assert item['values'][-1][1] == 4, "There should be 4 tasks" if item['label'] == 'Expected Answers': assert item['values'][0][0] == date_ms, item['values'][0][0] for i in item['values']: assert i[1] == 100, "Each date should have 100 answers" assert item['values'][0][ 1] == 100, "There should be 10 answers" assert auth + anon == 10, "date stats sum of auth and anon should be 10" max_hours = 0 for item in hours_stats: if item['label'] == 'Anon + Auth': max_hours = item['max'] print(item) assert item['max'] == 10, item['max'] assert item['max'] == 10, "Max hours value should be 10" for i in item['values']: if i[0] == hour: assert i[1] == 10, "There should be 10 answers" assert i[2] == 5, "The size of the bubble should be 5" else: assert i[1] == 0, "There should be 0 answers" assert i[2] == 0, "The size of the buggle should be 0" if item['label'] == 'Anonymous': anon = item['max'] for i in item['values']: if i[0] == hour: assert i[1] == anon, "There should be anon answers" assert i[2] == ( anon * 5 ) / max_hours, "The size of the bubble should be 5" else: assert i[1] == 0, "There should be 0 answers" assert i[2] == 0, "The size of the buggle should be 0" if item['label'] == 'Authenticated': auth = item['max'] for i in item['values']: if i[0] == hour: assert i[1] == auth, "There should be anon answers" assert i[2] == ( auth * 5 ) / max_hours, "The size of the bubble should be 5" else: assert i[1] == 0, "There should be 0 answers" assert i[2] == 0, "The size of the buggle should be 0" assert auth + anon == 10, "date stats sum of auth and anon should be 8" err_msg = "user stats sum of auth and anon should be 7" assert user_stats['n_anon'] + user_stats['n_auth'] == 7, err_msg for u in user_stats['auth']['top5']: assert u['restrict'] is False, u
def prepare_stats(self): project = ProjectFactory.create() stats.update_stats(project.id) return stats.get_stats(project.id, full=True)
def test_query_projectstats(self): """Test API query for project stats endpoint works""" project_stats = [] projects = ProjectFactory.create_batch(3) for project in projects: for task in TaskFactory.create_batch(4, project=project, n_answers=3): TaskRunFactory.create(task=task) stats.update_stats(project.id) ps = stats.get_stats(project.id, full=True) project_stats.append(ps) extra_stat_types = ['hours_stats', 'dates_stats', 'users_stats'] # As anon url = '/api/projectstats' res = self.app_get_json(url) data = json.loads(res.data) assert len(data) == 3, data # Limits res = self.app.get(url + "?limit=1") data = json.loads(res.data) assert len(data) == 1, data # Keyset pagination res = self.app.get(url + '?limit=1&last_id=' + str(projects[1].id)) data = json.loads(res.data) assert len(data) == 1, len(data) assert data[0]['id'] == project.id # Errors res = self.app.get(url + "?something") err = json.loads(res.data) err_msg = "AttributeError exception should be raised" res.status_code == 415, err_msg assert res.status_code == 415, err_msg assert err['action'] == 'GET', err_msg assert err['status'] == 'failed', err_msg assert err['exception_cls'] == 'AttributeError', err_msg # Desc filter url = "/api/projectstats?orderby=wrongattribute" res = self.app.get(url) data = json.loads(res.data) err_msg = "It should be 415." assert data['status'] == 'failed', data assert data['status_code'] == 415, data assert 'has no attribute' in data['exception_msg'], data # Order by url = "/api/projectstats?orderby=id" res = self.app.get(url) data = json.loads(res.data) err_msg = "It should get the last item first." ps_by_id = sorted(project_stats, key=lambda x: x.id, reverse=False) for i in range(len(project_stats)): assert ps_by_id[i].id == data[i]['id'] # Desc filter url = "/api/projectstats?orderby=id&desc=true" res = self.app.get(url) data = json.loads(res.data) err_msg = "It should get the last item first." ps_by_id = sorted(project_stats, key=lambda x: x.id, reverse=True) for i in range(len(project_stats)): assert ps_by_id[i].id == data[i]['id'] # Without full filter url = "/api/projectstats" res = self.app.get(url) data = json.loads(res.data) err_msg = "It should not return the full stats." extra = [row['info'].get(_type) for _type in extra_stat_types for row in data if row['info'].get(_type)] assert not extra # With full filter url = "/api/projectstats?full=1" res = self.app.get(url) data = json.loads(res.data) err_msg = "It should return full stats." for i, row in enumerate(data): for _type in extra_stat_types: assert row['info'][_type] == project_stats[i].info[_type]
def test_07_user_public_profile_json(self): '''Test PRIVACY user public profile privacy is respected for API access''' # As Anonymous user admin, user, owner = UserFactory.create_batch(3) project = ProjectFactory.create(owner=owner) TaskRunFactory.create_batch(30, project=project) TaskRunFactory.create(user=owner) update_stats(project.id) url = '/account/%s' % owner.name # Use a full url to avoid redirection on API access. full_url = 'http://localhost%s/' % url res = self.app.get(full_url, content_type='application/json') data = json.loads(res.data) print data.keys() # this data should be public visible in user err_msg = 'name should be public' assert data['user']['name'] == owner.name, err_msg err_msg = 'fullname should be public' assert data['user']['fullname'] == owner.fullname, err_msg err_msg = 'rank should be public' assert 'rank' in data['user'], err_msg err_msg = 'score should be public' assert 'score' in data['user'], err_msg # this data should not be public in user err_msg = 'id should not be public' assert 'id' not in data['user'], err_msg err_msg = 'api_key should not be public' assert 'api_key' not in data['user'], err_msg err_msg = 'confirmation_email_sent should not be public' assert 'confirmation_email_sent' not in data['user'], err_msg err_msg = 'email_addr should not be public' assert 'email_addr' not in data['user'], err_msg err_msg = 'google_user_id should not be public' assert 'google_user_id' not in data['user'], err_msg err_msg = 'facebook_user_id should not be public' assert 'facebook_user_id' not in data['user'], err_msg err_msg = 'twitter_user_id should not be public' assert 'twitter_user_id' not in data['user'], err_msg err_msg = 'valid_email should not be public' assert 'valid_email' not in data['user'], err_msg # public projects data print data project = data['projects'][0] err_msg = 'info should be public' assert 'info' in project, err_msg err_msg = 'description should be public' assert 'description' in project, err_msg err_msg = 'short_name should be public' assert 'short_name' in project, err_msg err_msg = 'n_tasks should be public' assert 'n_tasks' in project, err_msg err_msg = 'n_volunteers should be public' assert 'n_volunteers' in project, err_msg err_msg = 'overall_progress should be public' assert 'overall_progress' in project, err_msg err_msg = 'name should be public' assert 'name' in project, err_msg # non public projects data # err_msg = 'id should not be public' # assert 'id' not in project, err_msg err_msg = 'secret_key should not be public' assert 'secret_key' not in project, err_msg err_msg = 'results should not be public' assert 'results' not in project['info'], err_msg err_msg = 'onesignal should not be public' assert 'onesignal' not in project['info'], err_msg
def prepare_stats(self, n=1): project = ProjectFactory.create() TaskFactory.create_batch(n, project=project) stats.update_stats(project.id) return stats.get_stats(project.id, full=True)