From 41945900c13959e2e68b16203c7c022e8b62ed10 Mon Sep 17 00:00:00 2001 From: auslin-aot <99173163+auslin-aot@users.noreply.github.com> Date: Fri, 17 Jan 2025 15:52:17 +0530 Subject: [PATCH] FWF-3749: [Feature] Application list api changes related to client --- .../formsflow_api_utils/utils/constants.py | 1 + ...3_add_is_draft_column_application_table.py | 36 +++++++++++++++++++ forms-flow-api/requirements.txt | 2 +- forms-flow-api/requirements/prod.txt | 2 +- .../src/formsflow_api/models/application.py | 16 +++++++-- .../formsflow_api/resources/application.py | 11 +++++- .../src/formsflow_api/schemas/application.py | 5 +++ .../src/formsflow_api/services/application.py | 8 +++++ .../src/formsflow_api/services/draft.py | 5 ++- 9 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 forms-flow-api/migrations/versions/524194732683_add_is_draft_column_application_table.py diff --git a/forms-flow-api-utils/src/formsflow_api_utils/utils/constants.py b/forms-flow-api-utils/src/formsflow_api_utils/utils/constants.py index 08b2396b6a..05d28cd490 100644 --- a/forms-flow-api-utils/src/formsflow_api_utils/utils/constants.py +++ b/forms-flow-api-utils/src/formsflow_api_utils/utils/constants.py @@ -42,6 +42,7 @@ "process_name": {"field": "name", "operator": "ilike"}, "process_status": {"field": "status", "operator": "eq"}, "process_type": {"field": "process_type", "operator": "eq"}, + "parent_form_id": {"field": "parent_form_id", "operator": "eq"}, } DEFAULT_PROCESS_KEY = "Defaultflow" diff --git a/forms-flow-api/migrations/versions/524194732683_add_is_draft_column_application_table.py b/forms-flow-api/migrations/versions/524194732683_add_is_draft_column_application_table.py new file mode 100644 index 0000000000..3006aadc4a --- /dev/null +++ b/forms-flow-api/migrations/versions/524194732683_add_is_draft_column_application_table.py @@ -0,0 +1,36 @@ +"""add-is-draft-column-application-table + +Revision ID: 524194732683 +Revises: f581ec5971eb +Create Date: 2025-01-17 14:17:56.829145 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '524194732683' +down_revision = 'f581ec5971eb' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('application', sa.Column('is_draft', sa.Boolean(), nullable=True, server_default='false')) + op.create_index(op.f('ix_application_is_draft'), 'application', ['is_draft'], unique=False) + # Update is_draft column in application table to true for active draft applications + update_query = sa.text("""UPDATE public.application + SET is_draft = true + WHERE id IN (SELECT application_id FROM public.draft WHERE status='1');""") + # Execute the SQL statement + op.execute(update_query) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f('ix_application_is_draft'), table_name='application') + op.drop_column('application', 'is_draft') + # ### end Alembic commands ### diff --git a/forms-flow-api/requirements.txt b/forms-flow-api/requirements.txt index 50daee26df..a3b2ee3c8b 100644 --- a/forms-flow-api/requirements.txt +++ b/forms-flow-api/requirements.txt @@ -26,7 +26,7 @@ ecdsa==0.19.0 flask-jwt-oidc==0.7.0 flask-marshmallow==1.2.1 flask-restx==1.3.0 -formsflow_api_utils @ git+https://github.com/AOT-Technologies/forms-flow-ai.git@develop#subdirectory=forms-flow-api-utils +formsflow_api_utils @ git+https://github.com/auslin-aot/forms-flow-ai.git@feature/fwf-3749-application-get-api-changes#subdirectory=forms-flow-api-utils gunicorn==23.0.0 h11==0.14.0 h2==4.1.0 diff --git a/forms-flow-api/requirements/prod.txt b/forms-flow-api/requirements/prod.txt index 2e0fa6b0d5..8b9eb2ae12 100644 --- a/forms-flow-api/requirements/prod.txt +++ b/forms-flow-api/requirements/prod.txt @@ -17,4 +17,4 @@ markupsafe PyJWT redis lxml -git+https://github.com/AOT-Technologies/forms-flow-ai.git@develop#subdirectory=forms-flow-api-utils +git+https://github.com/auslin-aot/forms-flow-ai.git@feature/fwf-3749-application-get-api-changes#subdirectory=forms-flow-api-utils diff --git a/forms-flow-api/src/formsflow_api/models/application.py b/forms-flow-api/src/formsflow_api/models/application.py index 15a317aaed..57308f4cfa 100644 --- a/forms-flow-api/src/formsflow_api/models/application.py +++ b/forms-flow-api/src/formsflow_api/models/application.py @@ -35,6 +35,7 @@ class Application( latest_form_id = db.Column(db.String(100), nullable=False) is_resubmit = db.Column(db.Boolean, nullable=True, default=False) event_name = db.Column(db.String(100), nullable=True) + is_draft = db.Column(db.Boolean, default=False, index=True) draft = db.relationship( "Draft", backref=db.backref("Application", cascade="save-update, merge, delete") @@ -52,6 +53,7 @@ def create_from_dict(cls, application_info: dict) -> Application: ] application.submission_id = application_info["submission_id"] application.latest_form_id = application_info["form_id"] + application.is_draft = application_info.get("is_draft", False) application.save_and_flush() return application return None @@ -68,6 +70,7 @@ def update(self, mapper_info: dict): "modified_by", "is_resubmit", "event_name", + "is_draft", ], mapper_info, ) @@ -161,7 +164,7 @@ def filter_conditions(cls, **filters): filter_map = FILTER_MAPS[key] model_name = ( Application - if not filter_map["field"] == "form_name" + if filter_map["field"] not in ["form_name", "parent_form_id"] else FormProcessMapper ) condition = Application.create_filter_condition( @@ -184,10 +187,12 @@ def filter_conditions(cls, **filters): cls.modified_by, cls.is_resubmit, cls.event_name, + cls.is_draft, FormProcessMapper.form_name.label("application_name"), FormProcessMapper.process_key.label("process_key"), FormProcessMapper.process_name.label("process_name"), FormProcessMapper.process_tenant.label("process_tenant"), + FormProcessMapper.parent_form_id, ) query = query.filter(*filter_conditions) if filter_conditions else query return query @@ -200,13 +205,18 @@ def find_all_by_user( # pylint: disable=too-many-arguments, too-many-positional limit: int, order_by: str, sort_order: str, + include_drafts: str, + only_drafts: str, **filters, ) -> Application: """Fetch applications list based on searching parameters for Non-reviewer.""" query = cls.filter_conditions(**filters) query = FormProcessMapper.tenant_authorization(query=query) query = query.filter(Application.created_by == user_id) - query = cls.filter_draft_applications(query=query) + if only_drafts: # only draft applications + query = query.filter(Application.is_draft.is_(True)) + elif not include_drafts: # only submissions + query = cls.filter_draft_applications(query=query) order_by, sort_order = validate_sort_order_and_order_by(order_by, sort_order) if order_by and sort_order: table_name = "application" @@ -725,7 +735,7 @@ def filter_draft_applications(cls, query: Query): """Modifies the query to filter draft applications.""" if not isinstance(query, Query): raise TypeError("Query object must be of type Query") - return query.filter(cls.application_status != DRAFT_APPLICATION_STATUS) + return query.filter(cls.is_draft.is_(False)) @classmethod def get_all_application_count(cls): diff --git a/forms-flow-api/src/formsflow_api/resources/application.py b/forms-flow-api/src/formsflow_api/resources/application.py index f991a317d4..f17dcc5a01 100644 --- a/forms-flow-api/src/formsflow_api/resources/application.py +++ b/forms-flow-api/src/formsflow_api/resources/application.py @@ -171,7 +171,12 @@ def get(): # pylint:disable=too-many-locals modified_from_date = dict_data.get("modified_from_date") modified_to_date = dict_data.get("modified_to_date") sort_order = dict_data.get("sort_order", "desc") - if auth.has_role([VIEW_TASKS, MANAGE_TASKS]): + created_user_submissions = dict_data.get("created_user_submissions", False) + parent_form_id = dict_data.get("parent_form_id") + include_drafts = dict_data.get("include_drafts") + only_drafts = dict_data.get("only_drafts") + + if auth.has_role([VIEW_TASKS, MANAGE_TASKS]) and not created_user_submissions: ( application_schema_dump, application_count, @@ -189,6 +194,7 @@ def get(): # pylint:disable=too-many-locals application_status=application_status, page_no=page_no, limit=limit, + parent_form_id=parent_form_id, ) else: ( @@ -208,6 +214,9 @@ def get(): # pylint:disable=too-many-locals application_id=application_id, application_name=application_name, application_status=application_status, + parent_form_id=parent_form_id, + include_drafts=include_drafts, + only_drafts=only_drafts, ) return ( ( diff --git a/forms-flow-api/src/formsflow_api/schemas/application.py b/forms-flow-api/src/formsflow_api/schemas/application.py index 588e500d95..278c766494 100644 --- a/forms-flow-api/src/formsflow_api/schemas/application.py +++ b/forms-flow-api/src/formsflow_api/schemas/application.py @@ -38,6 +38,10 @@ class ApplicationListRequestSchema(ApplicationListReqSchema): data_key="modifiedTo", format="%Y-%m-%dT%H:%M:%S+00:00" ) sort_order = fields.Str(data_key="sortOrder", required=False) + created_user_submissions = fields.Bool(data_key="createdUserSubmissions") + parent_form_id = fields.Str(data_key="parentFormId") + include_drafts = fields.Bool(data_key="includeDrafts") + only_drafts = fields.Bool(data_key="onlyDrafts") class ApplicationSchema(AuditDateTimeSchema): @@ -66,6 +70,7 @@ class Meta: # pylint: disable=too-few-public-methods is_resubmit = fields.Bool(data_key="isResubmit", dump_only=True) event_name = fields.Str(data_key="eventName", dump_only=True) data = fields.Dict(data_key="data", load_only=True) + is_draft = fields.Bool(data_key="isDraft", dump_only=True) class ApplicationUpdateSchema(Schema): diff --git a/forms-flow-api/src/formsflow_api/services/application.py b/forms-flow-api/src/formsflow_api/services/application.py index 98c21c0131..6c3da99396 100644 --- a/forms-flow-api/src/formsflow_api/services/application.py +++ b/forms-flow-api/src/formsflow_api/services/application.py @@ -197,6 +197,7 @@ def get_auth_applications_and_count( # pylint: disable=too-many-arguments,too-m application_status: str, created_by: str, sort_order: str, + parent_form_id: str, **kwargs, ): """Get applications only from authorized groups.""" @@ -230,6 +231,7 @@ def get_auth_applications_and_count( # pylint: disable=too-many-arguments,too-m created_to=created_to, form_ids=form_ids, user_name=user_name, + parent_form_id=parent_form_id, ) draft_count = Draft.get_draft_count() return ( @@ -280,6 +282,9 @@ def get_all_applications_by_user( # pylint: disable=too-many-arguments,too-many application_status: str, application_name: str, application_id: int, + parent_form_id: str, + include_drafts: str, + only_drafts: bool, **kwargs, ): """Get all applications based on user.""" @@ -299,6 +304,9 @@ def get_all_applications_by_user( # pylint: disable=too-many-arguments,too-many modified_to=modified_to, created_from=created_from, created_to=created_to, + parent_form_id=parent_form_id, + include_drafts=include_drafts, + only_drafts=only_drafts, ) draft_count = Draft.get_draft_count() return ( diff --git a/forms-flow-api/src/formsflow_api/services/draft.py b/forms-flow-api/src/formsflow_api/services/draft.py index 7fe685bef7..5f6ae55455 100644 --- a/forms-flow-api/src/formsflow_api/services/draft.py +++ b/forms-flow-api/src/formsflow_api/services/draft.py @@ -55,6 +55,7 @@ def create_new_draft(cls, application_payload, draft_payload, token=None, **kwar application_payload["application_status"] = DRAFT_APPLICATION_STATUS application_payload["submission_id"] = None + application_payload["is_draft"] = True application = cls.__create_draft_application(application_payload) if not application: raise BusinessException(BusinessErrorCode.APPLICATION_CREATE_ERROR) @@ -129,10 +130,12 @@ def make_submission_from_draft(data: Dict, draft_id: str, token=None, **kwargs): application = Application.find_by_id(draft.application_id) mapper = FormProcessMapper.find_form_by_form_id(application.latest_form_id) + update_dict = {"is_draft": False} if application.form_process_mapper_id != mapper.id: # The form mapper version got updated after the draft entry # was created, update the application with new mapper - application.update({"form_process_mapper_id": mapper.id}) + update_dict["form_process_mapper_id"] = mapper.id + application.update(update_dict) task_variables = ( json.loads(mapper.task_variable) if mapper.task_variable is not None