2 from BeautifulSoup import BeautifulSoup
6 import simplejson as json
12 from django.conf import settings
13 from django.core import mail
14 from django.core.exceptions import ObjectDoesNotExist
15 from django.test import TestCase
16 from htsworkflow.frontend.experiments import models
17 from htsworkflow.frontend.experiments import experiments
18 from htsworkflow.frontend.auth import apidata
20 from htsworkflow.pipelines.test.simulate_runfolder import TESTDATA_DIR
24 class ExperimentsTestCases(TestCase):
25 fixtures = ['test_flowcells.json']
28 self.tempdir = tempfile.mkdtemp(prefix='htsw-test-experiments-')
29 settings.RESULT_HOME_DIR = self.tempdir
31 self.fc1_id = 'FC12150'
32 self.fc1_root = os.path.join(self.tempdir, self.fc1_id)
33 os.mkdir(self.fc1_root)
34 self.fc1_dir = os.path.join(self.fc1_root, 'C1-37')
35 os.mkdir(self.fc1_dir)
36 runxml = 'run_FC12150_2007-09-27.xml'
37 shutil.copy(os.path.join(TESTDATA_DIR, runxml),
38 os.path.join(self.fc1_dir, runxml))
41 os.path.join(TESTDATA_DIR,
42 'woldlab_070829_USI-EAS44_0017_FC11055_1.srf'),
43 os.path.join(self.fc1_dir,
44 'woldlab_070829_SERIAL_FC12150_%d.srf' %(i,))
47 self.fc2_dir = os.path.join(self.tempdir, '42JTNAAXX')
48 os.mkdir(self.fc2_dir)
49 os.mkdir(os.path.join(self.fc2_dir, 'C1-25'))
50 os.mkdir(os.path.join(self.fc2_dir, 'C1-37'))
51 os.mkdir(os.path.join(self.fc2_dir, 'C1-37', 'Plots'))
54 shutil.rmtree(self.tempdir)
56 def test_flowcell_information(self):
58 Check the code that packs the django objects into simple types.
60 for fc_id in [u'FC12150', u"42JTNAAXX", "42JU1AAXX"]:
61 fc_dict = experiments.flowcell_information(fc_id)
62 fc_django = models.FlowCell.objects.get(flowcell_id=fc_id)
63 self.failUnlessEqual(fc_dict['flowcell_id'], fc_id)
64 self.failUnlessEqual(fc_django.flowcell_id, fc_id)
65 self.failUnlessEqual(fc_dict['sequencer'], fc_django.sequencer.name)
66 self.failUnlessEqual(fc_dict['read_length'], fc_django.read_length)
67 self.failUnlessEqual(fc_dict['notes'], fc_django.notes)
68 self.failUnlessEqual(fc_dict['cluster_station'], fc_django.cluster_station.name)
70 for lane in fc_django.lane_set.all():
71 lane_dict = fc_dict['lane_set'][lane.lane_number]
72 self.failUnlessEqual(lane_dict['cluster_estimate'], lane.cluster_estimate)
73 self.failUnlessEqual(lane_dict['comment'], lane.comment)
74 self.failUnlessEqual(lane_dict['flowcell'], lane.flowcell.flowcell_id)
75 self.failUnlessEqual(lane_dict['lane_number'], lane.lane_number)
76 self.failUnlessEqual(lane_dict['library_name'], lane.library.library_name)
77 self.failUnlessEqual(lane_dict['library_id'], lane.library.id)
78 self.failUnlessAlmostEqual(float(lane_dict['pM']), float(lane.pM))
79 self.failUnlessEqual(lane_dict['library_species'],
80 lane.library.library_species.scientific_name)
82 response = self.client.get('/experiments/config/%s/json' % (fc_id,), apidata)
83 # strptime isoformat string = '%Y-%m-%dT%H:%M:%S'
84 fc_json = json.loads(response.content)
85 self.failUnlessEqual(fc_json['flowcell_id'], fc_id)
86 self.failUnlessEqual(fc_json['sequencer'], fc_django.sequencer.name)
87 self.failUnlessEqual(fc_json['read_length'], fc_django.read_length)
88 self.failUnlessEqual(fc_json['notes'], fc_django.notes)
89 self.failUnlessEqual(fc_json['cluster_station'], fc_django.cluster_station.name)
92 for lane in fc_django.lane_set.all():
93 lane_dict = fc_json['lane_set'][unicode(lane.lane_number)]
94 self.failUnlessEqual(lane_dict['cluster_estimate'], lane.cluster_estimate)
95 self.failUnlessEqual(lane_dict['comment'], lane.comment)
96 self.failUnlessEqual(lane_dict['flowcell'], lane.flowcell.flowcell_id)
97 self.failUnlessEqual(lane_dict['lane_number'], lane.lane_number)
98 self.failUnlessEqual(lane_dict['library_name'], lane.library.library_name)
99 self.failUnlessEqual(lane_dict['library_id'], lane.library.id)
100 self.failUnlessAlmostEqual(float(lane_dict['pM']), float(lane.pM))
101 self.failUnlessEqual(lane_dict['library_species'],
102 lane.library.library_species.scientific_name)
104 def test_invalid_flowcell(self):
106 Make sure we get a 404 if we request an invalid flowcell ID
108 response = self.client.get('/experiments/config/nottheone/json', apidata)
109 self.failUnlessEqual(response.status_code, 404)
111 def test_no_key(self):
113 Require logging in to retrieve meta data
115 response = self.client.get(u'/experiments/config/FC12150/json')
116 self.failUnlessEqual(response.status_code, 403)
118 def test_library_id(self):
120 Library IDs should be flexible, so make sure we can retrive a non-numeric ID
122 response = self.client.get('/experiments/config/FC12150/json', apidata)
123 self.failUnlessEqual(response.status_code, 200)
124 flowcell = json.loads(response.content)
126 self.failUnlessEqual(flowcell['lane_set']['3']['library_id'], 'SL039')
128 response = self.client.get('/samples/library/SL039/json', apidata)
129 self.failUnlessEqual(response.status_code, 200)
130 library_sl039 = json.loads(response.content)
132 self.failUnlessEqual(library_sl039['library_id'], 'SL039')
134 def test_raw_id_field(self):
138 Library's have IDs, libraries also have primary keys,
139 we eventually had enough libraries that the drop down combo box was too
140 hard to filter through, unfortnately we want a field that uses our library
141 id and not the internal primary key, and raw_id_field uses primary keys.
143 This tests to make sure that the value entered in the raw library id field matches
144 the library id looked up.
146 expected_ids = [u'10981',u'11016',u'SL039',u'11060',
147 u'11061',u'11062',u'11063',u'11064']
148 self.client.login(username='supertest', password='BJOKL5kAj6aFZ6A5')
149 response = self.client.get('/admin/experiments/flowcell/153/')
150 soup = BeautifulSoup(response.content)
152 input_field = soup.find(id='id_lane_set-%d-library' % (i,))
153 library_field = input_field.findNext('strong')
154 library_id, library_name = library_field.string.split(':')
155 # strip leading '#' sign from name
156 library_id = library_id[1:]
157 self.failUnlessEqual(library_id, expected_ids[i])
158 self.failUnlessEqual(input_field['value'], library_id)
160 def test_library_to_flowcell_link(self):
162 Make sure the library page includes links to the flowcell pages.
163 That work with flowcell IDs that have parenthetical comments.
165 self.client.login(username='supertest', password='BJOKL5kAj6aFZ6A5')
166 response = self.client.get('/library/11070/')
167 soup = BeautifulSoup(response.content)
168 failed_fc_span = soup.find(text='30012AAXX (failed)')
169 failed_fc_a = failed_fc_span.findPrevious('a')
170 # make sure some of our RDF made it.
171 self.failUnlessEqual(failed_fc_a.get('rel'), 'libns:flowcell')
172 self.failUnlessEqual(failed_fc_a.get('href'), '/flowcell/30012AAXX/')
173 fc_response = self.client.get(failed_fc_a.get('href'))
174 self.failUnlessEqual(fc_response.status_code, 200)
175 fc_lane_response = self.client.get('/flowcell/30012AAXX/8/')
176 self.failUnlessEqual(fc_lane_response.status_code, 200)
180 def test_lanes_for(self):
182 Check the code that packs the django objects into simple types.
185 lanes = experiments.lanes_for(user)
186 self.failUnlessEqual(len(lanes), 5)
188 response = self.client.get('/experiments/lanes_for/%s/json' % (user,), apidata)
189 lanes_json = json.loads(response.content)
190 self.failUnlessEqual(len(lanes), len(lanes_json))
191 for i in range(len(lanes)):
192 self.failUnlessEqual(lanes[i]['comment'], lanes_json[i]['comment'])
193 self.failUnlessEqual(lanes[i]['lane_number'], lanes_json[i]['lane_number'])
194 self.failUnlessEqual(lanes[i]['flowcell'], lanes_json[i]['flowcell'])
195 self.failUnlessEqual(lanes[i]['run_date'], lanes_json[i]['run_date'])
197 def test_lanes_for_no_lanes(self):
199 Do we get something meaningful back when the user isn't attached to anything?
202 lanes = experiments.lanes_for(user)
203 self.failUnlessEqual(len(lanes), 0)
205 response = self.client.get('/experiments/lanes_for/%s/json' % (user,), apidata)
206 lanes_json = json.loads(response.content)
208 def test_lanes_for_no_user(self):
210 Do we get something meaningful back when its the wrong user
212 user = 'not a real user'
213 self.failUnlessRaises(ObjectDoesNotExist, experiments.lanes_for, user)
215 response = self.client.get('/experiments/lanes_for/%s/json' % (user,), apidata)
216 self.failUnlessEqual(response.status_code, 404)
219 def test_raw_data_dir(self):
220 """Raw data path generator check"""
221 flowcell_id = self.fc1_id
222 raw_dir = os.path.join(settings.RESULT_HOME_DIR, flowcell_id)
224 fc = models.FlowCell.objects.get(flowcell_id=flowcell_id)
225 self.failUnlessEqual(fc.get_raw_data_directory(), raw_dir)
227 fc.flowcell_id = flowcell_id + " (failed)"
228 self.failUnlessEqual(fc.get_raw_data_directory(), raw_dir)
231 def test_data_run_import(self):
232 srf_file_type = models.FileType.objects.get(name='SRF')
233 runxml_file_type = models.FileType.objects.get(name='run_xml')
234 flowcell_id = self.fc1_id
235 flowcell = models.FlowCell.objects.get(flowcell_id=flowcell_id)
236 flowcell.update_data_runs()
237 self.failUnlessEqual(len(flowcell.datarun_set.all()), 1)
239 run = flowcell.datarun_set.all()[0]
240 result_files = run.datafile_set.all()
241 result_dict = dict(((rf.relative_pathname, rf) for rf in result_files))
243 srf4 = result_dict['FC12150/C1-37/woldlab_070829_SERIAL_FC12150_4.srf']
244 self.failUnlessEqual(srf4.file_type, srf_file_type)
245 self.failUnlessEqual(srf4.library_id, '11060')
246 self.failUnlessEqual(srf4.data_run.flowcell.flowcell_id, 'FC12150')
247 self.failUnlessEqual(
248 srf4.data_run.flowcell.lane_set.get(lane_number=4).library_id,
250 self.failUnlessEqual(
252 os.path.join(settings.RESULT_HOME_DIR, srf4.relative_pathname))
254 lane_files = run.lane_files()
255 self.failUnlessEqual(lane_files[4]['srf'], srf4)
257 runxml= result_dict['FC12150/C1-37/run_FC12150_2007-09-27.xml']
258 self.failUnlessEqual(runxml.file_type, runxml_file_type)
259 self.failUnlessEqual(runxml.library_id, None)
262 def test_read_result_file(self):
263 """make sure we can return a result file
265 flowcell_id = self.fc1_id
266 flowcell = models.FlowCell.objects.get(flowcell_id=flowcell_id)
267 flowcell.update_data_runs()
269 #self.client.login(username='supertest', password='BJOKL5kAj6aFZ6A5')
271 result_files = flowcell.datarun_set.all()[0].datafile_set.all()
272 for f in result_files:
273 url = '/experiments/file/%s' % ( f.random_key,)
274 response = self.client.get(url)
275 self.failUnlessEqual(response.status_code, 200)
276 mimetype = f.file_type.mimetype
278 mimetype = 'application/octet-stream'
280 self.failUnlessEqual(mimetype, response['content-type'])
282 class TestFileType(TestCase):
283 def test_file_type_unicode(self):
284 file_type_objects = models.FileType.objects
285 name = 'QSEQ tarfile'
286 file_type_object = file_type_objects.get(name=name)
287 self.failUnlessEqual(u"<FileType: QSEQ tarfile>",
288 unicode(file_type_object))
290 class TestFileType(TestCase):
291 def test_find_file_type(self):
292 file_type_objects = models.FileType.objects
293 cases = [('woldlab_090921_HWUSI-EAS627_0009_42FC3AAXX_l7_r1.tar.bz2',
294 'QSEQ tarfile', 7, 1),
295 ('woldlab_091005_HWUSI-EAS627_0010_42JT2AAXX_1.srf',
297 ('s_1_eland_extended.txt.bz2','ELAND Extended', 1, None),
298 ('s_7_eland_multi.txt.bz2', 'ELAND Multi', 7, None),
299 ('s_3_eland_result.txt.bz2','ELAND Result', 3, None),
300 ('s_1_export.txt.bz2','ELAND Export', 1, None),
301 ('s_1_percent_call.png', 'IVC Percent Call', 1, None),
302 ('s_2_percent_base.png', 'IVC Percent Base', 2, None),
303 ('s_3_percent_all.png', 'IVC Percent All', 3, None),
304 ('s_4_call.png', 'IVC Call', 4, None),
305 ('s_5_all.png', 'IVC All', 5, None),
306 ('Summary.htm', 'Summary.htm', None, None),
307 ('run_42JT2AAXX_2009-10-07.xml', 'run_xml', None, None),
309 for filename, typename, lane, end in cases:
310 ft = models.find_file_type_metadata_from_filename(filename)
311 self.failUnlessEqual(ft['file_type'],
312 file_type_objects.get(name=typename))
313 self.failUnlessEqual(ft.get('lane', None), lane)
314 self.failUnlessEqual(ft.get('end', None), end)
316 def test_assign_file_type_complex_path(self):
317 file_type_objects = models.FileType.objects
318 cases = [('/a/b/c/woldlab_090921_HWUSI-EAS627_0009_42FC3AAXX_l7_r1.tar.bz2',
319 'QSEQ tarfile', 7, 1),
320 ('foo/woldlab_091005_HWUSI-EAS627_0010_42JT2AAXX_1.srf',
322 ('../s_1_eland_extended.txt.bz2','ELAND Extended', 1, None),
323 ('/bleem/s_7_eland_multi.txt.bz2', 'ELAND Multi', 7, None),
324 ('/qwer/s_3_eland_result.txt.bz2','ELAND Result', 3, None),
325 ('/ty///1/s_1_export.txt.bz2','ELAND Export', 1, None),
326 ('/help/s_1_percent_call.png', 'IVC Percent Call', 1, None),
327 ('/bored/s_2_percent_base.png', 'IVC Percent Base', 2, None),
328 ('/example1/s_3_percent_all.png', 'IVC Percent All', 3, None),
329 ('amonkey/s_4_call.png', 'IVC Call', 4, None),
330 ('fishie/s_5_all.png', 'IVC All', 5, None),
331 ('/random/Summary.htm', 'Summary.htm', None, None),
332 ('/notrandom/run_42JT2AAXX_2009-10-07.xml', 'run_xml', None, None),
334 for filename, typename, lane, end in cases:
335 result = models.find_file_type_metadata_from_filename(filename)
336 self.failUnlessEqual(result['file_type'],
337 file_type_objects.get(name=typename))
338 self.failUnlessEqual(result.get('lane',None), lane)
339 self.failUnlessEqual(result.get('end', None), end)
341 class TestEmailNotify(TestCase):
342 fixtures = ['test_flowcells.json']
344 def test_started_email_not_logged_in(self):
345 response = self.client.get('/experiments/started/153/')
346 self.failUnlessEqual(response.status_code, 302)
348 def test_started_email_logged_in_user(self):
349 self.client.login(username='test', password='BJOKL5kAj6aFZ6A5')
350 response = self.client.get('/experiments/started/153/')
351 self.failUnlessEqual(response.status_code, 302)
353 def test_started_email_logged_in_staff(self):
354 self.client.login(username='admintest', password='BJOKL5kAj6aFZ6A5')
355 response = self.client.get('/experiments/started/153/')
356 self.failUnlessEqual(response.status_code, 200)
358 def test_started_email_send(self):
359 self.client.login(username='admintest', password='BJOKL5kAj6aFZ6A5')
360 response = self.client.get('/experiments/started/153/')
361 self.failUnlessEqual(response.status_code, 200)
363 self.failUnless('pk1@example.com' in response.content)
364 self.failUnless('Lane #8 : (11064) Paired ends 104' in response.content)
366 response = self.client.get('/experiments/started/153/', {'send':'1','bcc':'on'})
367 self.failUnlessEqual(response.status_code, 200)
368 self.failUnlessEqual(len(mail.outbox), 4)
369 for m in mail.outbox:
370 self.failUnless(len(m.body) > 0)
372 def test_email_navigation(self):
374 Can we navigate between the flowcell and email forms properly?
376 self.client.login(username='supertest', password='BJOKL5kAj6aFZ6A5')
377 response = self.client.get('/experiments/started/153/')
378 self.failUnlessEqual(response.status_code, 200)
379 self.failUnless(re.search('Flowcell FC12150', response.content))
380 # require that navigation back to the admin page exists
381 self.failUnless(re.search('<a href="/admin/experiments/flowcell/153/">[^<]+</a>', response.content))