# 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
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
from django.conf.urls.defaults import *
urlpatterns = patterns('',
-
(r'^$', 'htsworkflow.frontend.experiments.views.index'),
#(r'^liblist$', 'htsworkflow.frontend.experiments.views.test_Libs'),
#(r'^(?P<run_folder>.+)/$', 'gaworkflow.frontend.experiments.views.detail'),
- (r'^(?P<fcid>.+)/$', 'htsworkflow.frontend.experiments.views.makeFCSheet'),
+ (r'^fcsheet/(?P<fcid>.+)/$', '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<pk>.+)/$', 'htsworkflow.frontend.experiments.views.startedEmail'),
+ (r'^finished/(?P<pk>.+)/$', 'htsworkflow.frontend.experiments.views.finishedEmail'),
)
# 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')
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.")
class Meta:
ordering = ['username']
-
+
+ def admin_url(self):
+ return '/admin/%s/%s/%d' % (self._meta.app_label, self._meta.module_name, self.id)
"""
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):
"""
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'
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')
--- /dev/null
+{% extends "admin/change_form.html" %}
+{% load i18n %}
+{% block object-tools %}
+{% if change %}{% if not is_popup %}
+ <ul class="object-tools">
+ <li><a href="../../../../{{ app_label }}/started/{{ object_id }}/">{% trans "Started Email" %}</a></li>
+ <li><a href="history/" class="historylink">{% trans "History" %}</a></li>
+ {% if has_absolute_url %}<li><a href="../../../r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">{% trans
+ "View on site" %}</a></li>{% endif%}
+ </ul>
+{% endif %}{% endif %}
+{% endblock %}
\ No newline at end of file
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html> <head>
+<title></title>
+</head>
+<body>
+<p>
+{% for user_admin_url, username in warnings %}
+Warning: User <a href="{{ user_admin_url}}">{{ username }}</a> has no
+email address <br/>
+{% endfor %}
+</p>
+{% for to, subject, body, sending in emails %}
+<hr/>
+{% if sending %}<b>Message:</b> {{ sending }}<br/>{% endif %}
+<b>From:</b> {{ from }}<br/>
+<b>To:</b> {{ to }}<br/>
+<b>Subject:</b> {{ subject }}<br/>
+{{ body }}
+{% endfor %}<hr/>
+<form method="get">
+<label for="bcc">BCC Managers?</label>
+<input type="checkbox" id="bcc" name="bcc" checked="on"/><br/>
+<input type="hidden" name="send" value="1"/>
+<input type="submit" value="Send Email"/>
+</body>
+</html>
{% if data_run_list %}
- <ul>
- {% for run in data_run_list %}
- <li><a href="{{ run.fcid }}">{{ run.run_folder }}</a></li>
- {% endfor %}
- </ul>
+ <table>
+ {% for run in data_run_list %}
+ <tr>
+ <td>{{run.run_folder}}</td>
+ <td><a href="fcsheet/{{run.fcid}}">sheet</td>
+ <td><a href="started/{{run.fcid}}">started email</td>
+ <td><a href="finished/{{run.fcid}}">finished email</td>
+ </tr>
+ {% endfor %}
+ </table>
{% else %}
<p>No data runs are available.</p>
{% endif %}
--- /dev/null
+<p>
+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.
+</p>
+<p>{% for lane in lanes %}
+Lane #{{ lane.lane_number }} :
+<a href="https://jumpgate.caltech.edu/library/{{lane.library.library_id}}">
+{{ lane.library.library_id }}</a>
+{{ lane.library.library_name }}<br/>
+{% endfor %}</p>
+<p>
+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.
+</p>
+<p>
+<a href="https://jumpgate.caltech.edu/runfolders/cellcenter/">
+https://jumpgate.caltech.edu/runfolders/cellcenter/
+</a></p>