diff --git a/src/fiber_package/communication/activity.py b/src/fiber_package/communication/activity.py index e6f0786..9e9af96 100644 --- a/src/fiber_package/communication/activity.py +++ b/src/fiber_package/communication/activity.py @@ -1,5 +1,5 @@ -from typing import Literal, Self -from pydantic import model_validator +from typing import Literal, Self, Annotated +from pydantic import model_validator, Field from datetime import datetime @@ -8,8 +8,17 @@ from .submodels import ActivityPaymentInfo from .enums import CommunicationCategory from fiber_package.refs import SignupRefList +from fiber_package.signup.signup import SignupModel +from fiber_package.signup.util import validate_answer -from fiber_package.signup.elements.form_element import FormElement +from fiber_package.signup.element.form_base_text import FormBaseText +from fiber_package.signup.element.form_base_question import FormBaseQuestion + +# Special type which uses enum category to decide type! +FieldUnionType = Annotated[ + FormBaseText | FormBaseQuestion, + Field(discriminator="category") +] class ActivityModel(CommunicationModel): category: Literal[CommunicationCategory.activity] @@ -22,7 +31,7 @@ class ActivityModel(CommunicationModel): payment_info: ActivityPaymentInfo | None = None - form: list[FormElement] + form: list[FieldUnionType] signups: SignupRefList = SignupRefList([], "signup") @@ -34,3 +43,37 @@ class ActivityModel(CommunicationModel): if self.end_time < self.start_time: raise ValueError("End time cannot be earlier than start time") return self + + def validate_signup_form(self, signup: SignupModel) -> None: + required = set() + answerable = set() + + question_dict: dict[str, FormBaseQuestion] = {} + + for element in self.form: + if isinstance(element, FormBaseText): + continue + # so we know it is istance of FormBaseQuestion + answerable.add(element.name) + if element.required: + required.add(element.name) + question_dict[element.name] = element + + answers = signup.answers + for answer in answers: + q_name = answer.question_name + if q_name not in answerable: + raise ValueError( + f"Form contains answer to " + f"non-existing question {q_name}." + ) + answerable.remove(q_name) + if answer.question_name in required: + required.remove(q_name) + + question = question_dict[q_name] + validate_answer(question, answer) + + + if len(required) > 0: + raise ValueError("Form does not answer all required questions.") diff --git a/src/fiber_package/signup/element/form_base_question.py b/src/fiber_package/signup/element/form_base_question.py index 11365f0..c0c1942 100644 --- a/src/fiber_package/signup/element/form_base_question.py +++ b/src/fiber_package/signup/element/form_base_question.py @@ -1,6 +1,9 @@ from .form_element import FormElement +from fiber_package.signup.enums import FormQuestionCategory class FormBaseQuestion(FormElement): + category: FormQuestionCategory + required: bool title: str | None = None diff --git a/src/fiber_package/signup/element/form_base_text.py b/src/fiber_package/signup/element/form_base_text.py new file mode 100644 index 0000000..1cffa93 --- /dev/null +++ b/src/fiber_package/signup/element/form_base_text.py @@ -0,0 +1,5 @@ +from .form_element import FormElement +from fiber_package.signup.enums import FormTextCategory + +class FormBaseText(FormElement): + category: FormTextCategory diff --git a/src/fiber_package/signup/element/form_open_questions.py b/src/fiber_package/signup/element/form_open_questions.py index 8ad5de2..c0429a8 100644 --- a/src/fiber_package/signup/element/form_open_questions.py +++ b/src/fiber_package/signup/element/form_open_questions.py @@ -24,7 +24,7 @@ class FormOpenQuestions(FormBaseQuestion): def validate_answer(self, answer: AnswerOpenQuestions) -> None: if answer.question_name != self.name: raise ValueError("Answer question name does not match.") - + answer_list = answer.answer_list if len(answer_list) != len(self.questions): raise ValueError("Length of answer question list does not match.") diff --git a/src/fiber_package/signup/enums.py b/src/fiber_package/signup/enums.py index 5455ec8..6dff12b 100644 --- a/src/fiber_package/signup/enums.py +++ b/src/fiber_package/signup/enums.py @@ -12,3 +12,8 @@ class FormElementCategory(StrEnum): FormQuestionCategory = Literal[ FormElementCategory.open_questions ] + +FormTextCategory = Literal[ + FormElementCategory.text_piece, + FormElementCategory.title_text +] diff --git a/src/fiber_package/signup/util.py b/src/fiber_package/signup/util.py new file mode 100644 index 0000000..91d037f --- /dev/null +++ b/src/fiber_package/signup/util.py @@ -0,0 +1,18 @@ +from fiber_package.signup.element.form_base_question import FormBaseQuestion +from fiber_package.signup.element.form_open_questions import FormOpenQuestions + +from fiber_package.signup.answer.answer_base import AnswerBase +from fiber_package.signup.enums import FormElementCategory + +from fiber_package.signup.answer.answer_open_questions import AnswerOpenQuestions + +def validate_answer(question: FormBaseQuestion, answer: AnswerBase) -> None: + if question.category != answer.category: + raise ValueError("Question and answer category do not match!") + + if question.category == FormElementCategory.open_questions: + question_val = FormOpenQuestions.model_validate(question.model_dump()) + answer_val = AnswerOpenQuestions.model_validate(answer.model_dump()) + question_val.validate_answer(answer_val) + + # TODO: implement