Coverage for denofo/questionnaire/questions.py: 86%
401 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-09 15:27 +0200
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-09 15:27 +0200
1import warnings
2from typing import Any
3from denofo.utils.constants import FUNCS_TO_MODELS_DICT, GoQBack
4from denofo.utils.helpers import different_answers, get_model_from_qstack_dict
5from denofo.models import (
6 AnnotGenome,
7 DeNovoGeneAnnotation,
8 EvolutionaryInformation,
9 HomologyFilter,
10 TranslationalEvidence,
11 NonCodingHomologs,
12 PhylogeneticTaxa,
13 SyntenySearch,
14 TaxonID,
15 Transcriptome,
16)
17from denofo.choices import (
18 AnchorChoices,
19 AnnotGenomeChoices,
20 GeneticContextChoices,
21 HomologyDBChoices,
22 InputDataChoices,
23 ORFChoices,
24 SeqTypeChoices,
25 TaxSpecificityChoices,
26 ThresholdChoices,
27 TranslationEvidenceChoices,
28)
31class DeNovoQuestionnaire:
32 """
33 A class to handle the questionnaire for the de novo gene annotation.
34 Order of questions is defined here as well as the logic to call the next question
35 or go back to the previous question.
37 :param user_interface_funcs_dict: The user interface functions dictionary.
38 :type user_interface_funcs_dict: dict
40 :cvar question_stack: The question stack.
41 :vartype question_stack: list[tuple[callable, Any]]
42 :cvar current_idx: The current index in the question stack.
43 :vartype current_idx: int
44 :cvar interface_funcs_dict: The user interface functions dictionary.
45 :vartype interface_funcs_dict: dict
47 :func call_last_question: Call the previous question in the question stack.
48 :func call_next_question: Call the next question in the question stack.
49 :func q_end: End of the questionnaire.
50 :func start_questionnaire: Start the questionnaire.
51 """
53 def __init__(self, user_interface_funcs_dict: dict):
54 self.question_stack: list[tuple[callable, Any]] = []
55 self.current_idx: int = 0
56 self.interface_funcs_dict = user_interface_funcs_dict
58 self.deNovoGeneAnnotation = self.start_questionnaire()
60 def call_last_question(self):
61 """
62 Call the previous question in the question stack.
63 """
64 answer = None
66 if self.current_idx > 0:
67 self.current_idx -= 1
68 prev_answer = self.question_stack[self.current_idx][1]
69 self.question_stack[self.current_idx][0](prev_answer)
70 return
72 if self.question_stack:
73 answer = self.question_stack[self.current_idx][1]
74 self.question_stack[self.current_idx][0](answer)
75 return
77 self.q1(answer)
78 return
80 def call_next_question(
81 self,
82 current_answer: Any,
83 this_q: callable,
84 next_q: callable,
85 prev_answer: list = None,
86 ):
87 """
88 Call the next question in the question stack.
90 :param current_answer: The current answer.
91 :type current_answer: Any
92 :param this_q: The current question.
93 :type this_q: callable
94 :param next_q: The next question.
95 :type next_q: callable
96 :param prev_answer: The previous answer.
97 :type prev_answer: list, optional
98 """
99 next_answer = None
101 if different_answers(current_answer, prev_answer):
102 self.question_stack = self.question_stack[: self.current_idx]
103 self.question_stack.append((this_q, current_answer))
104 else:
105 if len(self.question_stack) > self.current_idx + 1:
106 next_answer = self.question_stack[self.current_idx + 1][1]
107 next_q = self.question_stack[self.current_idx + 1][0]
109 self.current_idx += 1
110 next_q(next_answer)
112 def q_end(self, answer: Any = None):
113 """
114 End of the questionnaire.
115 Calling this last function (without an actual question) is necessary to trigger
116 saving the last answer in the question stack through the call_next_question function.
117 """
118 return # end of questionnaire
120 def q6_1(self, answer: Any = None):
121 """
122 Get the URL/doi to the study/detailed methods.
123 """
124 studyURL = self.interface_funcs_dict["get_custom_entry"](
125 "Please provide the URL/doi to your study/detailed methods:",
126 multi_choice=True,
127 section_idx=5,
128 prev_answer=answer,
129 )
131 if isinstance(studyURL, GoQBack):
132 self.call_last_question()
133 return
135 self.call_next_question(studyURL, self.q6_1, self.q_end, answer)
137 def q6(self, answer: Any = None):
138 """
139 Check if URL/doi to the study/detailed methods should be provided.
140 """
141 answerStudyURL = self.interface_funcs_dict["get_yes_no"](
142 "Do you want to provide a URL/doi to your study/detailed methods? (yes/no)",
143 section_idx=5,
144 prev_answer=answer,
145 )
147 if isinstance(answerStudyURL, GoQBack):
148 self.call_last_question()
149 return
150 elif answerStudyURL:
151 next_question_callable = self.q6_1
152 else:
153 next_question_callable = self.q_end
155 self.call_next_question(answerStudyURL, self.q6, next_question_callable, answer)
157 def q5_2(self, answer: Any = None):
158 """
159 Get the custom method used as evidence for translation.
160 """
161 cstmTranslatEvidnc = self.interface_funcs_dict["get_custom_entry"](
162 "Please provide your custom method used as evidence for translation:",
163 multi_choice=True,
164 section_idx=4,
165 prev_answer=answer,
166 )
168 if isinstance(cstmTranslatEvidnc, GoQBack):
169 self.call_last_question()
170 return
172 self.call_next_question(cstmTranslatEvidnc, self.q5_2, self.q6, answer)
174 def q5_1(self, answer: Any = None):
175 """
176 Get the custom method used as evidence for translation.
177 """
178 translatEvidence = self.interface_funcs_dict["get_enum_choice_conversion"](
179 TranslationEvidenceChoices,
180 "Please choose the method used as evidence for translation:",
181 multi_choice=True,
182 section_idx=4,
183 prev_answer=answer,
184 )
186 if isinstance(translatEvidence, GoQBack):
187 self.call_last_question()
188 return
189 elif translatEvidence and TranslationEvidenceChoices.CUSTOM in translatEvidence:
190 next_question_callable = self.q5_2
191 else:
192 next_question_callable = self.q6
194 self.call_next_question(
195 translatEvidence, self.q5_1, next_question_callable, answer
196 )
198 def q5(self, answer: Any = None):
199 """
200 Check if translation of the de novo genes was verified.
201 """
202 translationEvidence = self.interface_funcs_dict["get_yes_no"](
203 "Did you verify the translation of the de novo genes? (yes/no)",
204 section_idx=4,
205 prev_answer=answer,
206 )
208 if isinstance(translationEvidence, GoQBack):
209 self.call_last_question()
210 return
211 elif translationEvidence:
212 next_question_callable = self.q5_1
213 else:
214 next_question_callable = self.q6
216 self.call_next_question(
217 translationEvidence, self.q5, next_question_callable, answer
218 )
220 def q4_1(self, answer: Any = None):
221 """
222 Get the custom metric or method used to identify selection pressure.
223 """
224 selection = self.interface_funcs_dict["get_custom_entry"](
225 "Please provide the metric or method used to identify selection pressure:",
226 section_idx=3,
227 prev_answer=answer,
228 )
230 if isinstance(selection, GoQBack):
231 self.call_last_question()
232 return
234 self.call_next_question(selection, self.q4_1, self.q5, answer)
236 def q4(self, answer: Any = None):
237 """
238 Check if selection pressure was studied for the de novo genes.
239 """
240 selectionPressure = self.interface_funcs_dict["get_yes_no"](
241 "Did you study selection pressure of the de novo genes? (yes/no)",
242 section_idx=3,
243 prev_answer=answer,
244 )
246 if isinstance(selectionPressure, GoQBack):
247 self.call_last_question()
248 return
249 elif selectionPressure:
250 next_question_callable = self.q4_1
251 else:
252 next_question_callable = self.q5
254 self.call_next_question(
255 selectionPressure, self.q4, next_question_callable, answer
256 )
258 def q3_3_3(self, answer: Any = None):
259 """
260 Get the custom software used for the synteny search.
261 """
262 softwareSyntenySearch = self.interface_funcs_dict["get_custom_entry"](
263 "Please choose the software used for the synteny search:",
264 multi_choice=True,
265 section_idx=2,
266 prev_answer=answer,
267 )
269 if isinstance(softwareSyntenySearch, GoQBack):
270 self.call_last_question()
271 return
273 self.call_next_question(softwareSyntenySearch, self.q3_3_3, self.q4, answer)
275 def q3_3_2(self, answer: Any = None):
276 """
277 Check if specific software was used for the synteny search.
278 """
279 sftwrSyntSearch = self.interface_funcs_dict["get_yes_no"](
280 "Did you use a specific software for the synteny search? (yes/no)",
281 section_idx=2,
282 prev_answer=answer,
283 )
285 if isinstance(sftwrSyntSearch, GoQBack):
286 self.call_last_question()
287 return
288 elif sftwrSyntSearch:
289 next_question_callable = self.q3_3_3
290 else:
291 next_question_callable = self.q4
293 self.call_next_question(
294 sftwrSyntSearch, self.q3_3_2, next_question_callable, answer
295 )
297 def q3_3_1(self, answer: Any = None):
298 """
299 Get the custom anchor for synteny search.
300 """
301 customAnchor = self.interface_funcs_dict["get_custom_entry"](
302 "Please provide your custom anchor for synteny search:",
303 multi_choice=True,
304 section_idx=2,
305 prev_answer=answer,
306 )
308 if isinstance(customAnchor, GoQBack):
309 self.call_last_question()
310 return
312 self.call_next_question(customAnchor, self.q3_3_1, self.q3_3_2, answer)
314 def q3_3(self, answer: Any = None):
315 """
316 Get the synteny search information from the user.
317 """
318 geneAnchor = self.interface_funcs_dict["get_enum_choice_conversion"](
319 AnchorChoices,
320 "What was used to identify the syntenic region?:",
321 multi_choice=True,
322 section_idx=2,
323 prev_answer=answer,
324 )
326 if isinstance(geneAnchor, GoQBack):
327 self.call_last_question()
328 return
329 elif geneAnchor and AnchorChoices.CUSTOM in geneAnchor:
330 next_question_callable = self.q3_3_1
331 else:
332 next_question_callable = self.q3_3_2
334 self.call_next_question(geneAnchor, self.q3_3, next_question_callable, answer)
336 def q3_2(self, answer: Any = None):
337 """
338 Check if synteny was studied between de novo genes and homologous sequences.
339 """
340 answerSynteny = self.interface_funcs_dict[
341 "get_yes_no"
342 ](
343 "Did you check for synteny between de novo genes and their homologous sequences? (yes/no)", # TODO: +non-genic?
344 section_idx=2,
345 prev_answer=answer,
346 )
348 if isinstance(answerSynteny, GoQBack):
349 self.call_last_question()
350 return
351 elif answerSynteny:
352 next_question_callable = self.q3_3
353 else:
354 next_question_callable = self.q4
356 self.call_next_question(
357 answerSynteny, self.q3_2, next_question_callable, answer
358 )
360 def q3_1(self, answer: Any = None):
361 """
362 Check if conservation/mutations between de novo genes and homologous sequences were studied.
363 """
364 enablingMutations = self.interface_funcs_dict[
365 "get_yes_no"
366 ](
367 "Did you study conservation/mutations between de novo genes and homologous sequences?", # TODO: +non-genic?
368 section_idx=2,
369 prev_answer=answer,
370 )
372 if isinstance(enablingMutations, GoQBack):
373 self.call_last_question()
374 return
376 self.call_next_question(enablingMutations, self.q3_1, self.q3_2, answer)
378 def q3(self, answer: Any = None):
379 """
380 Check if non-genic homologous sequences were detected.
381 """
382 nonCodeHomolog = self.interface_funcs_dict["get_yes_no"](
383 "Did you detect non-genic homologous sequences in genomes from other taxonomic groups? (yes/no)",
384 section_idx=2,
385 prev_answer=answer,
386 )
388 if isinstance(nonCodeHomolog, GoQBack):
389 self.call_last_question()
390 return
391 elif nonCodeHomolog:
392 next_question_callable = self.q3_1
393 else:
394 next_question_callable = self.q4
396 self.call_next_question(nonCodeHomolog, self.q3, next_question_callable, answer)
398 def q2_8(self, answer: Any = None):
399 """
400 Get the custom database(s) used for homology filtering.
401 """
402 customDB = self.interface_funcs_dict["get_custom_entry"](
403 "Please provide your custom database used for homology filtering:",
404 multi_choice=True,
405 section_idx=1,
406 prev_answer=answer,
407 )
409 if isinstance(customDB, GoQBack):
410 self.call_last_question()
411 return
413 self.call_next_question(customDB, self.q2_8, self.q3, answer)
415 def q2_7(self, answer: Any = None):
416 """
417 Get the custom database(s) used for homology filtering.
418 """
419 homologyDBChoice = self.interface_funcs_dict["get_enum_choice_conversion"](
420 HomologyDBChoices,
421 "Please choose the database(s) used for homology filtering:",
422 multi_choice=True,
423 section_idx=1,
424 prev_answer=answer,
425 )
427 if isinstance(homologyDBChoice, GoQBack):
428 self.call_last_question()
429 return
430 elif homologyDBChoice and HomologyDBChoices.CUSTOM in homologyDBChoice:
431 next_question_callable = self.q2_8
432 else:
433 next_question_callable = self.q3
435 self.call_next_question(
436 homologyDBChoice, self.q2_7, next_question_callable, answer
437 )
439 def q2_6_1(self, answer: Any = None):
440 """
441 Get the threshold value(s) for homology filtering based on selected metric(s).
442 """
443 thresholdValid = False
444 thresholdValue = []
445 thresholdChoice = dict(self.question_stack)[self.q2_5_3]
446 customThreshold = dict(self.question_stack).get(self.q2_6, [])
448 for idx, thChoice in enumerate(
449 [it for it in thresholdChoice if it != ThresholdChoices.CUSTOM]
450 + customThreshold
451 ):
452 while not thresholdValid:
453 if answer:
454 prev_answer = answer[idx] if len(answer) > idx else None
455 else:
456 prev_answer = None
457 answrThreshVal = self.interface_funcs_dict["get_custom_entry"](
458 f"Please provide the threshold value for your homology "
459 f"filtering based on {thChoice}:",
460 section_idx=1,
461 prev_answer=prev_answer,
462 )
464 if isinstance(answrThreshVal, GoQBack):
465 self.call_last_question()
466 return
468 if prev_answer and not different_answers(answrThreshVal, prev_answer):
469 thresholdValid = True
470 else:
471 thresholdValid = self.interface_funcs_dict[
472 "valid_input_for_pydmodel"
473 ](HomologyFilter, "thresholdValue", [answrThreshVal])
475 if thresholdValid:
476 thresholdValue.append(answrThreshVal)
478 thresholdValid = False
480 self.call_next_question(thresholdValue, self.q2_6_1, self.q2_7, answer)
482 def q2_6(self, answer: Any = None):
483 """
484 Get the custom metric for homology filtering.
485 """
486 customThreshold = self.interface_funcs_dict["get_custom_entry"](
487 "Please provide your custom metric for homology filtering:",
488 multi_choice=True,
489 section_idx=1,
490 prev_answer=answer,
491 )
493 if isinstance(customThreshold, GoQBack):
494 self.call_last_question()
495 return
497 self.call_next_question(customThreshold, self.q2_6, self.q2_6_1, answer)
499 def q2_5_3(self, answer: Any = None):
500 """
501 Get the metric used for homology filtering.
502 """
503 thresholdChoice = self.interface_funcs_dict["get_enum_choice_conversion"](
504 ThresholdChoices,
505 "Please choose the metric used for homology filtering:",
506 multi_choice=True,
507 section_idx=1,
508 prev_answer=answer,
509 )
511 if isinstance(thresholdChoice, GoQBack):
512 self.call_last_question()
513 return
514 elif thresholdChoice and ThresholdChoices.CUSTOM in thresholdChoice:
515 next_question_callable = self.q2_6
516 else:
517 next_question_callable = self.q2_6_1
519 self.call_next_question(
520 thresholdChoice, self.q2_5_3, next_question_callable, answer
521 )
523 def q2_5_2(self, answer: Any = None):
524 """
525 Get the custom structural similarity search software/method used for homology filtering
526 """
527 structSim = self.interface_funcs_dict["get_custom_entry"](
528 "Please provide the structural similarity search software/method used for homology filtering:",
529 multi_choice=False,
530 section_idx=1,
531 prev_answer=answer,
532 )
534 if isinstance(structSim, GoQBack):
535 self.call_last_question()
536 return
538 self.call_next_question(structSim, self.q2_5_2, self.q2_5_3, answer)
540 def q2_5_1(self, answer: Any = None):
541 """
542 Check if structural similarity was used for homology filtering.
543 """
544 structSim = self.interface_funcs_dict["get_yes_no"](
545 "Did you use structural similarity for homology filtering? (yes/no)",
546 section_idx=1,
547 prev_answer=answer,
548 )
550 if isinstance(structSim, GoQBack):
551 self.call_last_question()
552 return
554 if structSim:
555 next_question_callable = self.q2_5_2
556 else:
557 next_question_callable = self.q2_5_3
559 self.call_next_question(structSim, self.q2_5_1, next_question_callable, answer)
561 def q2_4(self, answer: Any = None):
562 """
563 Get the custom sequence type(s) used for homology filtering.
564 """
565 customSeqType = self.interface_funcs_dict["get_custom_entry"](
566 "Please provide your custom sequence type(s) used for homology filtering:",
567 multi_choice=True,
568 section_idx=1,
569 prev_answer=answer,
570 )
572 if isinstance(customSeqType, GoQBack):
573 self.call_last_question()
574 return
576 self.call_next_question(customSeqType, self.q2_4, self.q2_5_1, answer)
578 def q2_3(self, answer: Any = None):
579 """
580 Get the sequence type(s) used for homology filtering.
581 """
582 seqTypeChoice = self.interface_funcs_dict["get_enum_choice_conversion"](
583 SeqTypeChoices,
584 "Please choose your sequence type(s) used for homology filtering:",
585 multi_choice=True,
586 section_idx=1,
587 prev_answer=answer,
588 )
590 if isinstance(seqTypeChoice, GoQBack):
591 self.call_last_question()
592 return
593 elif seqTypeChoice and SeqTypeChoices.CUSTOM in seqTypeChoice:
594 next_question_callable = self.q2_4
595 else:
596 next_question_callable = self.q2_5_1
598 self.call_next_question(
599 seqTypeChoice, self.q2_3, next_question_callable, answer
600 )
602 def q2_2_1(self, answer: Any = None):
603 """
604 Get the taxonomic ID where the de novo genes emerged.
605 """
606 taxIDvalid = False
608 while not taxIDvalid:
609 warnings.filterwarnings("error")
611 answerTaxonID = self.interface_funcs_dict["get_custom_entry"](
612 "Please provide the taxonomic ID (name or number from NCBI Taxonomy DB) where they emerged:",
613 section_idx=1,
614 prev_answer=answer,
615 )
617 if isinstance(answerTaxonID, GoQBack):
618 self.call_last_question()
619 return
621 if answer and not different_answers(answerTaxonID, answer):
622 taxIDvalid = True
623 else:
624 taxIDvalid = self.interface_funcs_dict["valid_input_for_pydmodel"](
625 TaxonID, "taxID", answerTaxonID
626 )
628 warnings.filterwarnings("ignore")
630 self.call_next_question(answerTaxonID, self.q2_2_1, self.q2_3, answer)
632 def q2_2(self, answer: Any = None):
633 """
634 Get the taxonomic group where the de novo genes emerged.
635 """
636 taxSpecificity = self.interface_funcs_dict["get_enum_choice_conversion"](
637 TaxSpecificityChoices,
638 "Please choose the specificity for the taxonomic group where they emerged:",
639 section_idx=1,
640 prev_answer=answer,
641 )
643 if isinstance(taxSpecificity, GoQBack):
644 self.call_last_question()
645 return
647 self.call_next_question(taxSpecificity, self.q2_2, self.q2_2_1, answer)
649 def q2_1(self, answer: Any = None):
650 """
651 Check if phylogenetic taxa information is known.
652 """
653 phylogeneticTaxa = self.interface_funcs_dict["get_yes_no"](
654 "Do you know in which taxonomic group your de novo gene candidates emerged? (yes/no)",
655 section_idx=1,
656 prev_answer=answer,
657 )
659 if isinstance(phylogeneticTaxa, GoQBack):
660 self.call_last_question()
661 return
662 elif phylogeneticTaxa:
663 next_question_callable = self.q2_2 # get_PhylogeneticTaxa
664 else:
665 next_question_callable = self.q2_3
667 self.call_next_question(
668 phylogeneticTaxa, self.q2_1, next_question_callable, answer
669 )
671 def q2(self, answer: Any = None):
672 """
673 Check if a homology filter was applied.
674 """
675 homologyFilter = self.interface_funcs_dict["get_yes_no"](
676 "Did you validate absence of homology of your de novo genes? (yes/no)",
677 section_idx=1,
678 prev_answer=answer,
679 )
681 if isinstance(homologyFilter, GoQBack):
682 self.call_last_question()
683 return
684 elif homologyFilter:
685 next_question_callable = self.q2_1
686 else:
687 next_question_callable = self.q3
689 self.call_next_question(homologyFilter, self.q2, next_question_callable, answer)
691 def q1_3(self, answer: Any = None):
692 """
693 Get the custom input data (not annotated genome or transcriptome).
694 """
695 customInputData = self.interface_funcs_dict["get_custom_entry"](
696 "Please provide your custom input data for de novo gene detection:",
697 section_idx=0,
698 prev_answer=answer,
699 )
701 if isinstance(customInputData, GoQBack):
702 self.call_last_question()
703 return
705 self.call_next_question(customInputData, self.q1_3, self.q2, answer)
707 def q1_2_7(self, answer: Any = None):
708 """
709 Get the additional, custom transcriptome information from the user.
710 """
711 inputDataChoice = self.question_stack[0][1]
713 transcriptomeInfo = self.interface_funcs_dict["get_custom_entry"](
714 "Please provide the information about the transcriptome (e.g. tissue, cell type, ...):",
715 section_idx=0,
716 prev_answer=answer,
717 )
719 if isinstance(transcriptomeInfo, GoQBack):
720 self.call_last_question()
721 return
722 elif inputDataChoice and InputDataChoices.CUSTOM in inputDataChoice:
723 next_question_callable = self.q1_3
724 else:
725 next_question_callable = self.q2
727 self.call_next_question(
728 transcriptomeInfo, self.q1_2_7, next_question_callable, answer
729 )
731 def q1_2_6(self, answer: Any = None):
732 """
733 Check if additional info about the transcriptome should be added.
734 """
735 inputDataChoice = self.question_stack[0][1]
737 transcriptomeInfo = self.interface_funcs_dict["get_yes_no"](
738 "Do you want to add additional information about the transcriptome (e.g. tissue, cell type, ...)? (yes/no)",
739 section_idx=0,
740 prev_answer=answer,
741 )
743 if isinstance(transcriptomeInfo, GoQBack):
744 self.call_last_question()
745 return
746 elif transcriptomeInfo:
747 next_question_callable = self.q1_2_7
748 elif inputDataChoice and InputDataChoices.CUSTOM in inputDataChoice:
749 next_question_callable = self.q1_3
750 else:
751 next_question_callable = self.q2
753 self.call_next_question(
754 transcriptomeInfo, self.q1_2_6, next_question_callable, answer
755 )
757 def q1_2_5(self, answer: Any = None):
758 """
759 Get the custom ORF selection for the transcriptome data.
760 """
761 customORF = self.interface_funcs_dict["get_custom_entry"](
762 "Please provide your custom ORF selection for your transcriptome data:",
763 multi_choice=True,
764 section_idx=0,
765 prev_answer=answer,
766 )
768 if isinstance(customORF, GoQBack):
769 self.call_last_question()
770 return
772 self.call_next_question(customORF, self.q1_2_5, self.q1_2_6, answer)
774 def q1_2_4(self, answer: Any = None):
775 """
776 Get which ORFs in transcripts were selected.
777 """
778 ORFChoiceValid = False
780 while not ORFChoiceValid:
781 transORFChoice = self.interface_funcs_dict["get_enum_choice_conversion"](
782 ORFChoices,
783 "Please choose which ORFs in the transcripts were selected:",
784 multi_choice=True,
785 section_idx=0,
786 prev_answer=answer,
787 )
789 if isinstance(transORFChoice, GoQBack):
790 self.call_last_question()
791 return
793 if answer and not different_answers(transORFChoice, answer):
794 ORFChoiceValid = True
795 else:
796 ORFChoiceValid = self.interface_funcs_dict["valid_input_for_pydmodel"](
797 Transcriptome, "transORFChoice", transORFChoice
798 )
800 if transORFChoice and ORFChoices.CUSTOM in transORFChoice:
801 next_question_callable = self.q1_2_5
802 else:
803 next_question_callable = self.q1_2_6
805 self.call_next_question(
806 transORFChoice, self.q1_2_4, next_question_callable, answer
807 )
809 def q1_2_3(self, answer: Any = None):
810 """
811 Get the custom genetic context information from the user.
812 """
813 customGeneticContext = self.interface_funcs_dict["get_custom_entry"](
814 "Please provide your custom genetic context for your transcriptome data:",
815 multi_choice=True,
816 section_idx=0,
817 prev_answer=answer,
818 )
820 if isinstance(customGeneticContext, GoQBack):
821 self.call_last_question()
822 return
824 self.call_next_question(customGeneticContext, self.q1_2_3, self.q1_2_4, answer)
826 def q1_2_2(self, answer: Any = None):
827 """
828 Get the genetic context information from the user.
829 """
830 transContextChoice = self.interface_funcs_dict[
831 "get_enum_choice_conversion"
832 ](
833 GeneticContextChoices,
834 "Please indicate which transcripts were kept based on their overlap with the following genetic contexts:", # add a None / not filtererd any genetic context option?
835 multi_choice=True,
836 section_idx=0,
837 prev_answer=answer,
838 )
840 if isinstance(transContextChoice, GoQBack):
841 self.call_last_question()
842 return
843 elif transContextChoice and GeneticContextChoices.CUSTOM in transContextChoice:
844 next_question_callable = self.q1_2_3
845 else:
846 next_question_callable = self.q1_2_4
848 self.call_next_question(
849 transContextChoice, self.q1_2_2, next_question_callable, answer
850 )
852 def q1_2_1(self, answer: Any = None):
853 """
854 Get the TPM threshold used as a minimum level of transcript expression.
855 """
856 validExpressionLevel = False
858 while not validExpressionLevel:
859 expressionLevel = self.interface_funcs_dict["get_custom_entry"](
860 "Please provide the TPM threshold used as a minimum level of transcript expression:",
861 section_idx=0,
862 prev_answer=answer,
863 )
864 if isinstance(expressionLevel, GoQBack):
865 self.call_last_question()
866 return
868 if answer and not different_answers(expressionLevel, answer):
869 validExpressionLevel = True
870 else:
871 validExpressionLevel = self.interface_funcs_dict[
872 "valid_input_for_pydmodel"
873 ](Transcriptome, "expressionLevel", expressionLevel)
875 self.call_next_question(expressionLevel, self.q1_2_1, self.q1_2_2, answer)
877 def q1_2(self, answer: Any = None):
878 """
879 Get the transcriptome information from the user.
880 """
881 inputDataChoice = self.question_stack[0][1]
883 answerExpressionLevel = self.interface_funcs_dict[
884 "get_yes_no"
885 ](
886 "Did you apply a TPM threshold used as a minimum level of transcript expression? (yes/no)", # None means unknown/no threshold?
887 section_idx=0,
888 prev_answer=answer,
889 )
891 if isinstance(answerExpressionLevel, GoQBack):
892 self.call_last_question()
893 return
894 elif answerExpressionLevel:
895 next_question_callable = self.q1_2_1
896 elif inputDataChoice and InputDataChoices.CUSTOM in inputDataChoice:
897 next_question_callable = self.q1_3
898 else:
899 next_question_callable = self.q1_2_2
901 self.call_next_question(
902 answerExpressionLevel, self.q1_2, next_question_callable, answer
903 )
905 def q1_1(self, answer: Any = None):
906 """
907 Get the genome annotation method from the user.
908 """
909 inputDataChoice = self.question_stack[0][1]
910 genomeChoicevalid = False
912 while not genomeChoicevalid:
913 annoGenom = self.interface_funcs_dict["get_enum_choice_conversion"](
914 AnnotGenomeChoices,
915 "Please choose the genome annotation method:",
916 multi_choice=True,
917 section_idx=0,
918 prev_answer=answer,
919 )
921 if isinstance(annoGenom, GoQBack):
922 self.call_last_question()
923 return
925 if answer and not different_answers(annoGenom, answer):
926 genomeChoicevalid = True
927 else:
928 genomeChoicevalid = self.interface_funcs_dict[
929 "valid_input_for_pydmodel"
930 ](AnnotGenome, "annotGenomeChoice", annoGenom)
932 if inputDataChoice and InputDataChoices.TRANSCRIPTOME in inputDataChoice:
933 next_question_callable = self.q1_2
934 elif inputDataChoice and InputDataChoices.CUSTOM in inputDataChoice:
935 next_question_callable = self.q1_3
936 else:
937 next_question_callable = self.q2
939 self.call_next_question(annoGenom, self.q1_1, next_question_callable, answer)
941 def q1(self, answer: Any = None):
942 """
943 Get the input data choice from the user.
944 """
945 inputDataChoice = self.interface_funcs_dict["get_enum_choice_conversion"](
946 InputDataChoices,
947 "Did you detect your candidate de novo genes from a:",
948 multi_choice=True,
949 section_idx=0,
950 prev_answer=answer,
951 )
953 if isinstance(inputDataChoice, GoQBack):
954 self.call_last_question()
955 return
956 elif inputDataChoice and InputDataChoices.ANNOT_GENOME in inputDataChoice:
957 next_question_callable = self.q1_1
958 elif inputDataChoice and InputDataChoices.TRANSCRIPTOME in inputDataChoice:
959 next_question_callable = self.q1_2
960 elif inputDataChoice and InputDataChoices.CUSTOM in inputDataChoice:
961 next_question_callable = self.q1_3
962 else:
963 raise ValueError(
964 "Invalid input data choice. Please select from the following options: "
965 "ANNOT_GENOME, TRANSCRIPTOME, CUSTOM."
966 )
968 self.call_next_question(
969 inputDataChoice, self.q1, next_question_callable, answer
970 )
972 def get_DeNovoGeneAnnotation_from_qstack(
973 self,
974 match_dict: dict[str, Any] = FUNCS_TO_MODELS_DICT,
975 ) -> DeNovoGeneAnnotation:
976 """
977 Create a DeNovoGeneAnnotation object from the given question stack.
979 :param question_stack: The question stack.
980 :type question_stack: list[tuple[callable, Any]]
981 :param match_dict: The dictionary to match function callables to field names.
982 :type match_dict: dict[str, Any]
983 :return: The DeNovoGeneAnnotation object.
984 :rtype: DeNovoGeneAnnotation
985 """
986 # match function callables to field names
987 qstack_dict = dict(
988 map(
989 lambda kv: (match_dict[kv[0].__func__.__name__], kv[1]),
990 self.question_stack,
991 )
992 )
994 # create all sub-models
995 annotGenome = get_model_from_qstack_dict(qstack_dict, AnnotGenome)
996 transcriptome = get_model_from_qstack_dict(qstack_dict, Transcriptome)
997 evolInfo = get_model_from_qstack_dict(qstack_dict, EvolutionaryInformation)
998 if qstack_dict.get("taxID", False) and qstack_dict.get(
999 "phylogeneticTaxa", False
1000 ):
1001 taxonID = get_model_from_qstack_dict(qstack_dict, TaxonID)
1002 qstack_dict["taxonID"] = taxonID
1003 phylogeneticTaxa = get_model_from_qstack_dict(qstack_dict, PhylogeneticTaxa)
1004 qstack_dict["phylogeneticTaxa"] = phylogeneticTaxa
1005 homologyFilter = get_model_from_qstack_dict(qstack_dict, HomologyFilter)
1006 if qstack_dict.get("synteny", False):
1007 syntenySearch = get_model_from_qstack_dict(qstack_dict, SyntenySearch)
1008 qstack_dict["synteny"] = syntenySearch
1009 nonCodingHomologs = get_model_from_qstack_dict(qstack_dict, NonCodingHomologs)
1010 translatEvidnce = get_model_from_qstack_dict(qstack_dict, TranslationalEvidence)
1012 # create the DeNovoGeneAnnotation object
1013 return DeNovoGeneAnnotation(
1014 inputData=qstack_dict["inputData"],
1015 inputAnnotGenome=annotGenome,
1016 inputTranscriptome=transcriptome,
1017 customInputData=qstack_dict.get("customInputData", None),
1018 evolutionaryInformation=evolInfo,
1019 homologyFilter=homologyFilter,
1020 nonCodingHomologs=nonCodingHomologs,
1021 translationalEvidence=translatEvidnce,
1022 studyURL=qstack_dict.get("studyURL", None),
1023 )
1025 def start_questionnaire(self) -> DeNovoGeneAnnotation:
1026 """
1027 Start the questionnaire to get the user input.
1029 :return: The DeNovoGeneAnnotation object.
1030 :rtype: DeNovoGeneAnnotation
1031 """
1033 self.q1()
1035 if not self.question_stack:
1036 raise ValueError(
1037 "Question stack is empty. Ensure that questions are answered before proceeding."
1038 )
1040 return self.get_DeNovoGeneAnnotation_from_qstack()