def approve_new_user(): """ Approves a new user from the approve new members form """ # retrieve the user id from the page arguments passed by the button user_id = request.args(0) user = db.auth_user[user_id] template_dict = { 'name': user.first_name, 'user_url': URL('people', 'user', args=[user_id], scheme=True, host=True), 'profile_url': URL('user', 'profile', scheme=True, host=True), 'h_and_s_url': URL('health_safety', 'health_and_safety', scheme=True, host=True), 'admin': auth.user.first_name + " " + auth.user.last_name } safe_mailer(to=user.email, subject='SAFE: registration approved', template='registration_approved.html', template_dict=template_dict) # update the registration key for that user ID to remove 'pending' db(db.auth_user.id == user_id).update(registration_key='') session.flash = CENTER(B('User approved.'), _style='color: green') redirect(URL('people', 'administer_new_users')) return
def reject_new_user(): """ Rejects a new user from the approve new members form """ # retrieve the user id from the page arguments passed by the button user_id = request.args(0) user = db.auth_user[user_id] template_dict = { 'name': user.first_name, 'admin': auth.user.first_name + " " + auth.user.last_name } safe_mailer(to=user.email, subject='SAFE: registration rejected', template='registration_rejected.html', template_dict=template_dict) # remove that row from the auth_user database db(db.auth_user.id == user_id).delete() session.flash = CENTER(B('User rejected.'), _style='color: green') redirect(URL('people', 'administer_new_users')) return
def send_weekly_summary(): """ Sends out an email to selected staff attaching a text representation of the upcoming research visit schedule and a health and safety summary of who is going to be on site. """ db = current.db # Recipients - anybody who is a member of the weekly_summary group others = db((db.auth_user.id == db.auth_membership.user_id) & (db.auth_group.id == db.auth_membership.group_id) & (db.auth_group.role == 'weekly_summary')).select( db.auth_user.email, db.auth_user.alternative_email) # Build the recipient list send_to = [] for recip in others: send_to.append(recip.alternative_email) send_to.append(recip.email) # clear out blanks send_to = [eml for eml in send_to if eml is not None and eml != ''] # get the file contents try: now = datetime.date.today().isoformat() attach = { 'SAFE_visits_{}.txt'.format(now): all_rv_summary_text(), 'Visitor_H_and_S_info_{}.pdf'.format(now): health_and_safety_report() } safe_mailer(subject='Weekly research visit summary', to=send_to, template='weekly_rv_summary.html', template_dict=dict(), attachment_string_objects=attach) # commit changes to the db - necessary for things running from models db.commit() return 'Weekly research visit summary emailed' except BaseException: raise RuntimeError('Failed to email weekly research visit summary')
def resend_email(): """ Resends the email with the given id """ mail_id = request.vars['mail_id'] rec = db.safe_web_email_log[mail_id] # populate the emailer function with the db contents, # recovering the dict from json and turning off the cc_info # since this decision was made on the original send and so # cc is already populated. # also need to extract multiple emails from formatted strings: # '|xxx@yyy|xxx@yyy|xxx@yyy\' def string_to_list(item): return [ml for ml in item.split('|') if ml != ''] if item is not None else None to = string_to_list(rec.email_to) cc = string_to_list(rec.email_cc) bcc = string_to_list(rec.email_bcc) status = safe_mailer(subject=rec.subject, template=rec.template, template_dict=simplejson.loads(rec.template_dict), to=to, cc=cc, cc_info=False, bcc=bcc, reply_to=rec.reply_to) status = 'sent' if status else 'failed' rec.update_record(status=status) session.flash = 'Attempted to resend email: ' + status redirect(URL('scheduler', 'email_failures')) return 'resent'
def blog_details(): """ This allows blogger to create or edit a blog post - existing posts are linked to from the My SAFE page for the creator. These records are not visible to all users, who would just read the blog post. """ # do we have a request for an existing blog post? blog_id = request.args(0) if blog_id is not None: record = db.blog_posts(blog_id) else: record = None if blog_id is not None and record is None: # avoid unknown blogs session.flash = B(CENTER('Invalid blog id'), _style='color:red;') redirect(URL('blogs', 'blogs')) elif record is None or record.user_id == auth.user.id or auth.has_membership( 'admin'): if record is None: buttons = [ TAG.BUTTON('Submit', _type="submit", _class="button btn btn-default", _style='padding: 5px 15px 5px 15px;', _name='create') ] readonly = False else: readonly = True if record.admin_status == 'Submitted' else False buttons = [ TAG.BUTTON('Update and resubmit', _type="submit", _class="button btn btn-default", _style='padding: 5px 15px 5px 15px;', _name='update') ] # provide a form to create or edit form = SQLFORM( db.blog_posts, readonly=readonly, record=record, buttons=buttons, fields=['thumbnail_figure', 'authors', 'title', 'content'], showid=False) if form.validate(onvalidation=validate_blog_post): req_keys = list(request.vars.keys()) # get and add a comment to the history hist_str = '[{}] {} {}\\n -- {}\\n' new_history = hist_str.format( datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%MZ'), auth.user.first_name, auth.user.last_name, 'Post created' if blog_id is None else "Post edited") if 'update' in req_keys: id = record.update_record( admin_history=new_history + record.admin_history, **db.blog_posts._filter_fields(form.vars)) id = id.id msg = CENTER( B('Blog post updated and resubmitted for approval.'), _style='color: green') elif 'create' in req_keys: id = db.blog_posts.insert(admin_history=new_history, **db.blog_posts._filter_fields( form.vars)) msg = CENTER( B('Blog post created and submitted for approval.'), _style='color: green') else: pass # Email the link template_dict = { 'name': auth.user.first_name, 'url': URL('blogs', 'blog_details', args=[id], scheme=True, host=True), 'submission_type': 'blog post' } safe_mailer(to=auth.user.email, subject='SAFE: blog post submitted', template='generic_submitted.html', template_dict=template_dict) session.flash = msg redirect(URL('blogs', 'blog_details', args=[id])) elif form.errors: response.flash = CENTER(B('Problems with the form, check below.'), _style='color: red') else: pass # package form into a panel if record is None: status = "" vis = "" else: status = DIV( approval_icons[record.admin_status], XML(' '), 'Status: ', XML(' '), record.admin_status, _class='col-sm-3', _style= 'padding: 5px 15px 5px 15px;background-color:lightgrey;color:black;' ) if record.hidden: vis = DIV( 'Hidden', _class='col-sm-1 col-sm-offset-1', _style= 'padding: 5px 15px 5px 15px;background-color:lightgrey;color:black;' ) else: vis = DIV( 'Visible', _class='col-sm-1 col-sm-offset-1', _style= 'padding: 5px 15px 5px 15px;background-color:lightgrey;color:black;' ) panel_header = DIV(H5('Blog post', _class='col-sm-7'), status, vis, _class='row', _style='margin:0px 0px') if readonly: content = XML(record.content) else: content = form.custom.widget.content form = FORM( form.custom.begin, DIV(DIV(panel_header, _class="panel-heading"), DIV(LABEL('Thumbnail Figure:', _class="control-label col-sm-2"), DIV(form.custom.widget.thumbnail_figure, _class="col-sm-10"), _class='row', _style='margin:10px 10px'), DIV(LABEL('Authors:', _class="control-label col-sm-2"), DIV(form.custom.widget.authors, _class="col-sm-10"), _class='row', _style='margin:10px 10px'), DIV(LABEL('Title:', _class="control-label col-sm-2"), DIV(form.custom.widget.title, _class="col-sm-10"), _class='row', _style='margin:10px 10px'), DIV(LABEL('Blog Content:', _class="control-label col-sm-2"), DIV(content, _class="col-sm-10"), _class='row', _style='margin:10px 10px'), DIV(form.custom.submit, _class='panel-footer'), _class="panel panel-primary"), form.custom.end) else: # security doesn't allow people editing other users blogs session.flash = CENTER( B('You do not have permission to edit this blog post.'), _style='color: red') redirect(URL('blogs', 'blog_post', args=blog_id)) # admin history display if record is not None and record.admin_history is not None: admin_history = DIV(DIV(H5('Admin History', ), _class="panel-heading"), DIV(XML(record.admin_history.replace( '\\n', '<br />'), sanitize=True, permitted_tags=['br/']), _class='panel_body'), DIV(_class="panel-footer"), _class='panel panel-primary') else: admin_history = DIV() ## ADMIN INTERFACE if record is not None and auth.has_membership( 'admin') and record.admin_status == 'Submitted': admin = admin_decision_form(['Resubmit', 'Approved']) if admin.process(formname='admin').accepted: # update record with decision admin_str = '[{}] {} {}\\n ** Decision: {}\\n ** Comments: {}\\n' new_history = admin_str.format( datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%MZ'), auth.user.first_name, auth.user.last_name, admin.vars.decision, admin.vars.comment) + record.admin_history record.update_record(admin_status=admin.vars.decision, admin_history=new_history) # pick an decision poster = record.user_id template_dict = { 'name': poster.first_name, 'url': URL('blogs', 'blog_details', args=[blog_id], scheme=True, host=True), 'public_url': URL('blogs', 'blog_post', args=[blog_id], scheme=True, host=True), 'admin': auth.user.first_name + ' ' + auth.user.last_name, 'submission_type': 'blog post' } # pick an decision if admin.vars.decision == 'Approved': safe_mailer(to=poster.email, subject='SAFE: blog post approved', template='generic_approved.html', template_dict=template_dict) msg = CENTER(B('Blog approval emailed to poster at {}.'.format( poster.email)), _style='color: green') elif admin.vars.decision == 'Resubmit': safe_mailer(to=poster.email, subject='SAFE: blog post requires resubmission', template='generic_resubmit.html', template_dict=template_dict) msg = CENTER(B( 'Blog resubmission emailed to poster at {}.'.format( poster.email)), _style='color: green') else: pass redirect(URL('blogs', 'administer_blogs')) session.flash = msg elif admin.errors: response.flash = CENTER( B('Errors in form, please check and resubmit'), _style='color: red') else: pass else: admin = DIV() # pass components to the view return dict(form=form, admin_history=admin_history, admin=admin)
def help_request_details(): """ This controller shows a SQLFORM to submit or update a request for help on a project. Only projects for which the logged in user is a coordinator are available. The controller then passes the response through validation before sending a confirmation email out. """ # do we have a request for an existing help request post? request_id = request.args(0) if request_id is not None: record = db.help_request(request_id) else: record = None if request_id is not None and record is None: # avoid unknown blogs session.flash = B(CENTER('Invalid vacancy advert id'), _style='color:red;') redirect(URL('marketplace', 'help_requests')) elif record is None or record.user_id == auth.user.id or auth.has_membership( 'admin'): if record is None: buttons = [ TAG.BUTTON('Submit', _type="submit", _class="button btn btn-default", _style='padding: 5px 15px 5px 15px;', _name='create') ] readonly = False else: readonly = True if record.admin_status == 'Submitted' else False fill_label = "Mark as available" if record.available else "Mark as unavailable" buttons = [ TAG.BUTTON(fill_label, _type="submit", _class="button btn btn-default", _style='padding: 5px 15px 5px 15px;', _name='available'), XML(' ') * 5, TAG.BUTTON('Update and resubmit', _type="submit", _class="button btn btn-default", _style='padding: 5px 15px 5px 15px;', _name='update') ] # Restrict the project choices # - find the acceptable project ID numbers if auth.has_membership('admin'): # admins can advertise on any project query = db(db.project_details.project_id > 0) else: # otherwise coordinators can advertise on their own projects query = db((db.project_members.user_id == auth.user.id) & (db.project_members.is_coordinator == 'T') & (db.project_members.project_id == db.project_id.id) & (db.project_details.project_id == db.project_id.id) & (db.project_details.admin_status == 'Approved')) # - modify the help_request project_id requirements within this controller db.help_request.project_id.requires = IS_IN_DB( query, db.project_details.project_id, '%(title)s', zero=None) # check to see if anything is available if query.count() == 0: form = CENTER( B('You are not registered as a coordinator of any projects.'), _style='color: red') else: form = SQLFORM(db.help_request, fields=[ 'project_id', 'start_date', 'end_date', 'work_description', 'vacancy_type', 'paid_position', 'url' ], readonly=readonly, record=record, buttons=buttons, showid=False) if form.validate(onvalidation=validate_help_request): req_keys = list(request.vars.keys()) if 'available' in req_keys: # provide a simple toggle option for the availability hist_str = '[{}] {} {}\\n -- Vacancy marked as {}\\n' new_history = hist_str.format( datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%MZ'), auth.user.first_name, auth.user.last_name, "available" if record.available else "unavailable") id = record.update_record(available=not record.available, admin_history=new_history + record.admin_history) id = id.id msg = CENTER(B('Vacancy availability updated.'), _style='color: green') else: # get and add a comment to the history hist_str = '[{}] {} {}\\n -- {}\\n' new_history = hist_str.format( datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%MZ'), auth.user.first_name, auth.user.last_name, 'Vacancy advert created' if request_id is None else "Vacancy advert updated") if 'update' in req_keys: id = record.update_record( admin_status='Submitted', admin_history=new_history + record.admin_history, **db.help_request._filter_fields(form.vars)) id = id.id msg = CENTER(B( 'Vacancy advert updated and resubmitted for approval.' ), _style='color: green') elif 'create' in req_keys: id = db.help_request.insert( admin_status='Submitted', admin_history=new_history, **db.help_request._filter_fields(form.vars)) msg = CENTER(B( 'Vacancy advert created and submitted for approval.' ), _style='color: green') else: pass # Email the link template_dict = { 'name': auth.user.first_name, 'url': URL('marketplace', 'help_request_details', args=[id], scheme=True, host=True), 'submission_type': 'project help request' } safe_mailer(to=auth.user.email, subject='SAFE: Vacancy advert submitted', template='generic_submitted.html', template_dict=template_dict) session.flash = msg redirect(URL('marketplace', 'help_request_details', args=[id])) elif form.errors: response.flash = CENTER( B('Problems with the form, check below.'), _style='color: red') else: pass # package form into a panel if record is None: status = "" # vis = "" else: status = DIV( approval_icons[record.admin_status], XML(' '), 'Status: ', XML(' '), record.admin_status, _class='col-sm-3', _style= 'padding: 5px 15px 5px 15px;background-color:lightgrey;color:black;' ) # if record.hidden: # vis = DIV('Hidden', _class='col-sm-1 col-sm-offset-1', # _style='padding: 5px 15px 5px 15px;background-color:lightgrey;color:black;') # else: # vis = DIV('Visible', _class='col-sm-1 col-sm-offset-1', # _style='padding: 5px 15px 5px 15px;background-color:lightgrey;color:black;') panel_header = DIV( H5('Project vacancy advert', _class='col-sm-9'), status, # vis, _class='row', _style='margin:0px 10px') # package in controller if not readonly: form.custom.widget.work_description['_rows'] = 4 form.custom.widget.start_date[ '_class'] = "form-control input-sm" form.custom.widget.end_date['_class'] = "form-control input-sm" # set up availability header if record is not None: if record.available: avail = DIV(LABEL('Availability:', _class="control-label col-sm-2"), DIV("Vacancy is currently available", _class="col-sm-10"), _class='row', _style='margin:10px 10px') else: avail = DIV(LABEL('Availability:', _class="control-label col-sm-2"), DIV("Vacancy is marked as unavailable", _class="col-sm-10"), _class='row', _style='margin:10px 10px') else: # blank DIV for new headers avail = DIV() # locally update the description representation to allow formatting db.help_request.work_description.represent = lambda text, row: PRE( text) form = FORM( DIV(DIV(panel_header, _class="panel-heading"), DIV(form.custom.begin, avail, DIV(LABEL('Project:', _class="control-label col-sm-2"), DIV(form.custom.widget.project_id, _class="col-sm-10"), _class='row', _style='margin:10px 10px'), DIV(LABEL('Work description:', _class="control-label col-sm-2"), DIV(form.custom.widget.work_description, _class="col-sm-10"), _class='row', _style='margin:10px 10px'), DIV(LABEL('Dates:', _class="control-label col-sm-2"), DIV(DIV(form.custom.widget.start_date, SPAN('to', _class="input-group-addon input-sm"), form.custom.widget.end_date, _class="input-daterange input-group", _id="help_datepicker"), _class='col-sm-10'), _class='row', _style='margin:10px 10px'), HR(), DIV(P( 'Choose a vacancy type. If the post is paid, check the box and where ' 'possible provide a link for any further details and the application procedure.' ), _class='row', _style='margin:10px 10px'), DIV(LABEL('Vacancy type:', _class="control-label col-sm-2"), DIV(form.custom.widget.vacancy_type, _class='col-sm-7'), LABEL(form.custom.widget.paid_position, 'Paid Position', _class="control-label col-sm-3"), _class='row', _style='margin:10px 10px'), DIV(LABEL('Website for details', _class="control-label col-sm-2"), DIV(form.custom.widget.url, _class="col-sm-10"), _class='row', _style='margin:10px 10px'), DIV(DIV(form.custom.submit, _class="col-sm-10 col-sm-offset-2"), _class='row', _style='margin:10px 10px'), form.custom.end, _class='panel_body'), _class="panel panel-primary"), datepicker_script(html_id='help_datepicker', autoclose='true', startDate='"+0d"', endDate='""')) else: # security doesn't allow people editing other users blogs session.flash = CENTER( B('You do not have permission to edit this vacancy advert.'), _style='color: red') redirect(URL('marketplace', 'help_requests', args=request_id)) # admin history display if record is not None and record.admin_history is not None: admin_history = DIV(DIV(H5('Admin History', ), _class="panel-heading"), DIV(XML(record.admin_history.replace( '\\n', '<br />'), sanitize=True, permitted_tags=['br/']), _class='panel_body'), DIV(_class="panel-footer"), _class='panel panel-primary') else: admin_history = DIV() ## ADMIN INTERFACE if record is not None and auth.has_membership( 'admin') and record.admin_status == 'Submitted': admin = admin_decision_form(['Resubmit', 'Approved']) if admin.process(formname='admin').accepted: # update record with decision admin_str = '[{}] {} {}\\n ** Decision: {}\\n ** Comments: {}\\n' new_history = admin_str.format( datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%MZ'), auth.user.first_name, auth.user.last_name, admin.vars.decision, admin.vars.comment) + record.admin_history record.update_record(admin_status=admin.vars.decision, admin_history=new_history) # pick an decision poster = record.user_id template_dict = { 'name': poster.first_name, 'url': URL('marketplace', 'help_request_details', args=[request_id], scheme=True, host=True), 'public_url': URL('marketplace', 'view_help_request', args=[request_id], scheme=True, host=True), 'admin': auth.user.first_name + ' ' + auth.user.last_name, 'submission_type': 'project help request' } # pick an decision if admin.vars.decision == 'Approved': safe_mailer(to=poster.email, subject='SAFE: vacancy advert approved', template='generic_approved.html', template_dict=template_dict) msg = CENTER(B( 'Vacancy advert approval emailed to poster at {}.'.format( poster.email)), _style='color: green') elif admin.vars.decision == 'Resubmit': safe_mailer( to=poster.email, subject='SAFE: Vacancy advert requires resubmission', template='generic_resubmit.html', template_dict=template_dict) msg = CENTER( B('Vacancy advert resubmission emailed to poster at {}.'. format(poster.email)), _style='color: green') else: pass redirect(URL('marketplace', 'administer_help_requests')) session.flash = msg elif admin.errors: response.flash = CENTER( B('Errors in form, please check and resubmit'), _style='color: red') else: pass else: admin = DIV() # pass components to the view return dict(form=form, admin_history=admin_history, admin=admin)
def volunteer_details(): """ This controller shows a SQLFORM to submit or update an offer to volunteer at the SAFE project. The controller then passes the response through validation before sending a confirmation email out. """ # do we have a request for an existing help request post? request_id = request.args(0) if request_id is not None: record = db.help_offered(request_id) else: record = None if request_id is not None and record is None: # avoid unknown blogs session.flash = B(CENTER('Invalid volunteer offer id'), _style='color:red;') redirect(URL('marketplace', 'volunteers')) elif record is None or record.user_id == auth.user.id or auth.has_membership( 'admin'): if record is None: buttons = [ TAG.BUTTON('Submit', _type="submit", _class="button btn btn-default", _style='padding: 5px 15px 5px 15px;', _name='create') ] readonly = False else: readonly = True if record.admin_status == 'Submitted' else False buttons = [ TAG.BUTTON('Update and resubmit', _type="submit", _class="button btn btn-default", _style='padding: 5px 15px 5px 15px;', _name='update') ] db.help_offered.research_areas.comment = 'Select at least one research area of interest to help match you to projects.' form = SQLFORM(db.help_offered, fields=[ 'volunteer_type', 'statement_of_interests', 'research_areas', 'available_from', 'available_to' ], record=record, buttons=buttons, comments=True, showid=False, readonly=readonly) if form.validate(onvalidation=validate_volunteer): req_keys = list(request.vars.keys()) # get and add a comment to the history hist_str = '[{}] {} {}\\n -- {}\\n' new_history = hist_str.format( datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%MZ'), auth.user.first_name, auth.user.last_name, 'Volunteer offer created' if request_id is None else "Volunteer offer updated") if 'update' in req_keys: id = record.update_record( admin_status='Submitted', admin_history=new_history + record.admin_history, **db.help_offered._filter_fields(form.vars)) id = id.id msg = CENTER( B('Volunteer offer updated and resubmitted for approval.'), _style='color: green') elif 'create' in req_keys: id = db.help_offered.insert(admin_status='Submitted', admin_history=new_history, **db.help_offered._filter_fields( form.vars)) msg = CENTER( B('Volunteer offer created and submitted for approval.'), _style='color: green') else: pass # Email the link template_dict = { 'name': auth.user.first_name, 'url': URL('marketplace', 'volunteer_details', args=[id], scheme=True, host=True), 'submission_type': 'volunteer offer' } safe_mailer(to=auth.user.email, subject='SAFE: volunteer offer submitted', template='generic_submitted.html', template_dict=template_dict) session.flash = msg redirect(URL('marketplace', 'volunteer_details', args=[id])) elif form.errors: response.flash = CENTER(B('Problems with the form, check below.'), _style='color: red') else: pass # package in controller if not readonly: form.custom.widget.statement_of_interests['_rows'] = 4 form.custom.widget.available_from[ '_class'] = "form-control input-sm" form.custom.widget.available_to['_class'] = "form-control input-sm" if record is None: status = "" # vis = "" else: status = DIV( approval_icons[record.admin_status], XML(' '), 'Status: ', XML(' '), record.admin_status, _class='col-sm-3', _style= 'padding: 5px 15px 5px 15px;background-color:lightgrey;color:black;' ) # if record.hidden: # vis = DIV('Hidden', _class='col-sm-1 col-sm-offset-1', # _style='padding: 5px 15px 5px 15px;background-color:lightgrey;color:black;') # else: # vis = DIV('Visible', _class='col-sm-1 col-sm-offset-1', # _style='padding: 5px 15px 5px 15px;background-color:lightgrey;color:black;') panel_header = DIV( H5('Project help request', _class='col-sm-9'), status, # vis, _class='row', _style='margin:0px 10px') form = FORM( DIV(DIV(panel_header, _class="panel-heading"), DIV(form.custom.begin, DIV(LABEL('Volunteer type:', _class="control-label col-sm-2"), DIV(form.custom.widget.volunteer_type, _class="col-sm-10"), _class='row', _style='margin:10px 10px'), DIV(LABEL('Statement of interests:', _class="control-label col-sm-2"), DIV(form.custom.widget.statement_of_interests, _class="col-sm-10"), _class='row', _style='margin:10px 10px'), DIV(LABEL('Research areas:', _class="control-label col-sm-2"), DIV(form.custom.widget.research_areas, _class="col-sm-10"), _class='row', _style='margin:10px 10px'), DIV(LABEL('Dates:', _class="control-label col-sm-2"), DIV(DIV(form.custom.widget.available_from, SPAN('to', _class="input-group-addon input-sm"), form.custom.widget.available_to, _class="input-daterange input-group", _id="vol_datepicker"), _class='col-sm-10'), _class='row', _style='margin:10px 10px'), DIV(DIV(form.custom.submit, _class="col-sm-10 col-sm-offset-2"), _class='row', _style='margin:10px 10px'), form.custom.end, _class='panel_body'), _class="panel panel-primary"), datepicker_script(html_id='vol_datepicker', autoclose='true', startDate='"+0d"', endDate='"+365d"')) else: # security doesn't allow people editing other users volunteer offers session.flash = CENTER( B('You do not have permission to edit this volunteer offer.'), _style='color: red') redirect(URL('marketplace', 'volunteers', args=request_id)) # admin history display if record is not None and record.admin_history is not None: admin_history = DIV(DIV(H5('Admin History', ), _class="panel-heading"), DIV(XML(record.admin_history.replace( '\\n', '<br />'), sanitize=True, permitted_tags=['br/']), _class='panel_body'), DIV(_class="panel-footer"), _class='panel panel-primary') else: admin_history = DIV() ## ADMIN INTERFACE if record is not None and auth.has_membership( 'admin') and record.admin_status == 'Submitted': admin = admin_decision_form(['Resubmit', 'Approved']) if admin.process(formname='admin').accepted: # update record with decision admin_str = '[{}] {} {}\\n ** Decision: {}\\n ** Comments: {}\\n' new_history = admin_str.format( datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%MZ'), auth.user.first_name, auth.user.last_name, admin.vars.decision, admin.vars.comment) + record.admin_history record.update_record(admin_status=admin.vars.decision, admin_history=new_history) # pick an decision poster = record.user_id template_dict = { 'name': poster.first_name, 'url': URL('marketplace', 'volunteer_details', args=[request_id], scheme=True, host=True), 'public_url': URL('marketplace', 'view_volunteer', args=[request_id], scheme=True, host=True), 'admin': auth.user.first_name + ' ' + auth.user.last_name, 'submission_type': 'volunteer offer' } # pick an decision if admin.vars.decision == 'Approved': safe_mailer(to=poster.email, subject='SAFE: volunteer offer approved', template='generic_approved.html', template_dict=template_dict) msg = CENTER(B( 'Volunteer offer approval emailed to poster at {}.'.format( poster.email)), _style='color: green') elif admin.vars.decision == 'Resubmit': safe_mailer( to=poster.email, subject='SAFE: volunteer offer requires resubmission', template='generic_resubmit.html', template_dict=template_dict) msg = CENTER( B('Volunteer offer resubmission emailed to poster at {}.'. format(poster.email)), _style='color: green') else: pass redirect(URL('marketplace', 'administer_volunteers')) session.flash = msg elif admin.errors: response.flash = CENTER( B('Errors in form, please check and resubmit'), _style='color: red') else: pass else: admin = DIV() # pass components to the view return dict(form=form, admin_history=admin_history, admin=admin)
def output_details(): """ This controller provides an interface to create a new output (when no request arguments are passed) shows a SQLFORM to submit an upload and it also exposes a form for existing users to tag that upload to a project and only projects for which the logged in user is a member are available. The controller then passes the response through validation before sending a confirmation email out. """ # do we have a request for an existing blog post output_id = request.args(0) if output_id is not None: record = db.outputs(output_id) buttons = [ TAG.button('Save edits', _type="submit", _name='save', _style='padding: 5px 15px 5px 15px;'), XML(' ') * 5, TAG.button('Submit output', _type="submit", _name='submit', _style='padding: 5px 15px 5px 15px;') ] else: record = None buttons = [ TAG.button('Create output', _type="submit", _name='create', _style='padding: 5px 15px 5px 15px;') ] if output_id is not None and record is None: # avoid unknown outputs session.flash = B(CENTER('Invalid output id'), _style='color:red;') redirect(URL('outputs', 'outputs')) elif output_id is not None and ((record.user_id != auth.user.id) & (not auth.has_membership('admin'))): # security check to stop people editing other users outputs session.flash = CENTER(B( 'You do not have permission to edit these output details, showing output view.' ), _style='color: red') redirect(URL('outputs', 'view_output', args=output_id)) else: # allow admins to view but not edit existing records and lock submitted versions if (record is not None) and ((record.user_id != auth.user.id) or (record.admin_status == 'Submitted')): readonly = True else: readonly = False # create the form form = SQLFORM(db.outputs, record=output_id, fields=[ 'thumbnail_figure', 'file', 'title', 'abstract', 'lay_summary', 'format', 'citation', 'doi', 'url' ], readonly=readonly, showid=False, buttons=buttons) # now intercept and parse the various inputs if form.process(onvalidation=validate_output_details, formname='outputs').accepted: # reload the record output_record = db.outputs(form.vars.id) template_dict = { 'name': auth.user.first_name, 'url': URL('outputs', 'output_details', args=[output_record.id], scheme=True, host=True) } if form.submit: # Submit button pressed: Signal success and email the proposer safe_mailer(to=auth.user.email, subject='SAFE: project output submitted', template='output_submitted.html', template_dict=template_dict) session.flash = CENTER(B('SAFE project output submitted.'), _style='color: green') # create a comment to add to the history history_text = 'Output submitted' # update the status output_record.update_record( submission_date=datetime.date.today(), admin_status='Submitted') else: # is this brand new? If so send a link if record is None: safe_mailer(to=auth.user.email, subject='SAFE: project output draft created', template='output_created.html', template_dict=template_dict) session.flash = CENTER( B('SAFE project output draft created.'), _style='color: green') # create a comment to add to the history history_text = 'Output draft created' else: history_text = 'Output draft edited' session.flash = CENTER( B('SAFE project output draft created.'), _style='color: green') # create a comment to add to the history history_text = 'Output edited' # update the status - takes output back from approved output_record.update_record(admin_status='Draft') new_history = '[{}] {} {}\\n -- {}\\n'.format( datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%MZ'), auth.user.first_name, auth.user.last_name, history_text) if output_record.admin_history is None or output_record.admin_history == '': output_record.update_record(admin_history=new_history) else: output_record.update_record(admin_history=new_history + output_record.admin_history) # now send to the output_details page for the form we've just created redirect(URL('outputs', 'output_details', args=form.vars.id)) elif form.errors: response.flash = CENTER( B('Errors in form, please check and resubmit'), _style='color: red') else: pass # now repackage the form as a more attractive DIV if record is not None: # - thumbnail_figure if (record is None) or (record.thumbnail_figure in [None, 'NA', '']): pic = URL('static', 'images/default_thumbnails/missing_output.png') else: pic = URL('default', 'download', args=record.thumbnail_figure) # - file if record.file not in [None, 'NA', '']: # need to retrieve the file to get the original file name fn, stream = db.outputs.file.retrieve(record.file) stream.close() dfile = A(fn, _href=URL('default', 'download', args=record.file)) else: dfile = "" uploader = DIV( 'Uploaded by: ', record.user_id.first_name, ' ', record.user_id.last_name, _style='text-align:right; color=grey; font-size:smaller', _class='col-sm-8') else: pic = URL('static', 'images/default_thumbnails/missing_output.png') dfile = "" uploader = DIV() # make it clear the DOI should be a link if not readonly: form.custom.widget.doi["_placeholder"] = "http://dx.doi.org/" form = CAT( form.custom.begin, DIV(DIV(H5('Output details', ), _class="panel-heading"), DIV(DIV(LABEL('Title:', _class="control-label col-sm-2"), DIV(form.custom.widget.title, _class="col-sm-10"), _class='row'), DIV(DIV(DIV(LABEL('Upload Thumbnail:', _class="control-label col-sm-4"), DIV(form.custom.widget.thumbnail_figure, _class="col-sm-8"), _class='row'), DIV(LABEL('Current Thumbnail:', _class="control-label col-sm-4"), DIV(IMG(_src=pic, _height='100px'), _class='col-sm-8'), _class='row'), _class='col-sm-6'), DIV(DIV(LABEL('Upload Output File:', _class="control-label col-sm-4"), DIV(form.custom.widget.file, _class="col-sm-8"), _class='row'), DIV(LABEL('Current Output File:', _class="control-label col-sm-4"), DIV(dfile, _class='col-sm-8'), _class='row'), _class='col-sm-6'), _class='row'), DIV(LABEL('Scientific Abstract:', _class="control-label col-sm-2"), DIV(form.custom.widget.abstract, _class="col-sm-10"), _class='row'), DIV(LABEL('Lay Summary or Press Release:', _class="control-label col-sm-2"), DIV(form.custom.widget.lay_summary, _class="col-sm-10"), _class='row'), DIV(LABEL('Format:', _class="control-label col-sm-2"), DIV(form.custom.widget.format, _class="col-sm-10"), _class='row'), DIV(LABEL('Citation:', _class="control-label col-sm-2"), DIV(form.custom.widget.citation, _class="col-sm-10"), _class='row'), DIV(LABEL('URL for DOI:', _class="control-label col-sm-2"), DIV(form.custom.widget.doi, _class="col-sm-10"), _class='row'), DIV(LABEL('URL:', _class="control-label col-sm-2"), DIV(form.custom.widget.url, _class="col-sm-10"), _class='row'), _class='panel_body', _style='margin:10px 10px'), DIV(DIV(DIV(form.custom.submit, _class='col-sm-4'), uploader, _class='row'), _class='panel-footer'), _class="panel panel-primary"), form.custom.end) # Now handle adding projects: # - provide records of the projects already added and a restricted set # of projects that the user can chose if output_id is not None: # current projects projects_query = db( (db.project_outputs.output_id == output_id) & (db.project_outputs.project_id == db.project_id.id) & (db.project_id.project_details_id == db.project_details.id)) if projects_query.count() > 0: # select the rows and wrap up into a TABLE within a panel DIV projects_select = projects_query.select() projects_rows = [ TR( TD( A(r.project_details.title, _href=URL('projects', 'project_view', args=r.project_details.project_id))), TD()) for r in projects_select ] else: projects_rows = [] # wrap everything into a form with a list of projects to select linkable_query = db( (db.project_id.id == db.project_details.project_id) & (~db.project_id.id.belongs( projects_query.select(db.project_id.id)))) linkable = linkable_query.select(db.project_id.id, db.project_details.title, orderby=db.project_id.id) selector = SELECT(*[ OPTION('({}) {}'.format(r.project_id.id, r.project_details.title), _value=r.project_id.id) for r in linkable ], _class="generic-widget form-control", _name='project_id') add_button = TAG.BUTTON(add_member_icon, _type="submit", _style='background:none; border:none;' 'padding:0px 10px;font-size: 100%;') projects_rows.append(TR(selector, add_button)) projects = FORM( DIV(DIV(H5('Associated projects', ), _class="panel-heading"), TABLE(*projects_rows, _width='100%', _class='table table-striped'), DIV(_class="panel-footer"), _class='panel panel-primary')) if projects.process(formname='projects').accepted: # add the selected_project db.project_outputs.insert(project_id=projects.vars.project_id, output_id=output_id, user_id=auth.user.id, date_added=datetime.datetime.now()) # look for merged projects and add to the umbrella project as well project_record = db.project_id[projects.vars.project_id] if project_record.merged_to is not None: db.project_outputs.insert( project_id=project_record.merged_to, output_id=output_id, user_id=auth.user.id, date_added=datetime.datetime.now()) session.flash = CENTER(B('Output added to new project.'), _style='color: green') redirect(URL('outputs', 'output_details', args=output_id)) elif projects.errors: response.flash = CENTER(B('Problem adding project.'), _style='color: red') else: pass else: projects = DIV() # admin history display if record is not None and record.admin_history is not None: admin_history = DIV(DIV(H5('Admin History', ), _class="panel-heading"), DIV(XML(record.admin_history.replace( '\\n', '<br />'), sanitize=True, permitted_tags=['br/']), _class='panel_body'), DIV(_class="panel-footer"), _class='panel panel-primary') else: admin_history = DIV() # header text if record is None: header = CAT( H2('New Output Submission'), P( 'Please use the form below to create a new draft research output for the SAFE project. ', 'Once you have created the draft you will then be able to ', B('link your output '), 'to existing projects, make further edits and submit the completed outputs to the administrators.' ), P( 'Once you submit your output it will first be screened by an administrator. You will get an ', 'email to confirm that you have submitted an output and then another email to confirm ', 'whether the output has been accepted or not.')) else: header = CAT( H2(approval_icons[record.admin_status] + XML(' ') * 3 + record.title), P('Please use the form to edit your draft output and ', B('link your output '), 'to existing projects. When you have finished, click Submit.'), P( 'Once you submit your output it will first be screened by an administrator. You will get an ', 'email to confirm that you have submitted an output and then another email to confirm ', 'whether the output has been accepted or not.')) # Add an admin interface if record is not None and auth.has_membership('admin'): admin = admin_decision_form(selector_options=['Resubmit', 'Approved']) if admin.process(formname='admin').accepted: # add info to the history string admin_str = '[{}] {} {}\\n ** Decision: {}\\n ** Comments: {}\\n' new_history = admin_str.format( datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%MZ'), auth.user.first_name, auth.user.last_name, admin.vars.decision, admin.vars.comment) # update the admin history record.update_record(admin_status=admin.vars.decision, admin_history=new_history + record.admin_history) # set a flash message flash_message = CENTER(B( 'Decision emailed to project member at {}.'.format( record.user_id.email)), _style='color: green') # email the submitter admin_template_dict = { 'name': record.user_id.first_name, 'url': URL('outputs', 'output_details', args=[record.id], scheme=True, host=True), 'public_url': URL('outputs', 'view_output', args=[record.id], scheme=True, host=True), 'admin': auth.user.first_name + ' ' + auth.user.last_name } if admin.vars.decision == 'Approved': safe_mailer(to=record.user_id.email, subject='SAFE: project output approved', template='output_approved.html', template_dict=admin_template_dict) session.flash = flash_message elif admin.vars.decision == 'Resubmit': safe_mailer(to=record.user_id.email, subject='SAFE: resubmit project output', template='output_resubmit.html', template_dict=admin_template_dict) session.flash = flash_message else: pass # reload redirect(URL('outputs', 'output_details', args=output_id)) else: admin = DIV() return dict(form=form, projects=projects, admin_history=admin_history, header=header, admin=admin)
def remind_about_unknowns(): """ Daily emails to research visit proposers who haven't completed the visitor details for visits that start within the next week. """ db = current.db # get a list of research visits with <= a week to go. today = datetime.date.today() one_week_from_now = today + datetime.timedelta(days=7) # Select a summary to populate the task - implement # the SQL query below using the DAL: # select a.first_name, a.email, v.title, v.id, count(m.id) # from auth_user a join research_visit v on (a.id = v.proposer_id) # join research_visit_member m on (m.research_visit_id = v.id) # where m.user_id is null and # v.arrival_date <= now() + interval '7' day and # v.arrival_date > now() # group by a.first_name, a.email, v.title, v.id; # get the query structure of the data query = db( (db.research_visit.arrival_date <= one_week_from_now) & (db.research_visit.arrival_date > today) & (db.auth_user.id == db.research_visit.proposer_id) & (db.research_visit_member.research_visit_id == db.research_visit.id) & (db.research_visit_member.user_id is None)) offenders = query.select( db.auth_user.first_name, db.auth_user.email, db.research_visit.title, db.research_visit.id, db.research_visit_member.id.count().with_alias('count'), groupby=[ db.auth_user.first_name, db.auth_user.email, db.research_visit.id, db.research_visit.title ]) # now email each offender for offence in offenders: # dictionary to fill out the template template_dict = { 'name': offence.auth_user.first_name, 'title': offence.research_visit.title, 'count': offence.count, 'url': URL('research_visit', 'research_visit_details', args=[offence.research_visit.id], scheme='https', host='www.safeproject.net') } # email this offender safe_mailer(subject='SAFE Research Visit details', to=offence.auth_user.email, template='research_visit_details_reminder.html', template_dict=template_dict) # commit changes to the db - necessary for things running from models db.commit() ids = [str(o.research_visit.id) for o in offenders] if len(ids) > 0: return 'Emailed proposers of the following research visits: ' + ','.join( ids) else: return 'No incomplete research visits found within the next week'
def outdated_health_and_safety(): """ Daily emails to upcoming research visitors who haven't created their health and safety at all or haven't visited that link in more than 6 months """ db = current.db # Get some timestamps now = datetime.datetime.now() old_hs = (now - datetime.timedelta(days=180)).date() # Find people with bed reservations at SAFE or Maliau who have old or missing H&S # There may be a way of combining these two but it seems likely to be slower safe_off = db((db.bed_reservations_safe.arrival_date - now <= 14) & (db.bed_reservations_safe.departure_date >= now) & (db.bed_reservations_safe.research_visit_member_id == db.research_visit_member.id) & (db.research_visit_member.user_id == db.auth_user.id) & ((db.health_and_safety.id == None) | (db.health_and_safety.date_last_edited < old_hs))).select( db.auth_user.ALL, db.health_and_safety.ALL, left=db.health_and_safety.on( db.auth_user.id == db.health_and_safety.user_id), distinct=True, orderby=(db.auth_user.last_name, db.auth_user.first_name)) mali_off = db((db.bed_reservations_maliau.arrival_date - now <= 14) & (db.bed_reservations_maliau.departure_date >= now) & (db.bed_reservations_maliau.research_visit_member_id == db.research_visit_member.id) & (db.research_visit_member.user_id == db.auth_user.id) & ((db.health_and_safety.id == None) | (db.health_and_safety.date_last_edited < old_hs))).select( db.auth_user.ALL, db.health_and_safety.ALL, left=db.health_and_safety.on( db.auth_user.id == db.health_and_safety.user_id), distinct=True, orderby=(db.auth_user.last_name, db.auth_user.first_name)) offenders = safe_off + mali_off # now email each offender for offence in offenders: # dictionary to fill out the template template_dict = {'name': offence.auth_user.first_name} # email this offender safe_mailer( subject='SAFE Research Visit health and safety information', to=offence.auth_user.email, template='research_visit_health_and_safety.html', template_dict=template_dict) # commit changes to the db - necessary for things running from models db.commit() if len(offenders) > 0: offender_names = ', '.join([ '{first_name} {last_name}'.format(**rw.auth_user) for rw in offenders ]) return 'Emailed {} researchers with outdated or missing H&S info: {}'.format( len(offenders), offender_names) else: return 'All H&S up to date'