From: Diane Trout Date: Thu, 20 Aug 2009 23:40:12 +0000 (+0000) Subject: Implement a send started email feature. X-Git-Tag: 0.3.0~5 X-Git-Url: http://woldlab.caltech.edu/gitweb/?p=htsworkflow.git;a=commitdiff_plain;h=bce3c9b6e83373cd7d9627ad57c14f2056075a1c Implement a send started email feature. The UI is linked to from the admin page by overriding the django admin change_form for experments/flowcell to include an new link in the object-tools block. The actual page is implemented as a custom view. Its designed to render the emails to a preview page, and then, at the bottom of the page there's a button to actually send the emails. It'll re-load the page but with a small status message about what happened with the email in question. Additionally this requires that users be attached to affiliations and that users have email addresses. I forgot we were also going to extract an email address from the affiliation as well, if it was set. --- diff --git a/htsworkflow/frontend/experiments/experiments.py b/htsworkflow/frontend/experiments/experiments.py index 0d3f81f..cfea75f 100755 --- a/htsworkflow/frontend/experiments/experiments.py +++ b/htsworkflow/frontend/experiments/experiments.py @@ -1,9 +1,9 @@ # some core functions of the exp tracker module -from django.http import HttpResponse -from datetime import datetime -from string import * +from datetime import datetime, timedelta import os import re + +from django.http import HttpResponse from htsworkflow.frontend import settings from htsworkflow.frontend.experiments.models import FlowCell, DataRun from htsworkflow.frontend.samples.models import Library @@ -178,3 +178,55 @@ def getLaneLibs(req): else: outputfile = 'Missing input: flowcell id' return HttpResponse(outputfile, mimetype='text/plain') + +def estimateFlowcellDuration(flowcell): + """ + Attempt to estimate how long it will take to run a flowcell + + """ + # (3600 seconds * 1.5 hours per cycle ) + sequencing_seconds_per_cycle= 3600 * 1.5 + # 800 is a rough guess + pipeline_seconds_per_cycle = 800 + + cycles = flowcell.read_length + if flowcell.paired_end: + cycles *= 2 + sequencing_time = timedelta(0, cycles * sequencing_seconds_per_cycle) + analysis_time = timedelta(0, cycles * pipeline_seconds_per_cycle) + estimate_mid = sequencing_time + analysis_time + # floor estimate_mid + estimate_low = timedelta(estimate_mid.days, 0) + # floor estimate_mid and add a day + estimate_high = timedelta(estimate_mid.days+1, 0) + + return (estimate_low, estimate_high) + + +def makeUserLaneMap(flowcell): + """ + Given a flowcell return a mapping of users interested in + the libraries on those lanes. + """ + users = {} + + for lane in flowcell.lane_set.all(): + for affiliation in lane.library.affiliations.all(): + for user in affiliation.users.all(): + users.setdefault(user,[]).append(lane) + + return users + +def makeUserLibrarMap(libraries): + """ + Given an interable set of libraries return a mapping or + users interested in those libraries. + """ + users = {} + + for library in libraries: + for affiliation in library.affiliations.all(): + for user in affiliation.users.all(): + users.setdefault(user,[]).append(library) + + return users diff --git a/htsworkflow/frontend/experiments/urls.py b/htsworkflow/frontend/experiments/urls.py index c4df6a8..aaefba3 100755 --- a/htsworkflow/frontend/experiments/urls.py +++ b/htsworkflow/frontend/experiments/urls.py @@ -1,12 +1,17 @@ from django.conf.urls.defaults import * urlpatterns = patterns('', - (r'^$', 'htsworkflow.frontend.experiments.views.index'), #(r'^liblist$', 'htsworkflow.frontend.experiments.views.test_Libs'), #(r'^(?P.+)/$', 'gaworkflow.frontend.experiments.views.detail'), - (r'^(?P.+)/$', 'htsworkflow.frontend.experiments.views.makeFCSheet'), + (r'^fcsheet/(?P.+)/$', 'htsworkflow.frontend.experiments.views.makeFCSheet'), (r'^updStatus$', 'htsworkflow.frontend.experiments.experiments.updStatus'), (r'^getConfile$', 'htsworkflow.frontend.experiments.experiments.getConfile'), - (r'^getLanesNames$', 'htsworkflow.frontend.experiments.experiments.getLaneLibs') + (r'^getLanesNames$', 'htsworkflow.frontend.experiments.experiments.getLaneLibs'), + # for the following two URLS I have to pass in the primary key + # because I link to the page from an overridden version of the admin change_form + # which only makes the object primary key available in the form. + # (Or at least as far as I could tell) + (r'^started/(?P.+)/$', 'htsworkflow.frontend.experiments.views.startedEmail'), + (r'^finished/(?P.+)/$', 'htsworkflow.frontend.experiments.views.finishedEmail'), ) diff --git a/htsworkflow/frontend/experiments/views.py b/htsworkflow/frontend/experiments/views.py index a2d14bb..92f6cbd 100755 --- a/htsworkflow/frontend/experiments/views.py +++ b/htsworkflow/frontend/experiments/views.py @@ -1,10 +1,18 @@ # Create your views here. #from django.template import Context, loader #shortcut to the above modules +from django.contrib.auth.decorators import user_passes_test +from django.core.exceptions import ObjectDoesNotExist +from django.core.mail import EmailMessage, mail_managers +from django.http import HttpResponse from django.shortcuts import render_to_response, get_object_or_404 +from django.template import Context +from django.template.loader import get_template + from htsworkflow.frontend.experiments.models import * -from django.http import HttpResponse -from django.core.exceptions import ObjectDoesNotExist +from htsworkflow.frontend.experiments.experiments import \ + estimateFlowcellDuration, \ + makeUserLaneMap def index(request): all_runs = DataRun.objects.order_by('-run_start_time') @@ -32,3 +40,82 @@ def makeFCSheet(request,fcid): pass lanes = ['1','2','3','4','5','6','7','8'] return render_to_response('experiments/flowcellSheet.html',{'fc': rec}) + + +@user_passes_test(lambda u: u.is_staff) +def startedEmail(request, pk): + """ + Create the we have started processing your samples email + """ + fc = get_object_or_404(FlowCell, id=pk) + + send = request.REQUEST.get('send',False) + if send in ('1', 'on', 'True', 'true', True): + send = True + else: + send = False + + bcc_managers = request.REQUEST.get('bcc', False) + if bcc_managers in ('on', '1', 'True', 'true'): + bcc_managers = True + else: + bcc_managers = False + + user_lane = makeUserLaneMap(fc) + estimate_low, estimate_high = estimateFlowcellDuration(fc) + email_verify = get_template('experiments/email_preview.html') + email_template = get_template('experiments/started_email.html') + sender = settings.NOTIFICATION_SENDER + + warnings = [] + emails = [] + + for user in user_lane.keys(): + sending = "" + # build body + context = Context({u'flowcell': fc, + u'lanes': user_lane[user], + u'runfolder': 'blank', + u'finish_low': estimate_low, + u'finish_high': estimate_high, + u'user_admin': user.admin_url(), + }) + + # build view + subject = "Flowcell %s" % ( fc.flowcell_id ) + body = email_template.render(context) + + # provide warning + has_email = True + if user.email is None or len(user.email) == 0: + warnings.append((user.admin_url(), user.username)) + has_email = False + + if send: + if has_email: + email = EmailMessage(subject, body, sender, to=[user.email]) + if bcc_managers: + print 'bcc_managers', bcc_managers + email.bcc = settings.MANAGERS + print email.to, email.bcc + email.send() + sending = "sent" + else: + print settings.MANAGERS + mail_managers("Couldn't send to "+user.username, body) + sending = "bounced to managers" + + emails.append((user.email, subject, body, sending)) + + verify_context = Context({ + 'send': send, + 'warnings': warnings, + 'emails': emails, + 'from': sender, + }) + return HttpResponse(email_verify.render(verify_context)) + +def finishedEmail(request, pk): + """ + """ + return HttpResponse("I've got nothing.") diff --git a/htsworkflow/frontend/samples/models.py b/htsworkflow/frontend/samples/models.py index 4c5df1c..38c1863 100644 --- a/htsworkflow/frontend/samples/models.py +++ b/htsworkflow/frontend/samples/models.py @@ -269,4 +269,6 @@ class HTSUser(User): class Meta: ordering = ['username'] - + + def admin_url(self): + return '/admin/%s/%s/%d' % (self._meta.app_label, self._meta.module_name, self.id) diff --git a/htsworkflow/frontend/settings.py b/htsworkflow/frontend/settings.py index 675da57..91eccf9 100644 --- a/htsworkflow/frontend/settings.py +++ b/htsworkflow/frontend/settings.py @@ -26,17 +26,18 @@ The options understood by this module are (with their defaults): """ import ConfigParser import os +import shlex # make epydoc happy __docformat__ = "restructuredtext en" -def options_to_list(dest, section_name): +def options_to_list(options, dest, section_name, option_name): """ Load a options from section_name and store in a dictionary """ - if options.has_section(section_name): - for name in options.options(section_name): - dest.append( options.get(section_name, name) ) + if options.has_option(section_name, option_name): + opt = options.get(section_name, option_name) + dest.extend( shlex.split(opt) ) def options_to_dict(dest, section_name): """ @@ -80,9 +81,10 @@ DEBUG = True TEMPLATE_DEBUG = DEBUG ADMINS = [] -options_to_list(ADMINS, 'admins') +options_to_list(options, ADMINS, 'frontend', 'admins') -MANAGERS = ADMINS +MANAGERS = [] +options_to_list(options, MANAGERS, 'frontend', 'managers') AUTHENTICATION_BACKENDS = ( 'samples.auth_backend.HTSUserModelBackend', ) CUSTOM_USER_MODEL = 'samples.HTSUser' @@ -90,6 +92,11 @@ CUSTOM_USER_MODEL = 'samples.HTSUser' EMAIL_HOST = options.get('frontend', 'email_host') EMAIL_PORT = int(options.get('frontend', 'email_port')) +if options.has_option('frontend', 'notification_sender'): + NOTIFICATION_SENDER = options.get('frontend', 'notification_sender') +else: + NOTIFICATION_SENDER = "noreply@example.com" + # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'ado_mssql'. DATABASE_ENGINE = options.get('frontend', 'database_engine') diff --git a/htsworkflow/frontend/templates/admin/experiments/flowcell/change_form.html b/htsworkflow/frontend/templates/admin/experiments/flowcell/change_form.html new file mode 100644 index 0000000..2a44ee2 --- /dev/null +++ b/htsworkflow/frontend/templates/admin/experiments/flowcell/change_form.html @@ -0,0 +1,12 @@ +{% extends "admin/change_form.html" %} +{% load i18n %} +{% block object-tools %} +{% if change %}{% if not is_popup %} + +{% endif %}{% endif %} +{% endblock %} \ No newline at end of file diff --git a/htsworkflow/frontend/templates/experiments/email_preview.html b/htsworkflow/frontend/templates/experiments/email_preview.html new file mode 100644 index 0000000..a045c74 --- /dev/null +++ b/htsworkflow/frontend/templates/experiments/email_preview.html @@ -0,0 +1,26 @@ + + + + + +

+{% for user_admin_url, username in warnings %} +Warning: User {{ username }} has no +email address
+{% endfor %} +

+{% for to, subject, body, sending in emails %} +
+{% if sending %}Message: {{ sending }}
{% endif %} +From: {{ from }}
+To: {{ to }}
+Subject: {{ subject }}
+{{ body }} +{% endfor %}
+
+ +
+ + + + diff --git a/htsworkflow/frontend/templates/experiments/index.html b/htsworkflow/frontend/templates/experiments/index.html index 0d817d2..28c24bf 100644 --- a/htsworkflow/frontend/templates/experiments/index.html +++ b/htsworkflow/frontend/templates/experiments/index.html @@ -1,9 +1,14 @@ {% if data_run_list %} - + + {% for run in data_run_list %} + + + + + + + {% endfor %} +
{{run.run_folder}}sheetstarted emailfinished email
{% else %}

No data runs are available.

{% endif %} diff --git a/htsworkflow/frontend/templates/experiments/started_email.html b/htsworkflow/frontend/templates/experiments/started_email.html new file mode 100644 index 0000000..9434c93 --- /dev/null +++ b/htsworkflow/frontend/templates/experiments/started_email.html @@ -0,0 +1,19 @@ +

+The following libraries are on the flowcell {{ flowcell.flowcell_id }} +which is a {{ flowcell.read_length }} base pair {% if flowcell.paired_end %}paired end{% else %}single ended{% endif %} flowcell. +

+

{% for lane in lanes %} +Lane #{{ lane.lane_number }} : + +{{ lane.library.library_id }} +{{ lane.library.library_name }}
+{% endfor %}

+

+The data should be available at the following link when +the pipeline finishes, probably in about +{{ finish_low.days }} to {{ finish_high.days }} days after the flowcell is started. +

+

+ +https://jumpgate.caltech.edu/runfolders/cellcenter/ +