diff --git a/app/db/queries/application.py b/app/db/queries/application.py index 0938d57..5844009 100644 --- a/app/db/queries/application.py +++ b/app/db/queries/application.py @@ -62,18 +62,52 @@ def _initiate_cloned_page(to_clone: Page, new_form_id=None): return clone -def clone_single_page(page_id: str, new_form_id=None) -> Page: - page_to_clone: Page = db.session.query(Page).where(Page.page_id == page_id).one_or_none() - clone = _initiate_cloned_page(page_to_clone, new_form_id) +def _initiate_cloned_form(to_clone: Form, new_section_id: str) -> Form: + clone = Form(**to_clone.as_dict()) + clone.form_id = uuid4() + clone.section_id = new_section_id + clone.is_template = False + clone.source_template_id = to_clone.form_id + clone.template_name = None + clone.pages = [] + return clone + +def clone_single_form(form_id: str, new_section_id=None) -> Form: + form_to_clone: Form = db.session.query(Form).where(Form.form_id == form_id).one_or_none() + clone = _initiate_cloned_form(form_to_clone, new_section_id) + + cloned_pages = [] cloned_components = [] - for component_to_clone in page_to_clone.components: + for page_to_clone in form_to_clone.pages: + + cloned_page = _initiate_cloned_page(page_to_clone, new_form_id=clone.form_id) + cloned_pages.append(cloned_page) + cloned_components.extend(_initiate_cloned_components_for_page(page_to_clone.components, cloned_page.page_id)) + db.session.add_all([clone, *cloned_pages, *cloned_components]) + db.session.commit() + + return clone + + +def _initiate_cloned_components_for_page( + components_to_clone: list[Component], new_page_id: str = None, new_theme_id: str = None +): + cloned_components = [] + for component_to_clone in components_to_clone: cloned_component = _initiate_cloned_component( - component_to_clone, new_page_id=clone.page_id, new_theme_id=None + component_to_clone, new_page_id=new_page_id, new_theme_id=None ) # TODO how should themes work when cloning? cloned_components.append(cloned_component) - # clone.components = cloned_components + return cloned_components + + +def clone_single_page(page_id: str, new_form_id=None) -> Page: + page_to_clone: Page = db.session.query(Page).where(Page.page_id == page_id).one_or_none() + clone = _initiate_cloned_page(page_to_clone, new_form_id) + + cloned_components = _initiate_cloned_components_for_page(page_to_clone.components, new_page_id=clone.page_id) db.session.add_all([clone, *cloned_components]) db.session.commit() diff --git a/tests/test_clone.py b/tests/test_clone.py index 1719243..ca14b97 100644 --- a/tests/test_clone.py +++ b/tests/test_clone.py @@ -5,10 +5,13 @@ from app.db.models import Component from app.db.models import ComponentType from app.db.models import Page +from app.db.models.application_config import Form from app.db.queries.application import _initiate_cloned_component +from app.db.queries.application import _initiate_cloned_form from app.db.queries.application import _initiate_cloned_page from app.db.queries.application import clone_multiple_components from app.db.queries.application import clone_single_component +from app.db.queries.application import clone_single_form from app.db.queries.application import clone_single_page @@ -24,6 +27,31 @@ def mock_new_uuid(mocker): # ===================================================================================================================== +def test_initiate_cloned_form(mock_new_uuid): + clone: Form = Form( + form_id="old-id", + name_in_apply_json={"en": "test form 1"}, + section_id="old-section-id", + is_template=True, + template_name="Template Page", + runner_publish_name="template-form-1", + ) + result: Form = _initiate_cloned_form(to_clone=clone, new_section_id="new-section") + assert result + assert result.form_id == mock_new_uuid + + # Check other bits are the same + assert result.name_in_apply_json == clone.name_in_apply_json + assert result.runner_publish_name == clone.runner_publish_name + + # check template settings + assert result.is_template is False + assert result.source_template_id == "old-id" + assert result.template_name is None + + assert result.section_id == "new-section" + + def test_initiate_cloned_page(mock_new_uuid): clone: Page = Page( page_id="old-id", @@ -324,3 +352,198 @@ def test_clone_page_with_components(seed_dynamic_data, _db): assert len(old_page_from_db.components) == 3 for component in old_page_from_db.components: assert str(component.component_id) in old_component_ids + + +@pytest.mark.seed_config( + { + "forms": [ + Form( + form_id=uuid4(), + section_id=None, + name_in_apply_json={"en": "UT Form 1"}, + section_index=2, + runner_publish_name="ut-form-1", + ) + ] + } +) +def test_clone_form_no_pages(seed_dynamic_data, _db): + old_form = _db.session.get(Form, seed_dynamic_data["forms"][0].form_id) + assert old_form + + result = clone_single_form(form_id=old_form.form_id, new_section_id=None) + assert result + assert result.form_id != old_form.form_id + + cloned_form = _db.session.get(Form, result.form_id) + assert cloned_form + assert len(cloned_form.pages) == 0 + + old_form_from_db = _db.session.get(Form, old_form.form_id) + assert old_form_from_db + + +form_id_2 = uuid4() + + +@pytest.mark.seed_config( + { + "forms": [ + Form( + form_id=form_id_2, + section_id=None, + name_in_apply_json={"en": "UT Form 2"}, + section_index=2, + runner_publish_name="ut-form-2", + ) + ], + "pages": [ + Page( + page_id=uuid4(), + form_id=form_id_2, + display_path="testing-clone-from-form", + is_template=True, + name_in_apply_json={"en": "Clone testing"}, + form_index=0, + ) + ], + } +) +def test_clone_form_with_page(seed_dynamic_data, _db): + old_form = _db.session.get(Form, seed_dynamic_data["forms"][0].form_id) + assert old_form + + result = clone_single_form(form_id=old_form.form_id, new_section_id=None) + assert result + assert result.form_id != old_form.form_id + + cloned_form = _db.session.get(Form, result.form_id) + assert cloned_form + assert len(cloned_form.pages) == 1 + new_page_id = cloned_form.pages[0].page_id + + old_form_from_db = _db.session.get(Form, old_form.form_id) + assert old_form_from_db + assert len(old_form_from_db.pages) == 1 + old_page_id = old_form_from_db.pages[0].page_id + + assert old_page_id != new_page_id + + +form_id_3 = uuid4() +page_id_2 = uuid4() +page_id_3 = uuid4() + + +@pytest.mark.seed_config( + { + "forms": [ + Form( + form_id=form_id_3, + section_id=None, + name_in_apply_json={"en": "UT Form 2"}, + section_index=2, + runner_publish_name="ut-form-2", + ) + ], + "pages": [ + Page( + page_id=page_id_2, + form_id=form_id_3, + display_path="testing-clone-from-form-2", + is_template=True, + name_in_apply_json={"en": "Clone testing"}, + form_index=0, + ), + Page( + page_id=page_id_3, + form_id=form_id_3, + display_path="testing-clone-from-form-3", + is_template=True, + name_in_apply_json={"en": "Clone testing"}, + form_index=0, + ), + ], + "components": [ + Component( + component_id=uuid4(), + page_id=page_id_2, + title="Template qustion 1?", + type=ComponentType.YES_NO_FIELD, + page_index=1, + theme_id=None, + theme_index=2, + options={"hideTitle": False, "classes": "test-class"}, + runner_component_name="template_question_name_1", + is_template=True, + ), + Component( + component_id=uuid4(), + page_id=page_id_2, + title="Template qustion 2?", + type=ComponentType.YES_NO_FIELD, + page_index=1, + theme_id=None, + theme_index=2, + options={"hideTitle": False, "classes": "test-class"}, + runner_component_name="template_question_name_2", + is_template=True, + ), + Component( + component_id=uuid4(), + page_id=page_id_3, + title="Template qustion 3?", + type=ComponentType.YES_NO_FIELD, + page_index=1, + theme_id=None, + theme_index=2, + options={"hideTitle": False, "classes": "test-class"}, + runner_component_name="template_question_name_3", + is_template=True, + ), + ], + } +) +def test_clone_form_with_pages_and_components(seed_dynamic_data, _db): + old_form = _db.session.get(Form, seed_dynamic_data["forms"][0].form_id) + assert old_form + + result = clone_single_form(form_id=old_form.form_id, new_section_id=None) + assert result + assert result.form_id != old_form.form_id + + cloned_form = _db.session.get(Form, result.form_id) + assert cloned_form + assert len(cloned_form.pages) == 2 + new_page_id_1 = next(p.page_id for p in cloned_form.pages if p.display_path == "testing-clone-from-form-2") + new_page_id_2 = next(p.page_id for p in cloned_form.pages if p.display_path == "testing-clone-from-form-3") + new_page_1: Page = _db.session.get(Page, new_page_id_1) + new_page_2: Page = _db.session.get(Page, new_page_id_2) + + old_form_from_db = _db.session.get(Form, old_form.form_id) + assert old_form_from_db + assert len(old_form_from_db.pages) == 2 + + # Set old page id 1 and 2 to be the ids of the old pages that correspond to the new + # pages 1 and 2 by matching display path + old_page_id_1 = next(p.page_id for p in old_form_from_db.pages if p.display_path == new_page_1.display_path) + old_page_id_2 = next(p.page_id for p in old_form_from_db.pages if p.display_path == new_page_2.display_path) + + assert new_page_id_1 not in [old_page_id_1, old_page_id_2] + assert new_page_id_2 not in [old_page_id_1, old_page_id_2] + + # Check pages and components + + assert len(new_page_1.components) == 2 + assert len(new_page_2.components) == 1 + + old_page_1 = _db.session.get(Page, old_page_id_1) + old_page_2 = _db.session.get(Page, old_page_id_2) + + old_component_ids_1 = [c.component_id for c in old_page_1.components] + old_component_ids_2 = [c.component_id for c in old_page_2.components] + + # check the new components are different than the old ones + assert new_page_1.components[0].component_id not in old_component_ids_1 + assert new_page_1.components[1].component_id not in old_component_ids_1 + assert new_page_2.components[0].component_id not in old_component_ids_2 diff --git a/tests/test_integration.py b/tests/test_integration.py index 1fb87fa..73552fe 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -181,7 +181,7 @@ def test_build_form_json_with_conditions(seed_dynamic_data): # TODO this fails with components from a template (branching logic) -def test_build_assessment_config(seed_dynamic_data): +def test_build_assessment_config_no_branching(seed_dynamic_data): f: Fund = get_fund_by_id(seed_dynamic_data["funds"][0].fund_id) criteria = f.rounds[0].criteria[0]