Coverage for test / test_vars_qaqc_processor.py: 100%
143 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-23 05:22 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-23 05:22 +0000
1from unittest.mock import patch
3from application.util.functions import parse_datetime
4from application.vars.vars_qaqc_processor import VarsQaqcProcessor
5from test.data.vars_responses import ex_23060001, ex_23060002
6from test.util.mock_response import MockResponse
9def mocked_requests_get(*args, **kwargs):
10 return MockResponse(url=kwargs.get('url'))
13class TestVarsQaqcProcessor:
14 def test_init(self):
15 qaqc_processor = VarsQaqcProcessor(
16 sequence_names=['Deep Discoverer 23060001'],
17 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
18 vars_kb_url=MockResponse.VARS_KB_URL,
19 )
20 assert qaqc_processor.sequence_names == ['Deep Discoverer 23060001']
21 assert qaqc_processor.videos == []
22 assert qaqc_processor.working_records == []
23 assert qaqc_processor.final_records == []
24 assert len(qaqc_processor.phylogeny.data.keys()) > 0
26 @patch('requests.get', side_effect=mocked_requests_get)
27 def test_find_duplicate_associations(self, _):
28 qaqc_processor_okay = VarsQaqcProcessor(
29 sequence_names=['Deep Discoverer 23060001'],
30 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
31 vars_kb_url=MockResponse.VARS_KB_URL,
32 )
33 qaqc_processor_problems = VarsQaqcProcessor(
34 sequence_names=['Deep Discoverer 23060002'],
35 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
36 vars_kb_url=MockResponse.VARS_KB_URL,
37 )
38 qaqc_processor_okay.find_duplicate_associations()
39 qaqc_processor_problems.find_duplicate_associations()
40 assert qaqc_processor_okay.working_records == []
41 assert qaqc_processor_problems.working_records == [ex_23060002['annotations'][0]]
43 @patch('requests.get', side_effect=mocked_requests_get)
44 def test_find_missing_s1(self, _):
45 qaqc_processor_okay = VarsQaqcProcessor(
46 sequence_names=['Deep Discoverer 23060001'],
47 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
48 vars_kb_url=MockResponse.VARS_KB_URL,
49 )
50 qaqc_processor_problems = VarsQaqcProcessor(
51 sequence_names=['Deep Discoverer 23060002'],
52 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
53 vars_kb_url=MockResponse.VARS_KB_URL,
54 )
55 qaqc_processor_okay.find_missing_s1()
56 qaqc_processor_problems.find_missing_s1()
57 assert qaqc_processor_okay.working_records == []
58 assert qaqc_processor_problems.working_records == [ex_23060002['annotations'][1]]
60 @patch('requests.get', side_effect=mocked_requests_get)
61 def test_find_identical_s1_s2(self, _):
62 qaqc_processor_okay = VarsQaqcProcessor(
63 sequence_names=['Deep Discoverer 23060001'],
64 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
65 vars_kb_url=MockResponse.VARS_KB_URL,
66 )
67 qaqc_processor_problems = VarsQaqcProcessor(
68 sequence_names=['Deep Discoverer 23060002'],
69 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
70 vars_kb_url=MockResponse.VARS_KB_URL,
71 )
72 qaqc_processor_okay.find_identical_s1_s2()
73 qaqc_processor_problems.find_identical_s1_s2()
74 assert qaqc_processor_okay.working_records == []
75 assert qaqc_processor_problems.working_records == [ex_23060002['annotations'][2]]
77 @patch('requests.get', side_effect=mocked_requests_get)
78 def test_find_duplicate_s2(self, _):
79 qaqc_processor_okay = VarsQaqcProcessor(
80 sequence_names=['Deep Discoverer 23060001'],
81 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
82 vars_kb_url=MockResponse.VARS_KB_URL,
83 )
84 qaqc_processor_problems = VarsQaqcProcessor(
85 sequence_names=['Deep Discoverer 23060002'],
86 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
87 vars_kb_url=MockResponse.VARS_KB_URL,
88 )
89 qaqc_processor_okay.find_duplicate_s2()
90 qaqc_processor_problems.find_duplicate_s2()
91 assert qaqc_processor_okay.working_records == []
92 assert qaqc_processor_problems.working_records == [ex_23060002['annotations'][1]]
94 @patch('requests.get', side_effect=mocked_requests_get)
95 def test_find_missing_upon_substrate(self, _):
96 qaqc_processor_okay = VarsQaqcProcessor(
97 sequence_names=['Deep Discoverer 23060001'],
98 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
99 vars_kb_url=MockResponse.VARS_KB_URL,
100 )
101 qaqc_processor_problems = VarsQaqcProcessor(
102 sequence_names=['Deep Discoverer 23060002'],
103 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
104 vars_kb_url=MockResponse.VARS_KB_URL,
105 )
106 qaqc_processor_okay.find_missing_upon_substrate()
107 qaqc_processor_problems.find_missing_upon_substrate()
108 assert qaqc_processor_okay.working_records == []
109 assert qaqc_processor_problems.working_records == [ex_23060002['annotations'][0]]
111 @patch('requests.get', side_effect=mocked_requests_get)
112 def test_find_mismatched_substrates(self, _):
113 qaqc_processor_okay = VarsQaqcProcessor(
114 sequence_names=['Deep Discoverer 23060001'],
115 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
116 vars_kb_url=MockResponse.VARS_KB_URL,
117 )
118 qaqc_processor_problems = VarsQaqcProcessor(
119 sequence_names=['Deep Discoverer 23060002'],
120 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
121 vars_kb_url=MockResponse.VARS_KB_URL,
122 )
123 qaqc_processor_okay.find_mismatched_substrates()
124 qaqc_processor_problems.find_mismatched_substrates()
125 assert qaqc_processor_okay.working_records == []
126 assert qaqc_processor_problems.working_records == [ex_23060002['annotations'][3], ex_23060002['annotations'][5]]
128 @patch('requests.get', side_effect=mocked_requests_get)
129 def test_find_missing_upon(self, _):
130 qaqc_processor_okay = VarsQaqcProcessor(
131 sequence_names=['Deep Discoverer 23060001'],
132 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
133 vars_kb_url=MockResponse.VARS_KB_URL,
134 )
135 qaqc_processor_problems = VarsQaqcProcessor(
136 sequence_names=['Deep Discoverer 23060002'],
137 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
138 vars_kb_url=MockResponse.VARS_KB_URL,
139 )
140 qaqc_processor_okay.find_missing_upon()
141 qaqc_processor_problems.find_missing_upon()
142 assert qaqc_processor_okay.working_records == []
143 assert qaqc_processor_problems.working_records == [ex_23060002['annotations'][3]]
145 @patch('requests.get', side_effect=mocked_requests_get)
146 def test_get_num_records_missing_ancillary_data(self, _):
147 qaqc_processor_okay = VarsQaqcProcessor(
148 sequence_names=['Deep Discoverer 23060001'],
149 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
150 vars_kb_url=MockResponse.VARS_KB_URL,
151 )
152 qaqc_processor_problems = VarsQaqcProcessor(
153 sequence_names=['Deep Discoverer 23060002'],
154 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
155 vars_kb_url=MockResponse.VARS_KB_URL,
156 )
157 assert qaqc_processor_okay.get_num_records_missing_ancillary_data() == 0
158 assert qaqc_processor_problems.get_num_records_missing_ancillary_data() == 2
160 @patch('requests.get', side_effect=mocked_requests_get)
161 def test_find_missing_ancillary_data(self, _):
162 qaqc_processor_okay = VarsQaqcProcessor(
163 sequence_names=['Deep Discoverer 23060001'],
164 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
165 vars_kb_url=MockResponse.VARS_KB_URL,
166 )
167 qaqc_processor_problems = VarsQaqcProcessor(
168 sequence_names=['Deep Discoverer 23060002'],
169 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
170 vars_kb_url=MockResponse.VARS_KB_URL,
171 )
172 qaqc_processor_okay.find_missing_ancillary_data()
173 qaqc_processor_problems.find_missing_ancillary_data()
174 assert qaqc_processor_okay.working_records == []
175 assert qaqc_processor_problems.working_records == [ex_23060002['annotations'][2], ex_23060002['annotations'][3]]
177 @patch('requests.get', side_effect=mocked_requests_get)
178 def test_find_id_refs_different_concept_name(self, _):
179 qaqc_processor_okay = VarsQaqcProcessor(
180 sequence_names=['Deep Discoverer 23060001'],
181 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
182 vars_kb_url=MockResponse.VARS_KB_URL,
183 )
184 qaqc_processor_problems = VarsQaqcProcessor(
185 sequence_names=['Deep Discoverer 23060002'],
186 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
187 vars_kb_url=MockResponse.VARS_KB_URL,
188 )
189 qaqc_processor_okay.find_id_refs_different_concept_name()
190 qaqc_processor_problems.find_id_refs_different_concept_name()
191 assert qaqc_processor_okay.working_records == []
192 assert qaqc_processor_problems.working_records == [ex_23060002['annotations'][2], ex_23060002['annotations'][3]]
194 @patch('requests.get', side_effect=mocked_requests_get)
195 def test_find_id_refs_conflicting_associations(self, _):
196 qaqc_processor_okay = VarsQaqcProcessor(
197 sequence_names=['Deep Discoverer 23060001'],
198 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
199 vars_kb_url=MockResponse.VARS_KB_URL,
200 )
201 qaqc_processor_problems = VarsQaqcProcessor(
202 sequence_names=['Deep Discoverer 23060002'],
203 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
204 vars_kb_url=MockResponse.VARS_KB_URL,
205 )
206 qaqc_processor_okay.find_id_refs_conflicting_associations()
207 qaqc_processor_problems.find_id_refs_conflicting_associations()
208 assert qaqc_processor_okay.working_records == []
209 assert qaqc_processor_problems.working_records == [ex_23060002['annotations'][2], ex_23060002['annotations'][3]]
211 @patch('requests.get', side_effect=mocked_requests_get)
212 def test_find_blank_associations(self, _):
213 qaqc_processor_okay = VarsQaqcProcessor(
214 sequence_names=['Deep Discoverer 23060001'],
215 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
216 vars_kb_url=MockResponse.VARS_KB_URL,
217 )
218 qaqc_processor_problems = VarsQaqcProcessor(
219 sequence_names=['Deep Discoverer 23060002'],
220 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
221 vars_kb_url=MockResponse.VARS_KB_URL,
222 )
223 qaqc_processor_okay.find_blank_associations()
224 qaqc_processor_problems.find_blank_associations()
225 assert qaqc_processor_okay.working_records == []
226 assert qaqc_processor_problems.working_records == [ex_23060002['annotations'][0], ex_23060002['annotations'][1]]
228 @patch('requests.get', side_effect=mocked_requests_get)
229 def test_find_suspicious_hosts(self, _):
230 qaqc_processor_okay = VarsQaqcProcessor(
231 sequence_names=['Deep Discoverer 23060001'],
232 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
233 vars_kb_url=MockResponse.VARS_KB_URL,
234 )
235 qaqc_processor_problems = VarsQaqcProcessor(
236 sequence_names=['Deep Discoverer 23060002'],
237 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
238 vars_kb_url=MockResponse.VARS_KB_URL,
239 )
240 qaqc_processor_okay.find_suspicious_hosts()
241 qaqc_processor_problems.find_suspicious_hosts()
242 assert qaqc_processor_okay.working_records == []
243 assert qaqc_processor_problems.working_records == [ex_23060002['annotations'][1]]
245 @patch('requests.get', side_effect=mocked_requests_get)
246 def test_find_missing_expected_association(self, _):
247 qaqc_processor_okay = VarsQaqcProcessor(
248 sequence_names=['Deep Discoverer 23060001'],
249 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
250 vars_kb_url=MockResponse.VARS_KB_URL,
251 )
252 qaqc_processor_problems = VarsQaqcProcessor(
253 sequence_names=['Deep Discoverer 23060002'],
254 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
255 vars_kb_url=MockResponse.VARS_KB_URL,
256 )
257 qaqc_processor_okay.find_missing_expected_association()
258 qaqc_processor_problems.find_missing_expected_association()
259 assert qaqc_processor_okay.final_records == []
260 assert qaqc_processor_problems.final_records == [
261 {
262 'observation_uuid': '006fb032-13b5-4517-136c-11aa9597e81e',
263 'concept': 'Hydroidolina',
264 'associations': ex_23060002['annotations'][0]['associations'],
265 'activity': 'cruise',
266 'annotator': 'Nikki Cunanan',
267 'depth': 4255.0,
268 'phylum': 'Cnidaria',
269 'class': 'Hydrozoa',
270 'order': None,
271 'family': None,
272 'genus': None,
273 'species': None,
274 'identity_reference': '50',
275 'image_url': '',
276 'video_url': 'https://hurlvideo.soest.hawaii.edu/D2/2023/EX2306_02/EX2306_02_20230825T195000Z.m4v#t=3725',
277 'recorded_timestamp': '25 Aug 23 20:52:05 UTC',
278 'video_sequence_name': 'Deep Discoverer 23060002',
279 }
280 ]
282 @patch('requests.get', side_effect=mocked_requests_get)
283 def test_find_long_host_associate_time_diff(self, _):
284 qaqc_processor_okay = VarsQaqcProcessor(
285 sequence_names=['Deep Discoverer 23060001'],
286 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
287 vars_kb_url=MockResponse.VARS_KB_URL,
288 )
289 qaqc_processor_problems = VarsQaqcProcessor(
290 sequence_names=['Deep Discoverer 23060002'],
291 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
292 vars_kb_url=MockResponse.VARS_KB_URL,
293 )
294 qaqc_processor_okay.find_long_host_associate_time_diff()
295 qaqc_processor_problems.find_long_host_associate_time_diff()
296 assert qaqc_processor_okay.final_records == []
297 assert qaqc_processor_problems.final_records == [
298 {
299 'observation_uuid': '01f3e954-b793-40a3-6166-88f24898e81e',
300 'concept': 'Pomacentridae',
301 'associations': ex_23060002['annotations'][1]['associations'],
302 'activity': 'cruise',
303 'annotator': 'Nikki Cunanan',
304 'depth': 4256.0,
305 'phylum': 'Chordata',
306 'class': 'Actinopterygii',
307 'order': 'Perciformes',
308 'family': 'Pomacentridae',
309 'genus': None,
310 'species': None,
311 'identity_reference': None,
312 'image_url': '',
313 'video_url': 'https://hurlvideo.soest.hawaii.edu/D2/2023/EX2306_02/EX2306_02_20230825T195000Z.m4v#t=4543',
314 'recorded_timestamp': '25 Aug 23 21:05:43 UTC',
315 'video_sequence_name': 'Deep Discoverer 23060002',
316 'status': 'Host not found in previous records'
317 },
318 {
319 'observation_uuid': '02dfd7f4-c834-433d-4960-9577c98ce81e',
320 'concept': 'Hydroidolina',
321 'associations': ex_23060002['annotations'][2]['associations'],
322 'activity': 'cruise',
323 'annotator': 'Nikki Cunanan',
324 'depth': None,
325 'phylum': 'Cnidaria',
326 'class': 'Hydrozoa',
327 'order': None,
328 'family': None,
329 'genus': None,
330 'species': None,
331 'identity_reference': '13',
332 'image_url': '',
333 'video_url': 'https://hurlvideo.soest.hawaii.edu/D2/2023/EX2306_02/EX2306_02_20230825T195000Z.m4v#t=2435',
334 'recorded_timestamp': '25 Aug 23 20:30:35 UTC',
335 'video_sequence_name': 'Deep Discoverer 23060002',
336 'status': 'Time between record and closest previous matching host record greater than one minute (95 seconds)'
337 },
338 {
339 'observation_uuid': '0983d9f1-d28a-482e-0160-6d3df753e91e',
340 'concept': 'AssociateConcept',
341 'associations': ex_23060002['annotations'][4]['associations'],
342 'activity': 'stationary',
343 'annotator': 'Nikki Cunanan',
344 'depth': 4260.0,
345 'phylum': None,
346 'class': None,
347 'order': None,
348 'family': None, 'genus': None,
349 'species': None,
350 'identity_reference': None,
351 'image_url': '',
352 'video_url': 'https://hurlvideo.soest.hawaii.edu/D2/2023/EX2306_02/EX2306_02_20230825T195000Z.m4v#t=2941',
353 'recorded_timestamp': '25 Aug 23 20:39:01 UTC',
354 'video_sequence_name': 'Deep Discoverer 23060002',
355 'status': 'Time between record and closest previous matching host record greater than five minutes (10 mins, 0 seconds)'
356 },
357 ]
359 @patch('requests.get', side_effect=mocked_requests_get)
360 def test_find_num_bounding_boxes(self, _):
361 qaqc_processor = VarsQaqcProcessor(
362 sequence_names=['Deep Discoverer 23060001'],
363 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
364 vars_kb_url=MockResponse.VARS_KB_URL,
365 )
366 qaqc_processor.find_num_bounding_boxes()
367 assert qaqc_processor.final_records == [{
368 'bounding_box_counts': {
369 'Pomacentridae': {'annos': 5, 'boxes': 1},
370 'none': {'annos': 1, 'boxes': 0}
371 },
372 'total_count_annos': 6,
373 'total_count_boxes': 1,
374 }]
376 @patch('requests.get', side_effect=mocked_requests_get)
377 def test_find_unique_fields(self, _):
378 qaqc_processor = VarsQaqcProcessor(
379 sequence_names=['Deep Discoverer 23060001'],
380 vars_charybdis_url=MockResponse.VARS_CHARYBDIS_URL,
381 vars_kb_url=MockResponse.VARS_KB_URL,
382 )
383 qaqc_processor.find_unique_fields()
384 assert qaqc_processor.final_records == [
385 {
386 'concept-names': {
387 'Pomacentridae': {
388 'individuals': 5,
389 'records': 5,
390 },
391 'none': {
392 'individuals': 1,
393 'records': 1,
394 }
395 },
396 },
397 {
398 'concept-upon-combinations': {
399 'Pomacentridae:bed': {
400 'individuals': 2,
401 'records': 2,
402 },
403 'Pomacentridae:sed': {
404 'individuals': 3,
405 'records': 3,
406 },
407 'none:None': {
408 'individuals': 1,
409 'records': 1,
410 }
411 },
412 },
413 {
414 'substrate-combinations': {
415 '': {
416 'individuals': 1,
417 'records': 1,
418 },
419 'bed, bou, sed': {
420 'individuals': 1,
421 'records': 1,
422 },
423 'bed, sed': {
424 'individuals': 1,
425 'records': 1,
426 },
427 'mantra, sed': {
428 'individuals': 1,
429 'records': 1,
430 },
431 'sed': {
432 'individuals': 2,
433 'records': 2,
434 },
435 },
436 },
437 {
438 'comments': {
439 None: {
440 'individuals': 3,
441 'records': 3,
442 },
443 'Added for review: Don Draper': {
444 'individuals': 1,
445 'records': 1,
446 },
447 'Added for review: Jon Snow; This is a weird lookin sponge thing!': {
448 'individuals': 1,
449 'records': 1,
450 },
451 'this is a comment': {
452 'individuals': 1,
453 'records': 1,
454 },
455 },
456 },
457 {
458 'condition-comments': {
459 None: {
460 'individuals': 6,
461 'records': 6,
462 },
463 },
464 },
465 {
466 'megahabitats': {
467 None: {
468 'individuals': 5,
469 'records': 5,
470 },
471 'continental shelf': {
472 'individuals': 1,
473 'records': 1,
474 },
475 },
476 },
477 {
478 'habitats': {
479 None: {
480 'individuals': 5,
481 'records': 5,
482 },
483 'slope': {
484 'individuals': 1,
485 'records': 1,
486 },
487 },
488 },
489 {
490 'habitat-comments': {
491 None: {
492 'individuals': 5,
493 'records': 5,
494 },
495 'loose talus': {
496 'individuals': 1,
497 'records': 1,
498 },
499 },
500 },
501 {
502 'identity-certainty': {
503 None: {
504 'individuals': 4,
505 'records': 4,
506 },
507 'maybe': {
508 'individuals': 2,
509 'records': 2,
510 },
511 },
512 },
513 {
514 'occurrence-remarks': {
515 None: {
516 'individuals': 4,
517 'records': 4,
518 },
519 'bottom in sight': {
520 'individuals': 1,
521 'records': 1,
522 },
523 'in water column on descent': {
524 'individuals': 1,
525 'records': 1,
526 },
527 },
528 },
529 ]