Skip to content

Commit c3c7b73

Browse files
authored
RDISCROWD-8397: Option to download reports from task browse (#1070)
* Option to download reports from task browse
1 parent f3d63e8 commit c3c7b73

File tree

10 files changed

+392
-8
lines changed

10 files changed

+392
-8
lines changed

pybossa/cache/users.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from pybossa.data_access import data_access_levels
3434
from pybossa.util import get_taskrun_date_range_sql_clause_params
3535
from flask import current_app
36+
from pybossa.cache import ONE_HOUR
3637

3738
session = db.slave_session
3839

@@ -411,6 +412,13 @@ def get_user_by_id(user_id):
411412
return user
412413

413414

415+
@memoize(timeout=ONE_DAY)
416+
def get_user_by_email(email):
417+
assert email is not None
418+
user = User.query.filter_by(email_addr=email).first()
419+
return user
420+
421+
414422
def get_user_profile_metadata(user_id):
415423
user = get_user_by_id(user_id)
416424
info = user.info or {} if user else {}

pybossa/cloud_store_api/s3.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,10 @@ def upload_email_attachment(content, filename, user_email, project_id=None):
258258

259259
# generate signature for authorised access to the attachment
260260
from pybossa.core import signer
261+
from pybossa.core import sentinel
262+
from pybossa.redis_lock import register_user_exported_report
263+
from pybossa.cache.users import get_user_by_email
264+
261265
payload = {"project_id": project_id} if project_id else {}
262266
payload["user_email"] = user_email
263267
signature = signer.dumps(payload)
@@ -284,6 +288,9 @@ def upload_email_attachment(content, filename, user_email, project_id=None):
284288
server_url = app.config.get('SERVER_URL')
285289
url = f"{server_url}/attachment/{signature}/{timestamp}-{secure_file_name}"
286290
app.logger.info("upload email attachment url %s", url)
291+
user_id = get_user_by_email(user_email).id
292+
cache_info = register_user_exported_report(user_id, url, sentinel.master)
293+
app.logger.info("Cache updated for exported report %s", cache_info)
287294
return url
288295

289296

pybossa/emailsvc.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ def send(self, message):
4747
self.request_type: {
4848
"recipients": message["recipients"],
4949
"subject": message["subject"],
50-
"body": message["body"]
50+
"body": message["body"],
51+
"bcc": message.get("bcc") or []
5152
}
5253
}
5354
response = requests.post(self.url, headers=self.headers, json=payload, verify=self.ssl_cert)

pybossa/jobs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
from pybossa.core import email_service
5050
from pybossa.cloud_store_api.s3 import upload_email_attachment
5151

52+
5253
MINUTE = 60
5354
IMPORT_TASKS_TIMEOUT = (20 * MINUTE)
5455
TASK_DELETE_TIMEOUT = (60 * MINUTE)
@@ -984,7 +985,6 @@ def export_tasks(current_user_email_addr, short_name,
984985
bucket_name = current_app.config.get('EXPORT_BUCKET')
985986
max_email_size = current_app.config.get('EXPORT_MAX_EMAIL_SIZE', float('Inf'))
986987
max_s3_upload_size = current_app.config.get('EXPORT_MAX_UPLOAD_SIZE', float('Inf'))
987-
988988
if len(content) > max_s3_upload_size and bucket_name:
989989
current_app.logger.info("Task export project id %s: Task export exceeded max size %d, actual size: %d",
990990
project.id, max_s3_upload_size, len(content))

pybossa/redis_lock.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,21 @@
1717
# along with PYBOSSA. If not, see <http://www.gnu.org/licenses/>.
1818

1919
import json
20-
from datetime import timedelta
20+
from datetime import timedelta, datetime
2121
from time import time
2222

2323
from pybossa.contributions_guard import ContributionsGuard
2424
from pybossa.core import sentinel
2525
from werkzeug.exceptions import BadRequest
26+
import os
2627

2728
TASK_USERS_KEY_PREFIX = 'pybossa:project:task_requested:timestamps:{0}'
2829
USER_TASKS_KEY_PREFIX = 'pybossa:user:task_acquired:timestamps:{0}'
2930
TASK_ID_PROJECT_ID_KEY_PREFIX = 'pybossa:task_id:project_id:{0}'
3031
ACTIVE_USER_KEY = 'pybossa:active_users_in_project:{}'
3132
EXPIRE_LOCK_DELAY = 5
3233
EXPIRE_RESERVE_TASK_LOCK_DELAY = 30*60
33-
34+
USER_EXPORTED_REPORTS_KEY = 'pybossa:user:exported:reports:{}'
3435

3536
def get_active_user_key(project_id):
3637
return ACTIVE_USER_KEY.format(project_id)
@@ -127,6 +128,35 @@ def get_locked_tasks_project(project_id):
127128
})
128129
return tasks
129130

131+
def get_user_exported_reports_key(user_id):
132+
# redis key to store exported reports for user_id
133+
return USER_EXPORTED_REPORTS_KEY.format(user_id)
134+
135+
def register_user_exported_report(user_id, path, conn, ttl=60*60):
136+
# register report path for user_id
137+
# reports are stored as hset with key as user_id and field as timestamp:path
138+
now = time()
139+
key = get_user_exported_reports_key(user_id)
140+
filename = os.path.basename(path)
141+
value = json.dumps({"filename": filename, "path": path})
142+
conn.hset(key, now, value)
143+
conn.expire(key, ttl)
144+
cache_info = f"Registered exported report for user_id {user_id} at {now} with value {value}"
145+
return cache_info
146+
147+
def get_user_exported_reports(user_id, conn):
148+
# obtain all reports for user_id
149+
# reports are stored as hset with key as user_id and field as timestamp:path
150+
# return list of (timestamp, path) tuples
151+
key = get_user_exported_reports_key(user_id)
152+
reports_data = conn.hgetall(key).items()
153+
result = []
154+
for k, v in reports_data:
155+
decoded_value = json.loads(v.decode())
156+
formatted_time = datetime.fromtimestamp(float(k.decode())).strftime('%Y-%m-%d %H:%M:%S:%f')[:-3]
157+
result.append((formatted_time, decoded_value['filename'], decoded_value['path']))
158+
return result
159+
130160

131161
class LockManager(object):
132162
"""

pybossa/view/account.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,6 +1178,9 @@ def get_bookmarks(user_name, short_name, order_by, desc):
11781178
proj_bookmarks = taskbrowse_bookmarks.get(short_name, {})
11791179
return bookmarks_dict_to_array(proj_bookmarks, order_by, desc)
11801180

1181+
def get_user_reports(user_name):
1182+
user_reports = cached_users.get_user_reports(user_name)
1183+
return user_reports
11811184

11821185
def add_bookmark(user_name, short_name, bookmark_name, bookmark_url, order_by, desc):
11831186

pybossa/view/projects.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
from sqlalchemy.orm.attributes import flag_modified
111111
from pybossa.util import admin_or_project_owner, validate_ownership_id
112112
from pybossa.api.project import ProjectAPI
113+
from pybossa.redis_lock import get_user_exported_reports
113114

114115
cors_headers = ['Content-Type', 'Authorization']
115116

@@ -1849,7 +1850,7 @@ def get_users_completed(task):
18491850
get_users_fullname(page_tasks, lambda task: get_users_completed(task), 'completed_users')
18501851

18511852
taskbrowse_bookmarks = get_bookmarks(current_user.name, short_name, None, None)
1852-
1853+
user_reports = get_user_exported_reports(current_user.id, sentinel.master)
18531854
valid_user_preferences = app_settings.upref_mdata.get_valid_user_preferences() \
18541855
if app_settings.upref_mdata else {}
18551856
language_options = valid_user_preferences.get('languages')
@@ -1882,7 +1883,8 @@ def get_users_completed(task):
18821883
allow_taskrun_edit=allow_taskrun_edit,
18831884
regular_user=regular_user,
18841885
admin_subadmin_coowner=admin_subadmin_coowner,
1885-
taskbrowse_bookmarks=taskbrowse_bookmarks)
1886+
taskbrowse_bookmarks=taskbrowse_bookmarks,
1887+
user_reports=user_reports)
18861888

18871889

18881890
return handle_content_type(data)

0 commit comments

Comments
 (0)