From 62bc6bdd7e3023621ab4f207503575fbad20d5e9 Mon Sep 17 00:00:00 2001 From: signebedi Date: Sun, 15 Sep 2024 12:47:52 -0500 Subject: [PATCH] Added: support for including reviewer comments with an approval decision (#337) --- libreforms_fastapi/app/__init__.py | 10 +++++++- .../review_and_approval_individual.html.jinja | 25 ++++++++++++++++++- libreforms_fastapi/utils/document_database.py | 4 +++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/libreforms_fastapi/app/__init__.py b/libreforms_fastapi/app/__init__.py index 6636245..f498895 100644 --- a/libreforms_fastapi/app/__init__.py +++ b/libreforms_fastapi/app/__init__.py @@ -2722,10 +2722,13 @@ async def api_form_sign( doc_db = Depends(get_doc_db), session: SessionLocal = Depends(get_db), key: str = Depends(X_API_KEY), + reviewer_comments: str = "", ): """ Digitally signs a specific document in a form that requires approval and routes - to the next form stage. Logs the signing action. + to the next form stage. Logs the signing action. You can pass a `reviewer_comments` + query param to associate comments with the review action that is taking place. This + field has a max length of 300 characters. """ if not config.API_ENABLED: @@ -2739,6 +2742,10 @@ async def api_form_sign( if action not in permitted_actions: raise HTTPException(status_code=404, detail=f"Form '{action}' not permitted") + # Ensure reviewer_comments does not exceed 300 characters + if len(reviewer_comments) > 300: + raise HTTPException(status_code=400, detail="Reviewer comments cannot exceed 300 characters.") + # Ugh, I'd like to find a more efficient way to get the user data. But alas, that # the sqlalchemy-signing table is not optimized alongside the user model... user = session.query(User).filter_by(api_key=key).first() @@ -2782,6 +2789,7 @@ async def api_form_sign( # Build the metadata field metadata={ doc_db.last_editor_field: user.username, + doc_db.reviewer_comments_field: reviewer_comments } # Add the remote addr host if enabled diff --git a/libreforms_fastapi/app/templates/review_and_approval_individual.html.jinja b/libreforms_fastapi/app/templates/review_and_approval_individual.html.jinja index f56780e..1a68d4b 100644 --- a/libreforms_fastapi/app/templates/review_and_approval_individual.html.jinja +++ b/libreforms_fastapi/app/templates/review_and_approval_individual.html.jinja @@ -23,6 +23,19 @@

You are currently in the Review & Approval interface for this submission. You can see its details below. If you would like to go back to the general Review & Approval page, click here. If you would like to view this submission outside the Review & Approval interface, click here.

The current stage for this submission is {{stage_conf["label"] if "label" in stage_conf.keys() else form_stage}}.

+ +{% if "on_approve" in stage_conf.keys() or + "on_deny" in stage_conf.keys() or + "on_pushback" in stage_conf.keys() or + "on_confirm" in stage_conf.keys() %} +
+ + Optional. Use this space to provide any reviewer comments you would like to attach along with your decision. These comments will be automatically added when you select an action below. Max length: 300 characters. + +
+ +{% endif %} + {% if "on_approve" in stage_conf.keys()%} {% endif %} @@ -72,8 +85,18 @@ {% endif %} function signForm(action) { + + // Get the value from the reviewer_comments textarea + const reviewerComments = document.getElementById('reviewer_comments').value; + + // Construct the query parameter for reviewer_comments if it's not empty + let queryParams = ""; + if (reviewerComments.trim() !== "") { + queryParams = `?reviewer_comments=${encodeURIComponent(reviewerComments.trim())}`; + } + $.ajax({ - url: `/api/form/sign/${formName}/${documentId}/${action}`, + url: `/api/form/sign/${formName}/${documentId}/${action}${queryParams}`, type: 'PATCH', headers: { 'X-API-KEY': apiKey, diff --git a/libreforms_fastapi/utils/document_database.py b/libreforms_fastapi/utils/document_database.py index f1cd73d..33218b6 100644 --- a/libreforms_fastapi/utils/document_database.py +++ b/libreforms_fastapi/utils/document_database.py @@ -229,6 +229,7 @@ def _initialize_metadata_fields(self): # [self.created_by_field, self.last_editor_field, field_name, field_name] self.linked_to_form_field = "linked_form_fields" # [(field_name, form_name, [display_field, display_field])] + self.reviewer_comments_field = "reviewer_comments" return [ self.form_name_field, @@ -248,6 +249,7 @@ def _initialize_metadata_fields(self): self.journal_field, self.linked_to_user_field, self.linked_to_form_field, + self.reviewer_comments_field, ] @abstractmethod def _initialize_database_collections(self): @@ -732,6 +734,7 @@ def advance_document_stage( self.last_modified_field: current_timestamp.isoformat(), self.last_editor_field: metadata.get(self.last_editor_field, None), self.ip_address_field: metadata.get(self.ip_address_field, None), + self.reviewer_comments_field: metadata.get(self.reviewer_comments_field, ""), }, } @@ -808,6 +811,7 @@ def advance_document_stage( document['metadata'][self.last_modified_field] = current_timestamp.isoformat() document['metadata'][self.last_editor_field] = metadata.get(self.last_editor_field, None) document['metadata'][self.ip_address_field] = metadata.get(self.ip_address_field, None) + document['metadata'][self.reviewer_comments_field] = metadata.get(self.reviewer_comments_field, "") document['metadata'][self.journal_field] = journal # Set the form stage stored in the document's metadata to the