Adds a json api 'lanes_for' feature
[htsworkflow.git] / htsworkflow / frontend / experiments / tests.py
1 import re
2 from BeautifulSoup import BeautifulSoup
3 try:
4     import json
5 except ImportError, e:
6     import simplejson as json
7 import sys
8
9 from django.core import mail
10 from django.core.exceptions import ObjectDoesNotExist
11 from django.test import TestCase
12 from htsworkflow.frontend.experiments import models
13 from htsworkflow.frontend.experiments import experiments
14 from htsworkflow.frontend.auth import apidata
15
16 LANE_SET = range(1,9)
17
18 class ExperimentsTestCases(TestCase):
19     fixtures = ['test_flowcells.json']
20
21     def setUp(self):
22         pass
23
24     def test_flowcell_information(self):
25         """
26         Check the code that packs the django objects into simple types.
27         """
28         for fc_id in [u'303TUAAXX', u"42JTNAAXX", "42JU1AAXX"]:
29             fc_dict = experiments.flowcell_information(fc_id)
30             fc_django = models.FlowCell.objects.get(flowcell_id=fc_id)
31             self.failUnlessEqual(fc_dict['flowcell_id'], fc_id)
32             self.failUnlessEqual(fc_django.flowcell_id, fc_id)
33             self.failUnlessEqual(fc_dict['sequencer'], fc_django.sequencer.name)
34             self.failUnlessEqual(fc_dict['read_length'], fc_django.read_length)
35             self.failUnlessEqual(fc_dict['notes'], fc_django.notes)
36             self.failUnlessEqual(fc_dict['cluster_station'], fc_django.cluster_station.name)
37
38             for lane in fc_django.lane_set.all():
39                 lane_dict = fc_dict['lane_set'][lane.lane_number]
40                 self.failUnlessEqual(lane_dict['cluster_estimate'], lane.cluster_estimate)
41                 self.failUnlessEqual(lane_dict['comment'], lane.comment)
42                 self.failUnlessEqual(lane_dict['flowcell'], lane.flowcell.flowcell_id)
43                 self.failUnlessEqual(lane_dict['lane_number'], lane.lane_number)
44                 self.failUnlessEqual(lane_dict['library_name'], lane.library.library_name)
45                 self.failUnlessEqual(lane_dict['library_id'], lane.library.id)
46                 self.failUnlessAlmostEqual(float(lane_dict['pM']), float(lane.pM))
47                 self.failUnlessEqual(lane_dict['library_species'],
48                                      lane.library.library_species.scientific_name)
49                     
50             response = self.client.get('/experiments/config/%s/json' % (fc_id,), apidata)
51             # strptime isoformat string = '%Y-%m-%dT%H:%M:%S'
52             fc_json = json.loads(response.content)
53             self.failUnlessEqual(fc_json['flowcell_id'], fc_id)
54             self.failUnlessEqual(fc_json['sequencer'], fc_django.sequencer.name)
55             self.failUnlessEqual(fc_json['read_length'], fc_django.read_length)
56             self.failUnlessEqual(fc_json['notes'], fc_django.notes)
57             self.failUnlessEqual(fc_json['cluster_station'], fc_django.cluster_station.name)
58
59
60             for lane in fc_django.lane_set.all():
61                 lane_dict = fc_json['lane_set'][unicode(lane.lane_number)]
62                 self.failUnlessEqual(lane_dict['cluster_estimate'], lane.cluster_estimate)
63                 self.failUnlessEqual(lane_dict['comment'], lane.comment)
64                 self.failUnlessEqual(lane_dict['flowcell'], lane.flowcell.flowcell_id)
65                 self.failUnlessEqual(lane_dict['lane_number'], lane.lane_number)
66                 self.failUnlessEqual(lane_dict['library_name'], lane.library.library_name)
67                 self.failUnlessEqual(lane_dict['library_id'], lane.library.id)
68                 self.failUnlessAlmostEqual(float(lane_dict['pM']), float(lane.pM))
69                 self.failUnlessEqual(lane_dict['library_species'],
70                                      lane.library.library_species.scientific_name)
71
72     def test_invalid_flowcell(self):
73         """
74         Make sure we get a 404 if we request an invalid flowcell ID
75         """
76         response = self.client.get('/experiments/config/nottheone/json', apidata)
77         self.failUnlessEqual(response.status_code, 404)
78
79     def test_no_key(self):
80         """
81         Require logging in to retrieve meta data
82         """
83         response = self.client.get(u'/experiments/config/303TUAAXX/json')
84         self.failUnlessEqual(response.status_code, 403)
85
86     def test_library_id(self):
87         """
88         Library IDs should be flexible, so make sure we can retrive a non-numeric ID
89         """
90         response = self.client.get('/experiments/config/303TUAAXX/json', apidata)
91         self.failUnlessEqual(response.status_code, 200)
92         flowcell = json.loads(response.content)
93
94         self.failUnlessEqual(flowcell['lane_set']['3']['library_id'], 'SL039')
95
96         response = self.client.get('/samples/library/SL039/json', apidata)
97         self.failUnlessEqual(response.status_code, 200)
98         library_sl039 = json.loads(response.content)
99
100         self.failUnlessEqual(library_sl039['library_id'], 'SL039')
101
102     def test_raw_id_field(self):
103         """
104         Test ticket:147
105
106         Library's have IDs, libraries also have primary keys,
107         we eventually had enough libraries that the drop down combo box was too
108         hard to filter through, unfortnately we want a field that uses our library
109         id and not the internal primary key, and raw_id_field uses primary keys.
110
111         This tests to make sure that the value entered in the raw library id field matches
112         the library id looked up.
113         """
114         expected_ids = [u'10981',u'11016',u'SL039',u'11060',
115                         u'11061',u'11062',u'11063',u'11064']
116         self.client.login(username='supertest', password='BJOKL5kAj6aFZ6A5')
117         response = self.client.get('/admin/experiments/flowcell/153/')
118         soup = BeautifulSoup(response.content)
119         for i in range(0,8):
120             input_field = soup.find(id='id_lane_set-%d-library' % (i,))
121             library_field = input_field.findNext('strong')
122             library_id, library_name = library_field.string.split(':')
123             # strip leading '#' sign from name
124             library_id = library_id[1:]
125             self.failUnlessEqual(library_id, expected_ids[i])
126             self.failUnlessEqual(input_field['value'], library_id)
127
128     def test_lanes_for(self):
129         """
130         Check the code that packs the django objects into simple types.
131         """
132         user = 'test'
133         lanes = experiments.lanes_for(user)
134         self.failUnlessEqual(len(lanes), 5)
135
136         response = self.client.get('/experiments/lanes_for/%s/json' % (user,), apidata)
137         lanes_json = json.loads(response.content)
138         self.failUnlessEqual(len(lanes), len(lanes_json))
139         for i in range(len(lanes)):
140             self.failUnlessEqual(lanes[i]['comment'], lanes_json[i]['comment'])
141             self.failUnlessEqual(lanes[i]['lane_number'], lanes_json[i]['lane_number'])
142             self.failUnlessEqual(lanes[i]['flowcell'], lanes_json[i]['flowcell'])
143             self.failUnlessEqual(lanes[i]['run_date'], lanes_json[i]['run_date'])
144             
145     def test_lanes_for_no_lanes(self):
146         """
147         Do we get something meaningful back when the user isn't attached to anything?
148         """
149         user = 'supertest'
150         lanes = experiments.lanes_for(user)
151         self.failUnlessEqual(len(lanes), 0)
152
153         response = self.client.get('/experiments/lanes_for/%s/json' % (user,), apidata)
154         lanes_json = json.loads(response.content)
155
156     def test_lanes_for_no_user(self):
157         """
158         Do we get something meaningful back when its the wrong user
159         """
160         user = 'not a real user'
161         self.failUnlessRaises(ObjectDoesNotExist, experiments.lanes_for, user)
162
163         response = self.client.get('/experiments/lanes_for/%s/json' % (user,), apidata)
164         self.failUnlessEqual(response.status_code, 404)
165
166
167 class TestEmailNotify(TestCase):
168     fixtures = ['test_flowcells.json']
169
170     def test_started_email_not_logged_in(self):
171         response = self.client.get('/experiments/started/153/')
172         self.failUnlessEqual(response.status_code, 302)
173
174     def test_started_email_logged_in_user(self):
175         self.client.login(username='test', password='BJOKL5kAj6aFZ6A5')
176         response = self.client.get('/experiments/started/153/')
177         self.failUnlessEqual(response.status_code, 302)
178         
179     def test_started_email_logged_in_staff(self):
180         self.client.login(username='admintest', password='BJOKL5kAj6aFZ6A5') 
181         response = self.client.get('/experiments/started/153/')
182         self.failUnlessEqual(response.status_code, 200)
183
184     def test_started_email_send(self):
185         self.client.login(username='admintest', password='BJOKL5kAj6aFZ6A5') 
186         response = self.client.get('/experiments/started/153/')
187         self.failUnlessEqual(response.status_code, 200)
188         
189         self.failUnless('pk1@example.com' in response.content)
190         self.failUnless('Lane #8 : (11064) Paired ends 104' in response.content)
191
192         response = self.client.get('/experiments/started/153/', {'send':'1','bcc':'on'})
193         self.failUnlessEqual(response.status_code, 200)
194         self.failUnlessEqual(len(mail.outbox), 4)
195         for m in mail.outbox:
196             self.failUnless(len(m.body) > 0)
197
198     def test_email_navigation(self):
199         """
200         Can we navigate between the flowcell and email forms properly?
201         """
202         self.client.login(username='supertest', password='BJOKL5kAj6aFZ6A5') 
203         response = self.client.get('/experiments/started/153/')
204         self.failUnlessEqual(response.status_code, 200)
205         self.failUnless(re.search('Flowcell 303TUAAXX', response.content))
206         # require that navigation back to the admin page exists
207         self.failUnless(re.search('<a href="/admin/experiments/flowcell/153/">[^<]+</a>', response.content))
208