Skip to content

Commit

Permalink
Fs 4533 clones (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
srh-sloan authored Jul 23, 2024
1 parent 6c73dd2 commit abbdfa6
Show file tree
Hide file tree
Showing 11 changed files with 717 additions and 90 deletions.
10 changes: 10 additions & 0 deletions app/blueprints/fund_builder/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from app.blueprints.fund_builder.forms.round import RoundForm
from app.db.models.fund import Fund
from app.db.models.round import Round
from app.db.queries.application import clone_single_round
from app.db.queries.application import get_form_by_id
from app.db.queries.fund import add_fund
from app.db.queries.fund import get_all_funds
Expand Down Expand Up @@ -67,6 +68,15 @@ def view_app_config(round_id):
return render_template("view_application_config.html", round=round, fund=fund)


@build_fund_bp.route("/fund/<fund_id>/round/<round_id>/clone")
def clone_round(round_id, fund_id):

cloned = clone_single_round(round_id=round_id, new_fund_id=fund_id, new_short_name=f"R-C{randint(0,999)}")
flash(f"Cloned new round: {cloned.short_name}")

return redirect(url_for("build_fund_bp.view_fund", fund_id=fund_id))


@build_fund_bp.route("/fund/round/<round_id>/assessment_config")
def view_assess_config(round_id):
"""
Expand Down
6 changes: 6 additions & 0 deletions app/blueprints/fund_builder/templates/view_fund_config.html
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,12 @@ <h2 class="govuk-heading-m">{{round.title_json["en"]}}</h2>
"href": url_for("build_fund_bp.view_assess_config", round_id=round.round_id),
"classes": "govuk-button--secondary"
}) }}

{{ govukButton({
"text": "Clone this round",
"href": url_for("build_fund_bp.clone_round", round_id=round.round_id, fund_id=fund.fund_id),
"classes": "govuk-button--secondary"
}) }}
{% endfor %}

{% endset %}
Expand Down
32 changes: 32 additions & 0 deletions app/db/migrations/versions/~2024_07_19_1136-3fffc621bff4_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""empty message
Revision ID: 3fffc621bff4
Revises: 5c63de4e4e49
Create Date: 2024-07-19 11:36:32.716999
"""

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision = "3fffc621bff4"
down_revision = "5c63de4e4e49"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("page", schema=None) as batch_op:
batch_op.add_column(sa.Column("controller", sa.String(), nullable=True))

# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("page", schema=None) as batch_op:
batch_op.drop_column("controller")

# ### end Alembic commands ###
34 changes: 34 additions & 0 deletions app/db/migrations/versions/~2024_07_19_1233-3de2807b6917_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""empty message
Revision ID: 3de2807b6917
Revises: 3fffc621bff4
Create Date: 2024-07-19 12:33:29.898715
"""

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision = "3de2807b6917"
down_revision = "3fffc621bff4"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("round", schema=None) as batch_op:
batch_op.add_column(sa.Column("source_template_id", sa.UUID(), nullable=True))
batch_op.add_column(sa.Column("template_name", sa.String(), nullable=True))

# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("round", schema=None) as batch_op:
batch_op.drop_column("template_name")
batch_op.drop_column("source_template_id")

# ### end Alembic commands ###
38 changes: 38 additions & 0 deletions app/db/migrations/versions/~2024_07_19_1320-da88c6b36588_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""empty message
Revision ID: da88c6b36588
Revises: 3de2807b6917
Create Date: 2024-07-19 13:20:24.997440
"""

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision = "da88c6b36588"
down_revision = "3de2807b6917"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("round", schema=None) as batch_op:
batch_op.add_column(sa.Column("prospectus_link", sa.String(), nullable=False))
batch_op.add_column(sa.Column("privacy_notice_link", sa.String(), nullable=False))
batch_op.drop_column("privacy_notice")
batch_op.drop_column("prospectus")

# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("round", schema=None) as batch_op:
batch_op.add_column(sa.Column("prospectus", sa.VARCHAR(), autoincrement=False, nullable=False))
batch_op.add_column(sa.Column("privacy_notice", sa.VARCHAR(), autoincrement=False, nullable=False))
batch_op.drop_column("privacy_notice_link")
batch_op.drop_column("prospectus_link")

# ### end Alembic commands ###
1 change: 1 addition & 0 deletions app/db/models/application_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ class Page(BaseModel):
"Component", order_by="Component.page_index", collection_class=ordering_list("page_index")
)
source_template_id = Column(UUID(as_uuid=True), nullable=True)
controller = Column(String(), nullable=True)

def __repr__(self):
return f"Page(/{self.display_path} - {self.name_in_apply_json['en']}, Components: {self.components})"
Expand Down
34 changes: 21 additions & 13 deletions app/db/models/round.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
from sqlalchemy import Column
from sqlalchemy import DateTime
from sqlalchemy import ForeignKey
from sqlalchemy import String
from sqlalchemy import UniqueConstraint
from sqlalchemy import inspect
from sqlalchemy.dialects.postgresql import JSON
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped
Expand All @@ -23,29 +25,35 @@
class Round(BaseModel):
__table_args__ = (UniqueConstraint("fund_id", "short_name"),)
round_id = Column(
"round_id",
UUID(as_uuid=True),
primary_key=True,
default=uuid.uuid4,
nullable=False,
)
fund_id = Column(
"fund_id",
UUID(as_uuid=True),
ForeignKey("fund.fund_id"),
nullable=False,
)
title_json = Column("title_json", JSON(none_as_null=True), nullable=False, unique=False)
short_name = Column("short_name", db.String(), nullable=False, unique=False)
opens = Column("opens", DateTime())
deadline = Column("deadline", DateTime())
assessment_start = Column("assessment_start", DateTime())
reminder_date = Column("reminder_date", DateTime())
assessment_deadline = Column("assessment_deadline", DateTime())
prospectus_link = Column("prospectus", db.String(), nullable=False, unique=False)
privacy_notice_link = Column("privacy_notice", db.String(), nullable=False, unique=False)
audit_info = Column("audit_info", JSON(none_as_null=True))
is_template = Column("is_template", Boolean, default=False, nullable=False)
title_json = Column(JSON(none_as_null=True), nullable=False, unique=False)
short_name = Column(db.String(), nullable=False, unique=False)
opens = Column(DateTime())
deadline = Column(DateTime())
assessment_start = Column(DateTime())
reminder_date = Column(DateTime())
assessment_deadline = Column(DateTime())
prospectus_link = Column(db.String(), nullable=False, unique=False)
privacy_notice_link = Column(db.String(), nullable=False, unique=False)
audit_info = Column(JSON(none_as_null=True))
is_template = Column(Boolean, default=False, nullable=False)
source_template_id = Column(UUID(as_uuid=True), nullable=True)
template_name = Column(String(), nullable=True)
sections: Mapped[list["Section"]] = relationship("Section")
criteria: Mapped[list["Criteria"]] = relationship("Criteria")
# several other fields to add

def __repr__(self):
return f"Round({self.short_name - self.title_json['en']}, Sections: {self.sections})"

def as_dict(self):
return {col.name: self.__getattribute__(col.name) for col in inspect(self).mapper.columns}
106 changes: 100 additions & 6 deletions app/db/queries/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from app.db.models import Form
from app.db.models import Lizt
from app.db.models import Page
from app.db.models.application_config import Section
from app.db.models.round import Round


def get_form_for_component(component: Component) -> Form:
Expand Down Expand Up @@ -62,18 +64,90 @@ 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 _initiate_cloned_section(to_clone: Section, new_round_id: str) -> Form:
clone = Section(**to_clone.as_dict())
clone.round_id = new_round_id
clone.section_id = uuid4()
clone.is_template = False
clone.source_template_id = to_clone.section_id
clone.template_name = None
clone.pages = []
return clone


def clone_single_section(section_id: str, new_round_id=None) -> Section:
section_to_clone: Section = db.session.query(Section).where(Section.section_id == section_id).one_or_none()
clone = _initiate_cloned_section(section_to_clone, new_round_id)

cloned_forms = []
cloned_pages = []
cloned_components = []
# loop through forms in this section and clone each one
for form_to_clone in section_to_clone.forms:
cloned_form = _initiate_cloned_form(form_to_clone, clone.section_id)
# loop through pages in this section and clone each one
for page_to_clone in form_to_clone.pages:
cloned_page = _initiate_cloned_page(page_to_clone, new_form_id=cloned_form.form_id)
cloned_pages.append(cloned_page)
# clone the components on this page
cloned_components.extend(
_initiate_cloned_components_for_page(page_to_clone.components, cloned_page.page_id)
)

cloned_forms.append(cloned_form)

db.session.add_all([clone, *cloned_forms, *cloned_pages, *cloned_components])
db.session.commit()

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()

Expand Down Expand Up @@ -105,3 +179,23 @@ def clone_multiple_components(component_ids: list[str], new_page_id=None, new_th
db.session.commit()

return clones


def clone_single_round(round_id, new_fund_id, new_short_name) -> Round:
round_to_clone = db.session.query(Round).where(Round.round_id == round_id).one_or_none()
cloned_round = Round(**round_to_clone.as_dict())
cloned_round.short_name = new_short_name
cloned_round.round_id = uuid4()
cloned_round.fund_id = new_fund_id
cloned_round.is_template = False
cloned_round.source_template_id = round_to_clone.round_id
cloned_round.template_name = None
cloned_round.sections = []

db.session.add(cloned_round)
db.session.commit()

for section in round_to_clone.sections:
clone_single_section(section.section_id, cloned_round.round_id)

return cloned_round
Loading

0 comments on commit abbdfa6

Please sign in to comment.