feat: questionnaire shape update
This commit is contained in:
parent
c268097306
commit
0a7f9092c7
7 changed files with 85 additions and 24 deletions
|
|
@ -16,6 +16,7 @@ Endpoints:
|
||||||
- [PATCH](/org/contact): [root user]: Updates the (contact_type) contact for an org(id). Any number of details can be changed.
|
- [PATCH](/org/contact): [root user]: Updates the (contact_type) contact for an org(id). Any number of details can be changed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from datetime import datetime, timezone
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
|
|
||||||
from fastapi import APIRouter, status
|
from fastapi import APIRouter, status
|
||||||
|
|
@ -29,6 +30,7 @@ from src.contact.models import Contact
|
||||||
from src.contact.schemas import ContactAddress
|
from src.contact.schemas import ContactAddress
|
||||||
from src.contact.exceptions import ContactNotFoundException
|
from src.contact.exceptions import ContactNotFoundException
|
||||||
from src.database import db_dependency
|
from src.database import db_dependency
|
||||||
|
from src.organisation.schemas_questionnaires import QuestionnaireQuestionsVersion0
|
||||||
from src.user.dependencies import (
|
from src.user.dependencies import (
|
||||||
user_model_body_dependency,
|
user_model_body_dependency,
|
||||||
user_model_claims_dependency,
|
user_model_claims_dependency,
|
||||||
|
|
@ -64,6 +66,7 @@ from src.organisation.schemas import (
|
||||||
OrgPatchRootResponse,
|
OrgPatchRootResponse,
|
||||||
Questionnaire,
|
Questionnaire,
|
||||||
OrgPatchContactResponse,
|
OrgPatchContactResponse,
|
||||||
|
QuestionnaireMetadata,
|
||||||
)
|
)
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
|
|
@ -88,7 +91,9 @@ router = APIRouter(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
async def get_org_by_id(org_model: org_model_root_claim_query_dependency):
|
async def get_org_by_id(
|
||||||
|
db: db_dependency, org_model: org_model_root_claim_query_dependency
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Returns organisation details including key member email addresses
|
Returns organisation details including key member email addresses
|
||||||
"""
|
"""
|
||||||
|
|
@ -143,10 +148,21 @@ async def create_org(
|
||||||
ALl organisations are given the "partial" status on creation. See update_questionnaire() for more details.
|
ALl organisations are given the "partial" status on creation. See update_questionnaire() for more details.
|
||||||
"""
|
"""
|
||||||
if request_model.intake_questionnaire:
|
if request_model.intake_questionnaire:
|
||||||
intake_questionnaire = request_model.intake_questionnaire.model_dump()
|
questionnaire_questions = request_model.intake_questionnaire.model_dump()
|
||||||
else:
|
else:
|
||||||
intake_questionnaire = None
|
questionnaire_questions = QuestionnaireQuestionsVersion0().model_dump()
|
||||||
org_model = Org(name=request_model.name, intake_questionnaire=intake_questionnaire)
|
|
||||||
|
questionnaire_metadata = QuestionnaireMetadata(version=0, submission_date=None)
|
||||||
|
|
||||||
|
intake_questionnaire = Questionnaire(
|
||||||
|
metadata=questionnaire_metadata,
|
||||||
|
questions=questionnaire_questions,
|
||||||
|
)
|
||||||
|
|
||||||
|
org_model = Org(
|
||||||
|
name=request_model.name,
|
||||||
|
intake_questionnaire=intake_questionnaire.model_dump(mode="json"),
|
||||||
|
)
|
||||||
|
|
||||||
org_model.status = "partial"
|
org_model.status = "partial"
|
||||||
|
|
||||||
|
|
@ -204,18 +220,27 @@ async def update_questionnaire(
|
||||||
final "are you sure" check before setting the org to be in "submitted" status, awaiting admin approval.
|
final "are you sure" check before setting the org to be in "submitted" status, awaiting admin approval.
|
||||||
"""
|
"""
|
||||||
update_data = request_model.intake_questionnaire.model_dump(exclude_none=True)
|
update_data = request_model.intake_questionnaire.model_dump(exclude_none=True)
|
||||||
questionnaire_model = Questionnaire(**org_model.intake_questionnaire)
|
questionnaire = org_model.intake_questionnaire
|
||||||
|
questions_model = QuestionnaireQuestionsVersion0(**questionnaire["questions"])
|
||||||
for key, value in update_data.items():
|
for key, value in update_data.items():
|
||||||
if hasattr(questionnaire_model, key):
|
if hasattr(questions_model, key):
|
||||||
setattr(questionnaire_model, key, value)
|
setattr(questions_model, key, value)
|
||||||
else:
|
else:
|
||||||
raise UnprocessableContentException("Invalid keys in update request")
|
raise UnprocessableContentException("Invalid keys in update request")
|
||||||
|
|
||||||
|
metadata = QuestionnaireMetadata(version=questionnaire["metadata"]["version"])
|
||||||
|
|
||||||
# Allows for partially completed questionnaires to be saved without being submitted for review
|
# Allows for partially completed questionnaires to be saved without being submitted for review
|
||||||
if not request_model.partial:
|
if not request_model.partial:
|
||||||
org_model.status = "submitted"
|
org_model.status = "submitted"
|
||||||
|
metadata.submission_date = datetime.now(timezone.utc)
|
||||||
|
|
||||||
org_model.intake_questionnaire = questionnaire_model.model_dump()
|
questionnaire_model = Questionnaire(
|
||||||
|
metadata=metadata,
|
||||||
|
questions=questions_model,
|
||||||
|
)
|
||||||
|
|
||||||
|
org_model.intake_questionnaire = questionnaire_model.model_dump(mode="json")
|
||||||
db.flush()
|
db.flush()
|
||||||
response = OrgPatchQuestionnaireResponse(**org_model.__dict__)
|
response = OrgPatchQuestionnaireResponse(**org_model.__dict__)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ Models follow the nomenclature of:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from pydantic import EmailStr, ConfigDict
|
from pydantic import EmailStr, ConfigDict
|
||||||
|
|
||||||
|
|
@ -21,12 +22,17 @@ from src.schemas import (
|
||||||
from src.contact.schemas import ContactModel
|
from src.contact.schemas import ContactModel
|
||||||
|
|
||||||
from src.organisation.constants import Status, ContactType
|
from src.organisation.constants import Status, ContactType
|
||||||
|
from src.organisation.schemas_questionnaires import QuestionnaireQuestionsVersion0
|
||||||
|
|
||||||
|
|
||||||
|
class QuestionnaireMetadata(CustomBaseModel):
|
||||||
|
version: int
|
||||||
|
submission_date: Optional[datetime] = None
|
||||||
|
|
||||||
|
|
||||||
class Questionnaire(CustomBaseModel):
|
class Questionnaire(CustomBaseModel):
|
||||||
question_one: Optional[str] = None
|
metadata: QuestionnaireMetadata
|
||||||
question_two: Optional[str] = None
|
questions: QuestionnaireQuestionsVersion0
|
||||||
question_three: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class ContactSummary(CustomBaseModel):
|
class ContactSummary(CustomBaseModel):
|
||||||
|
|
@ -47,7 +53,7 @@ class OrgSchema(OrgIDMixin):
|
||||||
|
|
||||||
class OrgPostOrgRequest(CustomBaseModel):
|
class OrgPostOrgRequest(CustomBaseModel):
|
||||||
name: str
|
name: str
|
||||||
intake_questionnaire: Optional[Questionnaire] = None
|
intake_questionnaire: Optional[QuestionnaireQuestionsVersion0] = None
|
||||||
|
|
||||||
|
|
||||||
class OrgPostOrgResponse(CustomBaseModel):
|
class OrgPostOrgResponse(CustomBaseModel):
|
||||||
|
|
@ -57,7 +63,7 @@ class OrgPostOrgResponse(CustomBaseModel):
|
||||||
|
|
||||||
|
|
||||||
class OrgPatchQuestionnaireRequest(OrgIDMixin):
|
class OrgPatchQuestionnaireRequest(OrgIDMixin):
|
||||||
intake_questionnaire: Questionnaire
|
intake_questionnaire: QuestionnaireQuestionsVersion0
|
||||||
partial: bool
|
partial: bool
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
13
src/organisation/schemas_questionnaires.py
Normal file
13
src/organisation/schemas_questionnaires.py
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from src.schemas import CustomBaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class QuestionnaireQuestions(CustomBaseModel):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class QuestionnaireQuestionsVersion0(QuestionnaireQuestions):
|
||||||
|
question_one: Optional[str] = None
|
||||||
|
question_two: Optional[str] = None
|
||||||
|
question_three: Optional[str] = None
|
||||||
|
|
@ -103,7 +103,10 @@ def _seed(db):
|
||||||
owner_contact_id=2,
|
owner_contact_id=2,
|
||||||
security_contact_id=3,
|
security_contact_id=3,
|
||||||
status="approved",
|
status="approved",
|
||||||
intake_questionnaire={"question_two": "answer two"},
|
intake_questionnaire={
|
||||||
|
"metadata": {"version": 0, "submission_date": None},
|
||||||
|
"questions": {"question_two": "answer two"},
|
||||||
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
db.add(Service(name="Test Service", api_key="123456789"))
|
db.add(Service(name="Test Service", api_key="123456789"))
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,10 @@ async def test_get_org_auth_root_su(default_client: AsyncClient, db_session):
|
||||||
owner_contact_id=2,
|
owner_contact_id=2,
|
||||||
security_contact_id=3,
|
security_contact_id=3,
|
||||||
status="approved",
|
status="approved",
|
||||||
intake_questionnaire={},
|
intake_questionnaire={
|
||||||
|
"metadata": {"version": 0, "submission_date": None},
|
||||||
|
"questions": {},
|
||||||
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
db_session.flush()
|
db_session.flush()
|
||||||
|
|
|
||||||
|
|
@ -103,9 +103,13 @@ async def test_patch_org_questionnaire_partial_success(
|
||||||
assert data["status"] == "partial"
|
assert data["status"] == "partial"
|
||||||
assert "intake_questionnaire" in data
|
assert "intake_questionnaire" in data
|
||||||
assert isinstance(data["intake_questionnaire"], dict)
|
assert isinstance(data["intake_questionnaire"], dict)
|
||||||
assert data["intake_questionnaire"]["question_one"] == "new answer one"
|
metadata = data["intake_questionnaire"]["metadata"]
|
||||||
assert data["intake_questionnaire"]["question_two"] == "answer two"
|
assert metadata["version"] == 0
|
||||||
assert data["intake_questionnaire"]["question_three"] is None
|
assert metadata["submission_date"] is None
|
||||||
|
questions = data["intake_questionnaire"]["questions"]
|
||||||
|
assert questions["question_one"] == "new answer one"
|
||||||
|
assert questions["question_two"] == "answer two"
|
||||||
|
assert questions["question_three"] is None
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|
@ -172,9 +176,13 @@ async def test_patch_org_questionnaire_submit_success(
|
||||||
assert data["status"] == "submitted"
|
assert data["status"] == "submitted"
|
||||||
assert "intake_questionnaire" in data
|
assert "intake_questionnaire" in data
|
||||||
assert isinstance(data["intake_questionnaire"], dict)
|
assert isinstance(data["intake_questionnaire"], dict)
|
||||||
assert data["intake_questionnaire"]["question_one"] == "new answer one"
|
metadata = data["intake_questionnaire"]["metadata"]
|
||||||
assert data["intake_questionnaire"]["question_two"] == "answer two"
|
assert metadata["version"] == 0
|
||||||
assert data["intake_questionnaire"]["question_three"] is None
|
assert metadata["submission_date"] is not None
|
||||||
|
questions = data["intake_questionnaire"]["questions"]
|
||||||
|
assert questions["question_one"] == "new answer one"
|
||||||
|
assert questions["question_two"] == "answer two"
|
||||||
|
assert questions["question_three"] is None
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|
|
||||||
|
|
@ -161,9 +161,12 @@ async def test_get_self_orgs_dynamic(default_client: AsyncClient):
|
||||||
"security_contact": {"email": "security@test.org", "id": 3},
|
"security_contact": {"email": "security@test.org", "id": 3},
|
||||||
"billing_contact": {"email": "billing@test.org", "id": 1},
|
"billing_contact": {"email": "billing@test.org", "id": 1},
|
||||||
"intake_questionnaire": {
|
"intake_questionnaire": {
|
||||||
"question_one": None,
|
"questions": {
|
||||||
"question_three": None,
|
"question_one": None,
|
||||||
"question_two": "answer two",
|
"question_three": None,
|
||||||
|
"question_two": "answer two",
|
||||||
|
},
|
||||||
|
"metadata": {"version": 0, "submission_date": None},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue