From 095e09ddc1240527919302e5c7c665a6cf9d4cfd Mon Sep 17 00:00:00 2001 From: divyav-aot Date: Mon, 6 Nov 2023 12:39:44 -0500 Subject: [PATCH 01/45] ci/cd updated for rook --- .github/workflows/api-cd.yml | 16 ++++++++++++++++ .github/workflows/dedupe-cd.yml | 18 ++++++++++++++++++ .github/workflows/fileconv-cd.yml | 18 ++++++++++++++++++ .github/workflows/pdfstitch-cd.yml | 18 ++++++++++++++++++ .github/workflows/pdfstitch-ci.yml | 2 ++ .github/workflows/web-cd.yml | 18 ++++++++++++++++++ .github/workflows/zippingservice-cd.yml | 18 ++++++++++++++++++ .github/workflows/zippingservice-ci.yml | 2 ++ 8 files changed, 110 insertions(+) diff --git a/.github/workflows/api-cd.yml b/.github/workflows/api-cd.yml index e9156da62..8751ae698 100644 --- a/.github/workflows/api-cd.yml +++ b/.github/workflows/api-cd.yml @@ -8,6 +8,8 @@ on: - main - dev-marshal - test-marshal + - dev-rook + - test-rook paths: - "api/**" - ".github/workflows/api-cd.yml" @@ -59,7 +61,21 @@ jobs: echo "TAG_NAME=test-marshal" >> $GITHUB_ENV echo "BRANCH_NAME=test-marshal" >> $GITHUB_ENV echo "ENV_NAME=test" >> $GITHUB_ENV + - name: Set ENV variables for dev-rook branch + if: ${{ github.ref_name == 'dev-rook' }} + run: | + echo "For ${{ github.ref_name }} branch" + echo "TAG_NAME=dev-rook" >> $GITHUB_ENV + echo "BRANCH_NAME=dev-rook" >> $GITHUB_ENV + echo "ENV_NAME=dev" >> $GITHUB_ENV + - name: Set ENV variables for test-rook branch + if: ${{ github.ref_name == 'test-rook' }} + run: | + echo "For ${{ github.ref_name }} branch" + echo "TAG_NAME=test-rook" >> $GITHUB_ENV + echo "BRANCH_NAME=test-rook" >> $GITHUB_ENV + echo "ENV_NAME=test" >> $GITHUB_ENV - name: Login Openshift shell: bash run: | diff --git a/.github/workflows/dedupe-cd.yml b/.github/workflows/dedupe-cd.yml index 25fd83304..c50d20c9e 100644 --- a/.github/workflows/dedupe-cd.yml +++ b/.github/workflows/dedupe-cd.yml @@ -8,6 +8,8 @@ on: - main - dev-marshal - test-marshal + - dev-rook + - test-rook paths: - "computingservices/DedupeServices/**" - ".github/workflows/dedupe-cd.yml" @@ -62,6 +64,22 @@ jobs: echo "BRANCH_NAME=test-marshal" >> $GITHUB_ENV echo "ENV_NAME=test" >> $GITHUB_ENV + - name: Set ENV variables for dev-rook branch + if: ${{ github.ref_name == 'dev-rook' }} + run: | + echo "For ${{ github.ref_name }} branch" + echo "TAG_NAME=dev-rook" >> $GITHUB_ENV + echo "BRANCH_NAME=dev-rook" >> $GITHUB_ENV + echo "ENV_NAME=dev" >> $GITHUB_ENV + + - name: Set ENV variables for test-rook branch + if: ${{ github.ref_name == 'test-rook' }} + run: | + echo "For ${{ github.ref_name }} branch" + echo "TAG_NAME=test-rook" >> $GITHUB_ENV + echo "BRANCH_NAME=test-rook" >> $GITHUB_ENV + echo "ENV_NAME=test" >> $GITHUB_ENV + - name: Login Openshift shell: bash run: | diff --git a/.github/workflows/fileconv-cd.yml b/.github/workflows/fileconv-cd.yml index ddc6bf5d1..7d8be08fe 100644 --- a/.github/workflows/fileconv-cd.yml +++ b/.github/workflows/fileconv-cd.yml @@ -8,6 +8,8 @@ on: - main - dev-marshal - test-marshal + - dev-rook + - test-rook paths: - "MCS.FOI.S3FileConversion/**" - ".github/workflows/fileconv-cd.yml" @@ -62,6 +64,22 @@ jobs: echo "BRANCH_NAME=test-marshal" >> $GITHUB_ENV echo "ENV_NAME=test" >> $GITHUB_ENV + - name: Set ENV variables for dev-rook branch + if: ${{ github.ref_name == 'dev-rook' }} + run: | + echo "For ${{ github.ref_name }} branch" + echo "TAG_NAME=dev-rook" >> $GITHUB_ENV + echo "BRANCH_NAME=dev-rook" >> $GITHUB_ENV + echo "ENV_NAME=dev" >> $GITHUB_ENV + + - name: Set ENV variables for test-rook branch + if: ${{ github.ref_name == 'test-rook' }} + run: | + echo "For ${{ github.ref_name }} branch" + echo "TAG_NAME=test-rook" >> $GITHUB_ENV + echo "BRANCH_NAME=test-rook" >> $GITHUB_ENV + echo "ENV_NAME=test" >> $GITHUB_ENV + - name: Login Openshift shell: bash run: | diff --git a/.github/workflows/pdfstitch-cd.yml b/.github/workflows/pdfstitch-cd.yml index 7440ddeac..beb93e291 100644 --- a/.github/workflows/pdfstitch-cd.yml +++ b/.github/workflows/pdfstitch-cd.yml @@ -8,6 +8,8 @@ on: - main - dev-marshal - test-marshal + - dev-rook + - test-rook paths: - "computingservices/PDFStitchServices/**" - ".github/workflows/pdfstitch-cd.yml" @@ -61,6 +63,22 @@ jobs: echo "TAG_NAME=test-marshal" >> $GITHUB_ENV echo "BRANCH_NAME=test-marshal" >> $GITHUB_ENV echo "ENV_NAME=test" >> $GITHUB_ENV + + - name: Set ENV variables for dev-rook branch + if: ${{ github.ref_name == 'dev-rook' }} + run: | + echo "For ${{ github.ref_name }} branch" + echo "TAG_NAME=dev-rook" >> $GITHUB_ENV + echo "BRANCH_NAME=dev-rook" >> $GITHUB_ENV + echo "ENV_NAME=dev" >> $GITHUB_ENV + + - name: Set ENV variables for test-rook branch + if: ${{ github.ref_name == 'test-rook' }} + run: | + echo "For ${{ github.ref_name }} branch" + echo "TAG_NAME=test-rook" >> $GITHUB_ENV + echo "BRANCH_NAME=test-rook" >> $GITHUB_ENV + echo "ENV_NAME=test" >> $GITHUB_ENV - name: Login Openshift shell: bash diff --git a/.github/workflows/pdfstitch-ci.yml b/.github/workflows/pdfstitch-ci.yml index 0505fac61..f69f8fdc4 100644 --- a/.github/workflows/pdfstitch-ci.yml +++ b/.github/workflows/pdfstitch-ci.yml @@ -8,6 +8,8 @@ on: - dev - dev-marshal - test-marshal + - dev-rook + - test-rook paths: - "computingservices/PDFStitchServices/**" diff --git a/.github/workflows/web-cd.yml b/.github/workflows/web-cd.yml index 30f5404b3..1702bfc83 100644 --- a/.github/workflows/web-cd.yml +++ b/.github/workflows/web-cd.yml @@ -8,6 +8,8 @@ on: - main - dev-marshal - test-marshal + - dev-rook + - test-rook paths: - "web/**" - ".github/workflows/web-cd.yml" @@ -61,6 +63,22 @@ jobs: echo "TAG_NAME=test-marshal" >> $GITHUB_ENV echo "BRANCH_NAME=test-marshal" >> $GITHUB_ENV echo "ENV_NAME=test" >> $GITHUB_ENV + + - name: Set ENV variables for dev-rook branch + if: ${{ github.ref_name == 'dev-rook' }} + run: | + echo "For ${{ github.ref_name }} branch" + echo "TAG_NAME=dev-rook" >> $GITHUB_ENV + echo "BRANCH_NAME=dev-rook" >> $GITHUB_ENV + echo "ENV_NAME=dev" >> $GITHUB_ENV + + - name: Set ENV variables for test-rook branch + if: ${{ github.ref_name == 'test-rook' }} + run: | + echo "For ${{ github.ref_name }} branch" + echo "TAG_NAME=test-rook" >> $GITHUB_ENV + echo "BRANCH_NAME=test-rook" >> $GITHUB_ENV + echo "ENV_NAME=test" >> $GITHUB_ENV - name: Login Openshift shell: bash diff --git a/.github/workflows/zippingservice-cd.yml b/.github/workflows/zippingservice-cd.yml index 1ae64b9bf..80d1f6411 100644 --- a/.github/workflows/zippingservice-cd.yml +++ b/.github/workflows/zippingservice-cd.yml @@ -8,6 +8,8 @@ on: - main - dev-marshal - test-marshal + - dev-rook + - test-rook paths: - "computingservices/ZippingServices/**" - ".github/workflows/zippingservice-cd.yml" @@ -61,6 +63,22 @@ jobs: echo "TAG_NAME=test-marshal" >> $GITHUB_ENV echo "BRANCH_NAME=test-marshal" >> $GITHUB_ENV echo "ENV_NAME=test" >> $GITHUB_ENV + + - name: Set ENV variables for dev-rook branch + if: ${{ github.ref_name == 'dev-rook' }} + run: | + echo "For ${{ github.ref_name }} branch" + echo "TAG_NAME=dev-rook" >> $GITHUB_ENV + echo "BRANCH_NAME=dev-rook" >> $GITHUB_ENV + echo "ENV_NAME=dev" >> $GITHUB_ENV + + - name: Set ENV variables for test-rook branch + if: ${{ github.ref_name == 'test-rook' }} + run: | + echo "For ${{ github.ref_name }} branch" + echo "TAG_NAME=test-rook" >> $GITHUB_ENV + echo "BRANCH_NAME=test-rook" >> $GITHUB_ENV + echo "ENV_NAME=test" >> $GITHUB_ENV - name: Login Openshift shell: bash diff --git a/.github/workflows/zippingservice-ci.yml b/.github/workflows/zippingservice-ci.yml index bcb67090a..2296be5ee 100644 --- a/.github/workflows/zippingservice-ci.yml +++ b/.github/workflows/zippingservice-ci.yml @@ -8,6 +8,8 @@ on: - dev - dev-marshal - test-marshal + - dev-rook + - test-rook paths: - "computingservices/ZippingServices/**" From adf8f96474916700e1fbfdb814aafa383d011863 Mon Sep 17 00:00:00 2001 From: Aman-Hundal Date: Mon, 4 Dec 2023 16:40:02 -0800 Subject: [PATCH 02/45] Added isoipcereviewlayer attribute to document object sent to FE to get document end point --- api/reviewer_api/resources/document.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/api/reviewer_api/resources/document.py b/api/reviewer_api/resources/document.py index e398d6bc8..885d886fd 100644 --- a/api/reviewer_api/resources/document.py +++ b/api/reviewer_api/resources/document.py @@ -98,9 +98,15 @@ def get(requestid): response.raise_for_status() # get request status jsonobj = response.json() + #OIPC Layer Validation + if (jsonobj['isoipcreview'] == True and any(oipc['reasonid'] == 2 for oipc in jsonobj['oipcdetails'])): + isoipcreviewlayer = True + else: + isoipcreviewlayer = False requestinfo = { "bcgovcode": jsonobj["bcgovcode"], - "requesttype": jsonobj["requestType"] + "requesttype": jsonobj["requestType"], + "isoipcreviewlayer": isoipcreviewlayer, } result = documentservice().getdocuments(requestid, requestinfo["bcgovcode"]) return json.dumps({"requeststatusid": jsonobj["requeststatusid"], "documents": result, "requestnumber":jsonobj["axisRequestId"], "requestinfo":requestinfo}), 200 From 52121f15c2ea225258401e493ed09d5216703162 Mon Sep 17 00:00:00 2001 From: Aman-Hundal Date: Mon, 4 Dec 2023 17:14:03 -0800 Subject: [PATCH 03/45] FE integration for oipcreview layer check completed. Ticket completed: --- web/src/components/FOI/Home/DocumentSelector.tsx | 2 +- web/src/components/FOI/Home/LayerDropdown.tsx | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/web/src/components/FOI/Home/DocumentSelector.tsx b/web/src/components/FOI/Home/DocumentSelector.tsx index 00f340a8c..7f2580eb2 100644 --- a/web/src/components/FOI/Home/DocumentSelector.tsx +++ b/web/src/components/FOI/Home/DocumentSelector.tsx @@ -708,7 +708,7 @@ const DocumentSelector = ({ Redaction Layer:
- +

diff --git a/web/src/components/FOI/Home/LayerDropdown.tsx b/web/src/components/FOI/Home/LayerDropdown.tsx index e92c0e13f..0b2814ba5 100644 --- a/web/src/components/FOI/Home/LayerDropdown.tsx +++ b/web/src/components/FOI/Home/LayerDropdown.tsx @@ -15,7 +15,8 @@ import CloseIcon from '@mui/icons-material/Close'; import IconButton from "@mui/material/IconButton"; const LayerDropdown = ({ - ministryrequestid + ministryrequestid, + isoipcreviewlayer, }: any) => { const layers = useAppSelector((state: any) => state.documents?.redactionLayers); @@ -61,7 +62,16 @@ const LayerDropdown = ({ variant="outlined" > {layers.map((option: any) => ( - + option.redactionlayerid === 3 ? + + { + option.count === 0 + && option.redactionlayerid !== layer && + + } + {option.description} + + : { option.redactionlayerid > 2 && option.count === 0 From e0f868465ecb1bc94a0d7a4963be2db662b873aa Mon Sep 17 00:00:00 2001 From: Aman-Hundal Date: Mon, 4 Dec 2023 17:18:30 -0800 Subject: [PATCH 04/45] Refactor of FE conditional check for isoipcreview layer --- web/src/components/FOI/Home/LayerDropdown.tsx | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/web/src/components/FOI/Home/LayerDropdown.tsx b/web/src/components/FOI/Home/LayerDropdown.tsx index 0b2814ba5..7dd569997 100644 --- a/web/src/components/FOI/Home/LayerDropdown.tsx +++ b/web/src/components/FOI/Home/LayerDropdown.tsx @@ -49,6 +49,8 @@ const LayerDropdown = ({ setLayer(currentLayer.redactionlayerid) } + console.log(layers) + return ( <> {layers.map((option: any) => ( - option.redactionlayerid === 3 ? - - { - option.count === 0 - && option.redactionlayerid !== layer && - - } - {option.description} - - : + { option.redactionlayerid > 2 && option.count === 0 From f08ee335c4f6be69541c6760f8f5b218c6fce719 Mon Sep 17 00:00:00 2001 From: "sumathi.thirumani" Date: Fri, 8 Dec 2023 00:48:51 -0800 Subject: [PATCH 05/45] Changes to copy redactions from redline to oipc. --- api/migrations/versions/549893ea9059_oipc.py | 43 +++++++++++++++++++ api/reviewer_api/models/AnnotationSections.py | 27 ++++++++++++ api/reviewer_api/models/Annotations.py | 24 +++++++++++ api/reviewer_api/models/Documents.py | 23 ++++++++++ api/reviewer_api/models/RedactionLayers.py | 4 +- api/reviewer_api/resources/redaction.py | 18 ++++++++ .../services/annotationservice.py | 13 ++++++ api/reviewer_api/services/radactionservice.py | 8 ++++ 8 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 api/migrations/versions/549893ea9059_oipc.py diff --git a/api/migrations/versions/549893ea9059_oipc.py b/api/migrations/versions/549893ea9059_oipc.py new file mode 100644 index 000000000..a80dfda67 --- /dev/null +++ b/api/migrations/versions/549893ea9059_oipc.py @@ -0,0 +1,43 @@ +"""empty message + +Revision ID: 549893ea9059 +Revises: 2045c976ea60 +Create Date: 2023-11-23 22:53:32.071886 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '549893ea9059' +down_revision = '2045c976ea60' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('AnnotationSections', sa.Column('redactionlayerid', sa.Integer, nullable=False, server_default='1')) + op.create_foreign_key(None, 'AnnotationSections', 'RedactionLayers', ['redactionlayerid'], ['redactionlayerid']) + op.execute( + 'ALTER TABLE public."AnnotationSections" DROP CONSTRAINT "annotationsection_version_key";' + ) + op.execute( + 'ALTER TABLE public."AnnotationSections" ADD CONSTRAINT annotationsection_version_redactionlayerid_key UNIQUE (annotationname, version, redactionlayerid);' + ) + op.execute('Update public."AnnotationSections" set redactionlayerid = 1 where redactionlayerid is null ') + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('AnnotationSections', 'redactionlayerid') + op.execute( + 'ALTER TABLE public."AnnotationSections" DROP CONSTRAINT "annotationsection_version_redactionlayerid_key";' + ) + op.execute( + 'ALTER TABLE public."AnnotationSections" ADD CONSTRAINT annotationsection_version_key UNIQUE (annotationname, version);' + ) + # ### end Alembic commands ### diff --git a/api/reviewer_api/models/AnnotationSections.py b/api/reviewer_api/models/AnnotationSections.py index c7d56bf3a..4848c850e 100644 --- a/api/reviewer_api/models/AnnotationSections.py +++ b/api/reviewer_api/models/AnnotationSections.py @@ -11,6 +11,7 @@ from sqlalchemy import text from reviewer_api.utils.util import split, getbatchconfig import json +from sqlalchemy.orm import relationship, backref, aliased class AnnotationSection(db.Model): @@ -27,6 +28,9 @@ class AnnotationSection(db.Model): isactive = db.Column(db.Boolean, unique=False, nullable=False) annotationname = db.Column(db.Integer, ForeignKey("Annotations.annotationname")) + redactionlayerid = db.Column(db.Integer, db.ForeignKey("RedactionLayers.redactionlayerid") + ) + @classmethod def __getsectionkey(cls, _annotationname): try: @@ -62,6 +66,29 @@ def __getbulksectionkey(cls, _annotationnames): db.session.close() return apks + @classmethod + def copyannotationsections(cls, ministryrequestid, sourcelayers, targetlayer) -> DefaultMethodResult: + try: + sql = """ + insert into "AnnotationSections" (annotationname, foiministryrequestid, "section", created_at, + createdby, updated_at, updatedby, redactionlayerid, "version", isactive) + select annotationname, foiministryrequestid, "section", created_at, + createdby, updated_at, updatedby, :targetlayer, 1, isactive from "AnnotationSections" a + where annotationname not in (select annotationname from "AnnotationSections" + where foiministryrequestid= :ministryrequestid and isactive = true and a.redactionlayerid = :targetlayer) + and foiministryrequestid = :ministryrequestid and isactive =true and redactionlayerid in :sourcelayers; + """ + db.session.execute( + text(sql), + {"ministryrequestid": ministryrequestid, "sourcelayers": tuple(sourcelayers), "targetlayer": targetlayer}, + ) + db.session.commit() + return DefaultMethodResult(True, "Annotation sections are copied to layer", targetlayer) + except Exception as ex: + logging.error(ex) + finally: + db.session.close() + @classmethod def savesections( cls, annots, _foiministryrequestid, userinfo diff --git a/api/reviewer_api/models/Annotations.py b/api/reviewer_api/models/Annotations.py index 567bcea55..8f25ca52b 100644 --- a/api/reviewer_api/models/Annotations.py +++ b/api/reviewer_api/models/Annotations.py @@ -309,6 +309,30 @@ def saveannotations(cls, annots, redactionlayerid, userinfo) -> DefaultMethodRes else: return DefaultMethodResult(False, "Invalid Annotation Request", -1) + @classmethod + def copyannotations(cls, documentids, sourceredactionlayers, targetlayer) -> DefaultMethodResult: + try: + sql = """ + insert into "Annotations" (annotationname, documentid , documentversion, + annotation, pagenumber, isactive, createdby, created_at, updatedby, updated_at, + redactionlayerid, "version") + select annotationname, documentid , documentversion, + annotation, pagenumber, isactive, createdby, created_at, updatedby, updated_at, + :targetlayer, 1 from "Annotations" a + where redactionlayerid in :sourceredactionlayers and isactive = true and documentid in :documentids; + + """ + db.session.execute( + text(sql), + {"documentids": tuple(documentids), "sourceredactionlayers": tuple(sourceredactionlayers), "targetlayer": targetlayer}, + ) + db.session.commit() + return DefaultMethodResult(True, "Annotations are copied to layer", targetlayer) + except Exception as ex: + logging.error(ex) + finally: + db.session.close() + @classmethod def __chunksaveannotations( cls, annots, redactionlayerid, userinfo diff --git a/api/reviewer_api/models/Documents.py b/api/reviewer_api/models/Documents.py index aeed7d35f..d5d6c31a3 100644 --- a/api/reviewer_api/models/Documents.py +++ b/api/reviewer_api/models/Documents.py @@ -15,6 +15,7 @@ from reviewer_api.utils.util import pstformat import json import logging +from sqlalchemy import or_, and_, text class Document(db.Model): __tablename__ = 'Documents' @@ -123,6 +124,28 @@ def getdocument(cls, documentid): finally: db.session.close() + @classmethod + def getdocumentidsbyrequest(cls, ministryrequestid): + docids = [] + try: + sql = """ + select distinct on (docs.documentid) docs.documentid + from "Documents" docs + where docs.foiministryrequestid = :ministryrequestid + order by docs.documentid, docs.version desc + """ + rs = db.session.execute( + text(sql), + {"ministryrequestid": ministryrequestid}, + ) + for row in rs: + docids.append(row['documentid']) + return docids + except Exception as ex: + logging.error(ex) + finally: + db.session.close() + @classmethod def getdocumentsbyids(cls, idlist): try: diff --git a/api/reviewer_api/models/RedactionLayers.py b/api/reviewer_api/models/RedactionLayers.py index 31d2fdf0d..827bcadbb 100644 --- a/api/reviewer_api/models/RedactionLayers.py +++ b/api/reviewer_api/models/RedactionLayers.py @@ -52,14 +52,14 @@ def getall(cls, ministryrequestid): @classmethod def getredlineredactionlayer(cls): try: - pageflag_schema = RedactionLayerSchema(many=False) + layer_schema = RedactionLayerSchema(many=False) query = ( db.session.query(RedactionLayer) .filter_by(isactive=True, name="Redline") .order_by(RedactionLayer.sortorder.desc()) .first() ) - return pageflag_schema.dump(query) + return layer_schema.dump(query) except Exception as ex: logging.error(ex) finally: diff --git a/api/reviewer_api/resources/redaction.py b/api/reviewer_api/resources/redaction.py index c30d8cba2..caf2b4e90 100644 --- a/api/reviewer_api/resources/redaction.py +++ b/api/reviewer_api/resources/redaction.py @@ -240,6 +240,24 @@ def get(ministryrequestid): except BusinessException as exception: return {"status": exception.status_code, "message": exception.message}, 500 +@cors_preflight("POST,OPTIONS") +@API.route("/annotation//copy/") +class DeactivateRedactions(Resource): + + """save or update an annotation for a document""" + + @staticmethod + @TRACER.trace() + @cross_origin(origins=allowedorigins()) + @auth.require + def post(ministryrequestid, targetlayer): + try: + result = redactionservice().copyannotation(ministryrequestid, targetlayer) + return {'status': result.success, 'message': result.message }, 200 + except BusinessException as exception: + return {'status': False, 'message': exception.message }, 500 + + @cors_preflight("GET,OPTIONS") @API.route("/redactedsections/ministryrequest/") diff --git a/api/reviewer_api/services/annotationservice.py b/api/reviewer_api/services/annotationservice.py index c16bc9712..39974c2cf 100644 --- a/api/reviewer_api/services/annotationservice.py +++ b/api/reviewer_api/services/annotationservice.py @@ -1,5 +1,6 @@ from os import stat from re import VERBOSE +from reviewer_api.models.Documents import Document from reviewer_api.models.Annotations import Annotation from reviewer_api.models.AnnotationSections import AnnotationSection from reviewer_api.schemas.annotationrequest import SectionAnnotationSchema @@ -141,6 +142,18 @@ def getannotationsections(self, ministryid): annotationsections = AnnotationSection.get_by_ministryid(ministryid) return annotationsections + def copyannotation(self, ministryrequestid, sourcelayers, targetlayer): + documentids = Document.getdocumentidsbyrequest(ministryrequestid) + print(ministryrequestid) + print(sourcelayers) + print(targetlayer) + print(documentids) + annotresponse = Annotation.copyannotations(documentids, sourcelayers, targetlayer) + if annotresponse.success == True: + AnnotationSection.copyannotationsections(ministryrequestid, sourcelayers, targetlayer) + return DefaultMethodResult(True, "Copied Annotations", ministryrequestid) + + def saveannotation(self, annotationschema, userinfo): annots = self.__extractannotfromxml(annotationschema["xml"]) _redactionlayerid = self.__getredactionlayerid(annotationschema) diff --git a/api/reviewer_api/services/radactionservice.py b/api/reviewer_api/services/radactionservice.py index f6030e988..f29444f40 100644 --- a/api/reviewer_api/services/radactionservice.py +++ b/api/reviewer_api/services/radactionservice.py @@ -57,6 +57,14 @@ def getannotationinfo(self, documentid, documentversion, pagenumber): def getannotationinfobyrequest(self, requestid): return annotationservice().getrequestannotationinfo(requestid) + def copyannotation(self, ministryrequestid, targetlayer): + sourcelayers = [] + sourcelayers.append(redactionlayerservice().getdefaultredactionlayerid()) + print(ministryrequestid) + print(sourcelayers) + print(targetlayer) + return annotationservice().copyannotation(ministryrequestid, sourcelayers, targetlayer) + def saveannotation(self, annotationschema, userinfo): result = annotationservice().saveannotation(annotationschema, userinfo) if ( From e7351c6a6e71a2b1baa5e0933ec19f8948c60610 Mon Sep 17 00:00:00 2001 From: Aman-Hundal Date: Thu, 14 Dec 2023 16:48:01 -0800 Subject: [PATCH 06/45] Adjusted oipc layer validation logic in backend to incl. request state has to be closed and a response package for the cuurent version of the request exists. REFACTOR + TESTING WIP --- api/reviewer_api/resources/document.py | 25 ++++++++++++++++--- .../components/FOI/Home/DocumentSelector.tsx | 2 +- web/src/components/FOI/Home/LayerDropdown.tsx | 6 ++--- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/api/reviewer_api/resources/document.py b/api/reviewer_api/resources/document.py index 885d886fd..b8099a3df 100644 --- a/api/reviewer_api/resources/document.py +++ b/api/reviewer_api/resources/document.py @@ -23,6 +23,7 @@ from reviewer_api.utils.util import cors_preflight, allowedorigins, getrequiredmemberships from reviewer_api.exceptions import BusinessException from reviewer_api.schemas.document import FOIRequestDeleteRecordsSchema, FOIRequestUpdateRecordsSchema +from reviewer_api.services.pdfstitchpackageservice import pdfstitchpackageservice import json import requests import logging @@ -98,15 +99,31 @@ def get(requestid): response.raise_for_status() # get request status jsonobj = response.json() + + print(jsonobj) #OIPC Layer Validation - if (jsonobj['isoipcreview'] == True and any(oipc['reasonid'] == 2 for oipc in jsonobj['oipcdetails'])): - isoipcreviewlayer = True + if (jsonobj['isoipcreview'] == True and any(oipc['reasonid'] == 2 for oipc in jsonobj['oipcdetails']) and jsonobj['currentState'] == "Closed" and any(state['status'] == "Response" for state in jsonobj['stateTransition'])): + #look at state transisont array and verify if respone exists and get latest response created_at AND check if response package exists in the pdfstitchpackge table + #latest package created_at > latest response created_at (USE THE MODEL ATTRIBUTE THAT GIVES LATEST STATES AND ADD A NEW COLM FOR CREATED_AT) (found in state transiiton) + #logic -> you cant only create rresponse package when state is in respones. First change state to response (created at) after that they create a redline and dl the response package at a later date. this logic bakes in the fact that response and response packages can occur throughout and change states at many times + latest_response_package = pdfstitchpackageservice().getpdfstitchpackage(requestid, 'responsepackage') + for state in jsonobj['stateTransition']: + if (state['status'] == "Response"): + latest_response_state = state + break + print(latest_response_state) + print(latest_response_package) + if (bool(latest_response_package) == True and latest_response_package['createdat'] > latest_response_state['created_at']): + validoipcreviewlayer = True + else: + validoipcreviewlayer = False else: - isoipcreviewlayer = False + validoipcreviewlayer = False + requestinfo = { "bcgovcode": jsonobj["bcgovcode"], "requesttype": jsonobj["requestType"], - "isoipcreviewlayer": isoipcreviewlayer, + "validoipcreviewlayer": validoipcreviewlayer, } result = documentservice().getdocuments(requestid, requestinfo["bcgovcode"]) return json.dumps({"requeststatusid": jsonobj["requeststatusid"], "documents": result, "requestnumber":jsonobj["axisRequestId"], "requestinfo":requestinfo}), 200 diff --git a/web/src/components/FOI/Home/DocumentSelector.tsx b/web/src/components/FOI/Home/DocumentSelector.tsx index 7f2580eb2..96515e6e6 100644 --- a/web/src/components/FOI/Home/DocumentSelector.tsx +++ b/web/src/components/FOI/Home/DocumentSelector.tsx @@ -708,7 +708,7 @@ const DocumentSelector = ({ Redaction Layer:
- +

diff --git a/web/src/components/FOI/Home/LayerDropdown.tsx b/web/src/components/FOI/Home/LayerDropdown.tsx index 7dd569997..1c0114980 100644 --- a/web/src/components/FOI/Home/LayerDropdown.tsx +++ b/web/src/components/FOI/Home/LayerDropdown.tsx @@ -16,7 +16,7 @@ import IconButton from "@mui/material/IconButton"; const LayerDropdown = ({ ministryrequestid, - isoipcreviewlayer, + validoipcreviewlayer, }: any) => { const layers = useAppSelector((state: any) => state.documents?.redactionLayers); @@ -49,8 +49,6 @@ const LayerDropdown = ({ setLayer(currentLayer.redactionlayerid) } - console.log(layers) - return ( <> {layers.map((option: any) => ( - + { option.redactionlayerid > 2 && option.count === 0 From f756358cf4e80ac77e09dae170ddb6357288c4ab Mon Sep 17 00:00:00 2001 From: Aman-Hundal Date: Thu, 14 Dec 2023 16:59:07 -0800 Subject: [PATCH 07/45] small refactor. wIP testing + code clean --- api/reviewer_api/resources/document.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/api/reviewer_api/resources/document.py b/api/reviewer_api/resources/document.py index b8099a3df..a8b2831ad 100644 --- a/api/reviewer_api/resources/document.py +++ b/api/reviewer_api/resources/document.py @@ -99,26 +99,23 @@ def get(requestid): response.raise_for_status() # get request status jsonobj = response.json() - print(jsonobj) + #OIPC Layer Validation + #look at state transisont array and verify if respone exists and get latest response created_at AND check if response package exists in the pdfstitchpackge table + #latest package created_at > latest response created_at (USE THE MODEL ATTRIBUTE THAT GIVES LATEST STATES AND ADD A NEW COLM FOR CREATED_AT) (found in state transiiton) + #logic -> you cant only create rresponse package when state is in respones. First change state to response (created at) after that they create a redline and dl the response package at a later date. this logic bakes in the fact that response and response packages can occur throughout and change states at many times + validoipcreviewlayer = False if (jsonobj['isoipcreview'] == True and any(oipc['reasonid'] == 2 for oipc in jsonobj['oipcdetails']) and jsonobj['currentState'] == "Closed" and any(state['status'] == "Response" for state in jsonobj['stateTransition'])): - #look at state transisont array and verify if respone exists and get latest response created_at AND check if response package exists in the pdfstitchpackge table - #latest package created_at > latest response created_at (USE THE MODEL ATTRIBUTE THAT GIVES LATEST STATES AND ADD A NEW COLM FOR CREATED_AT) (found in state transiiton) - #logic -> you cant only create rresponse package when state is in respones. First change state to response (created at) after that they create a redline and dl the response package at a later date. this logic bakes in the fact that response and response packages can occur throughout and change states at many times - latest_response_package = pdfstitchpackageservice().getpdfstitchpackage(requestid, 'responsepackage') for state in jsonobj['stateTransition']: if (state['status'] == "Response"): latest_response_state = state break + latest_response_package = pdfstitchpackageservice().getpdfstitchpackage(requestid, 'responsepackage') print(latest_response_state) print(latest_response_package) if (bool(latest_response_package) == True and latest_response_package['createdat'] > latest_response_state['created_at']): validoipcreviewlayer = True - else: - validoipcreviewlayer = False - else: - validoipcreviewlayer = False requestinfo = { "bcgovcode": jsonobj["bcgovcode"], From 27a3d9d170a177d864794838330f4b7c1e8b0470 Mon Sep 17 00:00:00 2001 From: Milos Despotovic Date: Fri, 15 Dec 2023 10:26:00 -0800 Subject: [PATCH 08/45] Add oipc layer to api --- api/reviewer_api/resources/foiflowmasterdata.py | 7 ++++--- api/reviewer_api/services/redactionlayerservice.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/api/reviewer_api/resources/foiflowmasterdata.py b/api/reviewer_api/resources/foiflowmasterdata.py index bacbbb8d6..ef6df1782 100644 --- a/api/reviewer_api/resources/foiflowmasterdata.py +++ b/api/reviewer_api/resources/foiflowmasterdata.py @@ -257,13 +257,14 @@ def post(ministryrequestid): @cors_preflight("POST,OPTIONS") @API.route("/foiflow/oss/presigned/redline/") +@API.route("/foiflow/oss/presigned/redline//") class FOIFlowS3PresignedRedline(Resource): @staticmethod @TRACER.trace() @cross_origin(origins=allowedorigins()) @auth.require @auth.ismemberofgroups(getrequiredmemberships()) - def post(ministryrequestid): + def post(ministryrequestid, redlinetype="redline"): try: data = request.get_json() documentmapper = redactionservice().getdocumentmapper( @@ -293,8 +294,8 @@ def post(ministryrequestid): if is_single_redline_package(_bcgovcode) == False: division_name = div["divisionname"] # generate save url for stitched file - filepath_put = "{0}/redline/{1}/{0} - Redline - {1}.pdf".format( - filepathlist[0], division_name + filepath_put = "{0}/{2}/{1}/{0} - {2} - {1}.pdf".format( + filepathlist[0], division_name, redlinetype ) # filename_put, file_extension_put = os.path.splitext(filepath_put) diff --git a/api/reviewer_api/services/redactionlayerservice.py b/api/reviewer_api/services/redactionlayerservice.py index 6f6cb3fd6..07ff685b5 100644 --- a/api/reviewer_api/services/redactionlayerservice.py +++ b/api/reviewer_api/services/redactionlayerservice.py @@ -15,7 +15,7 @@ def getdefaultredactionlayerid(self): def getmappedredactionlayers(self, redactionlayer): mpxlayers = [] defaultlayerid = self.getdefaultredactionlayerid() - if redactionlayer["redactionlayerid"] != defaultlayerid: + if redactionlayer["redactionlayerid"] != defaultlayerid and redactionlayer["redactionlayerid"] != 3: mpxlayers.append(defaultlayerid) mpxlayers.append(redactionlayer["redactionlayerid"]) return mpxlayers From afa19ad5d8768da2d41b3f0464237be64c5edc8a Mon Sep 17 00:00:00 2001 From: Milos Despotovic Date: Fri, 15 Dec 2023 10:37:27 -0800 Subject: [PATCH 09/45] Update Redline for oipc layer --- .../services/docReviewerService.tsx | 27 +++++ web/src/apiManager/services/foiOSSService.tsx | 9 +- web/src/components/FOI/Home/LayerDropdown.tsx | 4 + web/src/components/FOI/Home/Redlining.js | 104 +++++++++++++++++- 4 files changed, 137 insertions(+), 7 deletions(-) diff --git a/web/src/apiManager/services/docReviewerService.tsx b/web/src/apiManager/services/docReviewerService.tsx index 166f74681..a07730617 100644 --- a/web/src/apiManager/services/docReviewerService.tsx +++ b/web/src/apiManager/services/docReviewerService.tsx @@ -171,6 +171,33 @@ httpPOSTRequest({url: apiUrlPost, data: data, token: UserService.getToken() ?? ' }); }; +export const createOipcLayer= ( + requestid: string, + callback?: any, + errorCallback?: any, +) => { + const oipcLayerId = 3; + let apiUrlPost: string = `${API.DOCREVIEWER_ANNOTATION}/${requestid}/copy/${oipcLayerId}`; + let requestJSON = {}; + httpPOSTRequest({url: apiUrlPost, data: requestJSON, token: UserService.getToken() ?? '', isBearer: true}) + .then((res:any) => { + if (res.data) { + const redactionlayerid = 3 // oipc layer id + store.dispatch(incrementLayerCount(3) as any); + if (callback) callback(res.data); + } else { + throw new Error(`Error while copying annotations to OIPC layer`); + } + }) + .catch((error:any) => { + if (errorCallback) { + errorCallback("Error while copying annotations to OIPC layer"); + } else { + throw new Error(`Error while copying annotations to OIPC layer`); + } + }); +} + export const deleteRedaction = ( requestid: string, redactionlayerid: number, diff --git a/web/src/apiManager/services/foiOSSService.tsx b/web/src/apiManager/services/foiOSSService.tsx index 2e868f238..b11d537b3 100644 --- a/web/src/apiManager/services/foiOSSService.tsx +++ b/web/src/apiManager/services/foiOSSService.tsx @@ -48,9 +48,14 @@ export const getFOIS3DocumentRedlinePreSignedUrl = ( ministryrequestID: any, documentList: any[], callback: any, - errorCallback: any + errorCallback: any, + type: any = "redline" ) => { - const apiurl = API.FOI_GET_S3DOCUMENT_PRESIGNEDURL_REDLINE + "/" + ministryrequestID; + let apiurl; + apiurl = API.FOI_GET_S3DOCUMENT_PRESIGNEDURL_REDLINE + "/" + ministryrequestID; + if (type === "oipc") { + apiurl = apiurl + "/oipc" + } httpPOSTRequest({url: apiurl, data: {"divdocumentList":documentList}, token: UserService.getToken() || ''}) .then((res:any) => { diff --git a/web/src/components/FOI/Home/LayerDropdown.tsx b/web/src/components/FOI/Home/LayerDropdown.tsx index e92c0e13f..f0f80ed5c 100644 --- a/web/src/components/FOI/Home/LayerDropdown.tsx +++ b/web/src/components/FOI/Home/LayerDropdown.tsx @@ -13,6 +13,7 @@ import DialogContentText from '@mui/material/DialogContentText'; import DialogTitle from '@mui/material/DialogTitle'; import CloseIcon from '@mui/icons-material/Close'; import IconButton from "@mui/material/IconButton"; +import { createOipcLayer } from "../../../apiManager/services/docReviewerService"; const LayerDropdown = ({ ministryrequestid @@ -41,6 +42,9 @@ const LayerDropdown = ({ const handleModalContinue = (e: any) => { setOpenModal(false); store.dispatch(setCurrentLayer(layers.find((l: any) => l.redactionlayerid === layer))); + if (layers.find((l: any) => l.redactionlayerid === layer).redactionlayerid === 3) { + createOipcLayer(ministryrequestid); + } } const handleModalCancel = (e: any) => { diff --git a/web/src/components/FOI/Home/Redlining.js b/web/src/components/FOI/Home/Redlining.js index 6ade44fa3..31d5adea6 100644 --- a/web/src/components/FOI/Home/Redlining.js +++ b/web/src/components/FOI/Home/Redlining.js @@ -101,6 +101,7 @@ const Redlining = React.forwardRef( ); const sections = useSelector((state) => state.documents?.sections); const currentLayer = useSelector((state) => state.documents?.currentLayer); + const redactionLayers = useAppSelector((state) => state.documents?.redactionLayers); const viewer = useRef(null); const documentList = useAppSelector( @@ -302,6 +303,37 @@ const Redlining = React.forwardRef( menu.classList.add("FlyoutMenu"); menu.id = "saving_menu"; + const redlineForOipcBtn = document.createElement("button"); + redlineForOipcBtn.textContent = "Redline for OIPC"; + redlineForOipcBtn.id = "redline_for_oipc"; + redlineForOipcBtn.className = "redline_for_oipc"; + redlineForOipcBtn.style.backgroundColor = "transparent"; + redlineForOipcBtn.style.border = "none"; + redlineForOipcBtn.style.padding = "8px 8px 8px 10px"; + redlineForOipcBtn.style.cursor = "pointer"; + redlineForOipcBtn.style.alignItems = "left"; + //TODO - adjust this + redlineForOipcBtn.disabled = false; + + redlineForOipcBtn.onclick = () => { + // Save to s3 + setModalFor("oipc"); + setModalTitle("Redline for OIPC"); + setModalMessage([ + "Are you sure want to create the redline PDF for OIPC review?", +
, +
, + + When you create the redline PDF, your web browser page will + automatically refresh + , + ]); + setModalButtonLabel("Create OIPC Redline PDF"); + setRedlineModalOpen(true); + }; + + menu.appendChild(redlineForOipcBtn); + const redlineForSignOffBtn = document.createElement("button"); redlineForSignOffBtn.textContent = "Redline for Sign Off"; redlineForSignOffBtn.id = "redline_for_sign_off"; @@ -634,7 +666,13 @@ const Redlining = React.forwardRef( setMerge(true); setFetchAnnotResponse(data); } else { - annotManager.disableReadOnlyMode(); + const oipcLayer = redactionLayers.find((l) => l.redactionlayerid === 3) + //Set to read only if oipc layer exists + if (oipcLayer && oipcLayer.count > 0 && currentLayer.name.toLowerCase() !== "oipc") { + annotManager.enableReadOnlyMode(); + } else { + annotManager.disableReadOnlyMode(); + } docInstance?.UI.setToolbarGroup("toolbarGroup-Redact"); const existingAnnotations = annotManager.getAnnotationsList(); await annotManager.deleteAnnotations(existingAnnotations, { @@ -700,6 +738,12 @@ const Redlining = React.forwardRef( // If the event is triggered by importing then it can be ignored // This will happen when importing the initial annotations // from the server or individual changes from other users + + // Disable creating annotations in redline layer if OIPC layer is present + const oipcLayer = redactionLayers.find((l) => l.redactionlayerid === 3) + if (oipcLayer && oipcLayer.count > 0 && currentLayer.name.toLowerCase() === "redline") { + return; + } if (info.source !== "redactionApplied" && info.source !== "cancelRedaction") { //ignore annots/redact changes made by applyRedaction @@ -2291,6 +2335,25 @@ const Redlining = React.forwardRef( } }; + const fetchDocumentOipcRedlineAnnotations = async (requestid, documentids) => { + let documentOipcRedlineAnnotations = {}; + let docCounter = 0; + for (let documentid of documentids) { + fetchDocumentAnnotations( + requestid, + "oipc", + documentid, + async (data) => { + docCounter++; + documentOipcRedlineAnnotations[documentid] = data[documentid]; + if (docCounter == documentids.length) { + setRedlineDocumentAnnotations(documentOipcRedlineAnnotations); + } + } + ); + } + }; + const isIgnoredDocument = (doc, pagecount, divisionDocuments) => { const divdocumentlist = JSON.parse(JSON.stringify(divisionDocuments)); let removepagesCount = 0; @@ -2507,6 +2570,15 @@ const Redlining = React.forwardRef( '' + formattedAnnotationXML + ""; + + //Notes: + // You can apply redactions on the current doc but not on the stitchObject used for redlining + let doc = await docViewer.getDocument(); + + console.log('doc is object of Document: ' , doc instanceof docInstance.Core.Document) + console.log('stichObject is object of Document: ' , stitchObject instanceof docInstance.Core.Document) + + // This stitchObject is an instand of Document, but doesn't seem to have the applyRedactions function stitchObject .getFileData({ // saves the document with annotations in it @@ -2575,7 +2647,7 @@ const Redlining = React.forwardRef( } }, [redlineDocumentAnnotations, redlineStitchObject, redlineStitchInfo]); - const saveRedlineDocument = (_instance) => { + const saveRedlineDocument = async (docViewer, annotationManager, _instance) => { toastId.current = toast(`Start saving redline...`, { autoClose: false, closeButton: false, @@ -2586,6 +2658,8 @@ const Redlining = React.forwardRef( const divisions = getDivisionsForSaveRedline(divisionFilesList); const divisionDocuments = getDivisionDocumentMappingForRedline(divisions); const documentids = documentList.map((obj) => obj.documentid); + let redlinetype = "redline"; + if (currentLayer.name.toLowerCase() == "oipc") redlinetype = "oipc"; getFOIS3DocumentRedlinePreSignedUrl( requestid, //normalizeforPdfStitchingReq(divisionDocuments), @@ -2604,7 +2678,11 @@ const Redlining = React.forwardRef( res.issingleredlinepackage ); let incompatableList = prepareRedlineIncompatibleMapping(res); - fetchDocumentRedlineAnnotations(requestid, documentids); + if (currentLayer.name.toLowerCase() == "oipc") { + fetchDocumentOipcRedlineAnnotations(requestid, documentids); + } else { + fetchDocumentRedlineAnnotations(requestid, documentids); + } setRedlineZipperMessage({ ministryrequestid: requestid, category: "redline", @@ -2690,6 +2768,18 @@ const Redlining = React.forwardRef( documentsObjArr = []; } } + let annotList = annotationManager.getAnnotationsList(); + let s14AnnotIds = []; + for (let annot of annotList) { + if (annot.hR?.toLowerCase() === "s. 14") { + s14AnnotIds.push(annot.Aj?.parentRedaction); + // await annotManager.applyRedactions(annot); + } + } + for (let annotId of s14AnnotIds) { + let redactionAnnotation = annotList.find((annot) => annot.Zk === annotId) + const res = await annotationManager.applyRedactions(redactionAnnotation); + } //if (Object.keys(stitchDoc).length >0) { @@ -2705,7 +2795,8 @@ const Redlining = React.forwardRef( }, (error) => { console.log("Error fetching document:", error); - } + }, + redlinetype ); }; @@ -2713,8 +2804,11 @@ const Redlining = React.forwardRef( setRedlineModalOpen(false); setRedlineSaving(true); switch (modalFor) { + case "oipc": + saveRedlineDocument(docViewer, annotManager, docInstance); + break; case "redline": - saveRedlineDocument(docInstance); + saveRedlineDocument(docViewer, annotManager, docInstance); break; case "responsepackage": saveResponsePackage(docViewer, annotManager, docInstance); From 4d5418003087054457b343c40198cd42d1323294 Mon Sep 17 00:00:00 2001 From: Milos Despotovic Date: Fri, 15 Dec 2023 10:43:22 -0800 Subject: [PATCH 10/45] Remove var declaration --- web/src/apiManager/services/docReviewerService.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web/src/apiManager/services/docReviewerService.tsx b/web/src/apiManager/services/docReviewerService.tsx index a07730617..5dad4e196 100644 --- a/web/src/apiManager/services/docReviewerService.tsx +++ b/web/src/apiManager/services/docReviewerService.tsx @@ -182,8 +182,7 @@ export const createOipcLayer= ( httpPOSTRequest({url: apiUrlPost, data: requestJSON, token: UserService.getToken() ?? '', isBearer: true}) .then((res:any) => { if (res.data) { - const redactionlayerid = 3 // oipc layer id - store.dispatch(incrementLayerCount(3) as any); + store.dispatch(incrementLayerCount(oipcLayerId) as any); if (callback) callback(res.data); } else { throw new Error(`Error while copying annotations to OIPC layer`); From a3437af36deb360b106461d8c58545db2652c96d Mon Sep 17 00:00:00 2001 From: Aman-Hundal Date: Fri, 15 Dec 2023 13:50:01 -0800 Subject: [PATCH 11/45] OIPC validation logic tested and refactored. Ticket 4695 + scenario 1 of 4169 compelted --- api/reviewer_api/resources/document.py | 21 +------------------- api/reviewer_api/services/documentservice.py | 18 ++++++++++++++++- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/api/reviewer_api/resources/document.py b/api/reviewer_api/resources/document.py index a8b2831ad..c4b3a311f 100644 --- a/api/reviewer_api/resources/document.py +++ b/api/reviewer_api/resources/document.py @@ -23,7 +23,6 @@ from reviewer_api.utils.util import cors_preflight, allowedorigins, getrequiredmemberships from reviewer_api.exceptions import BusinessException from reviewer_api.schemas.document import FOIRequestDeleteRecordsSchema, FOIRequestUpdateRecordsSchema -from reviewer_api.services.pdfstitchpackageservice import pdfstitchpackageservice import json import requests import logging @@ -99,28 +98,10 @@ def get(requestid): response.raise_for_status() # get request status jsonobj = response.json() - print(jsonobj) - - #OIPC Layer Validation - #look at state transisont array and verify if respone exists and get latest response created_at AND check if response package exists in the pdfstitchpackge table - #latest package created_at > latest response created_at (USE THE MODEL ATTRIBUTE THAT GIVES LATEST STATES AND ADD A NEW COLM FOR CREATED_AT) (found in state transiiton) - #logic -> you cant only create rresponse package when state is in respones. First change state to response (created at) after that they create a redline and dl the response package at a later date. this logic bakes in the fact that response and response packages can occur throughout and change states at many times - validoipcreviewlayer = False - if (jsonobj['isoipcreview'] == True and any(oipc['reasonid'] == 2 for oipc in jsonobj['oipcdetails']) and jsonobj['currentState'] == "Closed" and any(state['status'] == "Response" for state in jsonobj['stateTransition'])): - for state in jsonobj['stateTransition']: - if (state['status'] == "Response"): - latest_response_state = state - break - latest_response_package = pdfstitchpackageservice().getpdfstitchpackage(requestid, 'responsepackage') - print(latest_response_state) - print(latest_response_package) - if (bool(latest_response_package) == True and latest_response_package['createdat'] > latest_response_state['created_at']): - validoipcreviewlayer = True - requestinfo = { "bcgovcode": jsonobj["bcgovcode"], "requesttype": jsonobj["requestType"], - "validoipcreviewlayer": validoipcreviewlayer, + "validoipcreviewlayer": documentservice().validate_oipcreviewlayer(jsonobj, requestid), } result = documentservice().getdocuments(requestid, requestinfo["bcgovcode"]) return json.dumps({"requeststatusid": jsonobj["requeststatusid"], "documents": result, "requestnumber":jsonobj["axisRequestId"], "requestinfo":requestinfo}), 200 diff --git a/api/reviewer_api/services/documentservice.py b/api/reviewer_api/services/documentservice.py index f0b4f4c7e..3f7d965a4 100644 --- a/api/reviewer_api/services/documentservice.py +++ b/api/reviewer_api/services/documentservice.py @@ -2,15 +2,17 @@ from reviewer_api.models.DocumentMaster import DocumentMaster from reviewer_api.models.FileConversionJob import FileConversionJob from reviewer_api.models.DeduplicationJob import DeduplicationJob -from datetime import datetime as datetime2 +from datetime import datetime as datetime2, timezone from os import path from reviewer_api.models.DocumentDeleted import DocumentDeleted import json from reviewer_api.utils.util import pstformat from reviewer_api.models.DocumentAttributes import DocumentAttributes +from reviewer_api.services.pdfstitchpackageservice import pdfstitchpackageservice import requests from reviewer_api.auth import auth, AuthHelper from os import getenv +from reviewer_api.utils.enums import StateName requestapiurl = getenv("FOI_REQ_MANAGEMENT_API_URL") @@ -473,3 +475,17 @@ def savedocument(self, documentid, documentversion, newfilepath, userid): def deleterequestdocument(self, documentid, documentversion): return + + def validate_oipcreviewlayer(self, request_json, requestid): + validoipcreviewlayer = False + latest_response_package = pdfstitchpackageservice().getpdfstitchpackage(requestid, 'responsepackage') + if (request_json['isoipcreview'] == True and any(oipc['reasonid'] == 2 for oipc in request_json['oipcdetails']) and request_json['currentState'] == StateName.closed.value and any(state['status'] == StateName.response.value for state in request_json['stateTransition']) and latest_response_package not in ({}, None)): + for state in request_json['stateTransition']: + if (state['status'] == StateName.response.value): + latest_response_state = state + break + latest_response_package_created_at = datetime2.fromisoformat(latest_response_package['createdat']) + latest_response_state_created_at = datetime2.strptime(latest_response_state['created_at'], "%a, %d %b %Y %H:%M:%S %Z").replace(tzinfo=timezone.utc) + if (latest_response_package_created_at > latest_response_state_created_at): + validoipcreviewlayer = True + return validoipcreviewlayer From f458f5b69d3375630ade98e8204791e52c1872b3 Mon Sep 17 00:00:00 2001 From: Milos Despotovic Date: Fri, 15 Dec 2023 15:49:23 -0800 Subject: [PATCH 12/45] Update state when creating oipc review layer --- web/public/stylesheets/webviewer.css | 5 +++++ web/src/components/FOI/Home/LayerDropdown.tsx | 6 ++++-- web/src/components/FOI/Home/Redlining.js | 14 ++++++++++---- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/web/public/stylesheets/webviewer.css b/web/public/stylesheets/webviewer.css index a86860f3d..1fea153e6 100644 --- a/web/public/stylesheets/webviewer.css +++ b/web/public/stylesheets/webviewer.css @@ -9,6 +9,11 @@ cursor: not-allowed !important; } +.redline_for_oipc:disabled { + color: #999999 !important; + cursor: not-allowed !important; +} + .final_package:disabled { color: #999999 !important; cursor: not-allowed !important; diff --git a/web/src/components/FOI/Home/LayerDropdown.tsx b/web/src/components/FOI/Home/LayerDropdown.tsx index f0f80ed5c..16878791c 100644 --- a/web/src/components/FOI/Home/LayerDropdown.tsx +++ b/web/src/components/FOI/Home/LayerDropdown.tsx @@ -41,9 +41,11 @@ const LayerDropdown = ({ const handleModalContinue = (e: any) => { setOpenModal(false); - store.dispatch(setCurrentLayer(layers.find((l: any) => l.redactionlayerid === layer))); + const successCallback = () => { + store.dispatch(setCurrentLayer(layers.find((l: any) => l.redactionlayerid === layer))); + } if (layers.find((l: any) => l.redactionlayerid === layer).redactionlayerid === 3) { - createOipcLayer(ministryrequestid); + createOipcLayer(ministryrequestid, successCallback); } } diff --git a/web/src/components/FOI/Home/Redlining.js b/web/src/components/FOI/Home/Redlining.js index 31d5adea6..0f4aa44d9 100644 --- a/web/src/components/FOI/Home/Redlining.js +++ b/web/src/components/FOI/Home/Redlining.js @@ -252,6 +252,9 @@ const Redlining = React.forwardRef( RequestStates["Peer Review"], ].includes(requestStatus) ); + const [enableSavingOipcRedline, setEnableSavingOipcRedline] = useState( + redactionLayers.find((l) => l.redactionlayerid === 3)?.count > 0 + ) const [enableSavingFinal, setEnableSavingFinal] = useState( isReadyForSignOff() && requestStatus == RequestStates["Response"] ); @@ -312,8 +315,7 @@ const Redlining = React.forwardRef( redlineForOipcBtn.style.padding = "8px 8px 8px 10px"; redlineForOipcBtn.style.cursor = "pointer"; redlineForOipcBtn.style.alignItems = "left"; - //TODO - adjust this - redlineForOipcBtn.disabled = false; + redlineForOipcBtn.disabled = !enableSavingOipcRedline; redlineForOipcBtn.onclick = () => { // Save to s3 @@ -1125,6 +1127,7 @@ const Redlining = React.forwardRef( const checkSavingRedlineButton = (_instance) => { let _enableSavingRedline = isReadyForSignOff() && isValidRedlineDownload(); + const _enableSavingOipcRedline = redactionLayers.find((l) => l.redactionlayerid === 3).count > 0; setEnableSavingRedline( _enableSavingRedline && @@ -1134,6 +1137,7 @@ const Redlining = React.forwardRef( RequestStates["Peer Review"], ].includes(requestStatus) ); + setEnableSavingOipcRedline(_enableSavingOipcRedline); setEnableSavingFinal( _enableSavingRedline && requestStatus == RequestStates["Response"] ); @@ -1145,7 +1149,9 @@ const Redlining = React.forwardRef( RequestStates["Records Review"], RequestStates["Ministry Sign Off"], RequestStates["Peer Review"], - ].includes(requestStatus);; + ].includes(requestStatus) || + _enableSavingOipcRedline; + document.getElementById("redline_for_oipc").disabled = !_enableSavingOipcRedline document.getElementById("final_package").disabled = !_enableSavingRedline || requestStatus !== RequestStates["Response"]; } @@ -1153,7 +1159,7 @@ const Redlining = React.forwardRef( useEffect(() => { checkSavingRedlineButton(docInstance); - }, [pageFlags, isStitchingLoaded]); + }, [pageFlags, isStitchingLoaded, currentLayer]); const stitchDocumentsFunc = async (doc) => { let docCopy = [...docsForStitcing]; From a1143b80f10e7ac8b36549efa3aeae8f46c4f3ba Mon Sep 17 00:00:00 2001 From: "sumathi.thirumani" Date: Wed, 20 Dec 2023 14:37:12 -0800 Subject: [PATCH 13/45] Changes to redact S14 sections for OIPC. --- web/src/components/FOI/Home/Redlining.js | 79 +++++++++++++++++++++--- 1 file changed, 71 insertions(+), 8 deletions(-) diff --git a/web/src/components/FOI/Home/Redlining.js b/web/src/components/FOI/Home/Redlining.js index 0f4aa44d9..1760a5109 100644 --- a/web/src/components/FOI/Home/Redlining.js +++ b/web/src/components/FOI/Home/Redlining.js @@ -132,6 +132,7 @@ const Redlining = React.forwardRef( const [modalMessage, setModalMessage] = useState([""]); const [modalButtonLabel, setModalButtonLabel] = useState(""); const [redlineSaving, setRedlineSaving] = useState(false); + const [redlineCategory, setRedlineCategory] = useState(false); // State variables for Bulk Edit using Multi Selection option const [editRedacts, setEditRedacts] = useState(null); const [multiSelectFooter, setMultiSelectFooter] = useState(null); @@ -2471,6 +2472,34 @@ const Redlining = React.forwardRef( return stitchAnnotation.join(); }; + const getAnnotationSections = (annot) => { + let customSectionsData = annot.getCustomData("sections"); + let stampJson = JSON.parse( + customSectionsData + .replace(/"\[/g, "[") + .replace(/\]"/g, "]") + .replace(/"/g, '"') + .replace(/\\/g, "") + ); + return stampJson; + } + + const annotationSectionsMapping = async (xfdfString) => { + let annotationManager = docInstance?.Core.annotationManager; + let annotList = await annotationManager.importAnnotations(xfdfString); + let sectionStamps = {}; + for (const annot of annotList) { + let parentRedaction = annot.getCustomData("parentRedaction"); + if (parentRedaction) { + if (annot.Subject == "Free Text") { + let parentRedactionId = parentRedaction.replace(/"/g, '"').replace(/\\/g, "") + sectionStamps[parentRedactionId] = getAnnotationSections(annot); + } + } + } + return sectionStamps; + } + const formatAnnotationsForDocument = ( domParser, data, @@ -2577,14 +2606,47 @@ const Redlining = React.forwardRef( formattedAnnotationXML + ""; - //Notes: - // You can apply redactions on the current doc but not on the stitchObject used for redlining - let doc = await docViewer.getDocument(); - - console.log('doc is object of Document: ' , doc instanceof docInstance.Core.Document) - console.log('stichObject is object of Document: ' , stitchObject instanceof docInstance.Core.Document) - - // This stitchObject is an instand of Document, but doesn't seem to have the applyRedactions function + //OIPC - Special Block (Redact S.14) : Begin + if(redlineCategory == "oipc") { + const rarr = []; + let annotationManager = docInstance?.Core.annotationManager; + let sectionStamps = await annotationSectionsMapping(xfdfString); + let rects = []; + for (const [key, value] of Object.entries(sectionStamps)) { + let s14check_annotationid = key; + let s14check_sections = sectionStamps[key]; + for (const [s14key, s14value] in s14check_sections) { + if (s14check_sections[s14key]["section"] === "s. 14") { + let s14annoation = annotationManager.getAnnotationById(s14check_annotationid); + if ( s14annoation.Subject === "Redact") { + rects = rects.concat( + s14annoation.getQuads().map((q) => { + return { + page: s14annoation.getPageNumber(), + recto: q.toRect() + }; + }) + ); + } + } + } + } + for (const rect of rects) { + let height = docViewer.getPageHeight(rect.page); + rarr.push(await PDFNet.Redactor.redactionCreate(rect.page, (await PDFNet.Rect.init(rect.recto.x1,height-rect.recto.y1,rect.recto.x2,height-rect.recto.y2)), false, '')); + + } + if (rarr.length > 0) { + const app = {}; + app.redaction_overlay = true; + app.border = false; + app.show_redacted_content_regions = false; + const doc = await stitchObject.getPDFDoc(); + await PDFNet.Redactor.redact(doc, rarr, app); + } + + } + //OIPC - Special Block : End stitchObject .getFileData({ // saves the document with annotations in it @@ -2809,6 +2871,7 @@ const Redlining = React.forwardRef( const saveDoc = () => { setRedlineModalOpen(false); setRedlineSaving(true); + setRedlineCategory(modalFor); switch (modalFor) { case "oipc": saveRedlineDocument(docViewer, annotManager, docInstance); From 56bb2ca76259358afa13d17ab6f658ac6f8a5492 Mon Sep 17 00:00:00 2001 From: Milos Despotovic Date: Thu, 21 Dec 2023 17:19:16 -0800 Subject: [PATCH 14/45] Updated presigned redline endpoint --- .../resources/foiflowmasterdata.py | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/api/reviewer_api/resources/foiflowmasterdata.py b/api/reviewer_api/resources/foiflowmasterdata.py index ef6df1782..c4783ff0c 100644 --- a/api/reviewer_api/resources/foiflowmasterdata.py +++ b/api/reviewer_api/resources/foiflowmasterdata.py @@ -257,14 +257,14 @@ def post(ministryrequestid): @cors_preflight("POST,OPTIONS") @API.route("/foiflow/oss/presigned/redline/") -@API.route("/foiflow/oss/presigned/redline//") +@API.route("/foiflow/oss/presigned/redline///") class FOIFlowS3PresignedRedline(Resource): @staticmethod @TRACER.trace() @cross_origin(origins=allowedorigins()) @auth.require @auth.ismemberofgroups(getrequiredmemberships()) - def post(ministryrequestid, redlinetype="redline"): + def post(ministryrequestid, redlinetype="redline", layer="redline"): try: data = request.get_json() documentmapper = redactionservice().getdocumentmapper( @@ -288,14 +288,24 @@ def post(ministryrequestid, redlinetype="redline"): singlepkgpath = None; #New Logic - Begin filepaths = [] + + # set type of package based off redline and layer + packagetype = "redline" + if redlinetype == "oipc": + packagetype = "oipcreview" + elif redlinetype == "redline" and layer == "redline": + packagetype = "redline" + elif redlinetype == "redline" and layer == "oipc": + packagetype = "oipcredline" + for div in data["divdocumentList"]: if len(div["documentlist"]) > 0: filepathlist = div["documentlist"][0]["filepath"].split("/")[4:] - if is_single_redline_package(_bcgovcode) == False: + if is_single_redline_package(_bcgovcode, redlinetype) == False: division_name = div["divisionname"] # generate save url for stitched file filepath_put = "{0}/{2}/{1}/{0} - {2} - {1}.pdf".format( - filepathlist[0], division_name, redlinetype + filepathlist[0], division_name, packagetype ) # filename_put, file_extension_put = os.path.splitext(filepath_put) @@ -345,13 +355,13 @@ def post(ministryrequestid, redlinetype="redline"): ) elif len(div["incompatableList"]) > 0: filepathlist = div["incompatableList"][0]["filepath"].split("/")[4:] - if is_single_redline_package(_bcgovcode) and singlepkgpath is None : + if is_single_redline_package(_bcgovcode, redlinetype) and singlepkgpath is None : if len(div["documentlist"]) > 0 or len(div["incompatableList"]) > 0: div = data["divdocumentList"][0] filepathlist = div["documentlist"][0]["filepath"].split("/")[4:] filename = filepathlist[0] - filepath_put = "{0}/redline/{1}-Redline.pdf".format( - filepathlist[0],filename + filepath_put = "{0}/{2}/{1}-Redline.pdf".format( + filepathlist[0],filename, packagetype ) s3path_save = s3client.generate_presigned_url( ClientMethod="get_object", @@ -366,7 +376,7 @@ def post(ministryrequestid, redlinetype="redline"): singlepkgpath = s3path_save data["s3path_save"] = s3path_save - if is_single_redline_package(_bcgovcode): + if is_single_redline_package(_bcgovcode, redlinetype): for div in data["divdocumentList"]: if len(div["documentlist"]) > 0: documentlist_copy = div["documentlist"][:] @@ -385,7 +395,7 @@ def post(ministryrequestid, redlinetype="redline"): data["requestnumber"] = filepathlist[0] data["bcgovcode"] = _bcgovcode - data["issingleredlinepackage"] = "Y" if is_single_redline_package(_bcgovcode) else "N" + data["issingleredlinepackage"] = "Y" if is_single_redline_package(_bcgovcode, redlinetype) else "N" return json.dumps(data), 200 except BusinessException as exception: return {"status": exception.status_code, "message": exception.message}, 500 From 8aeba7935ae0247a8763df4b6f12cccf824a9f8b Mon Sep 17 00:00:00 2001 From: Milos Despotovic Date: Thu, 21 Dec 2023 17:19:33 -0800 Subject: [PATCH 15/45] Update util function --- api/reviewer_api/utils/util.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/api/reviewer_api/utils/util.py b/api/reviewer_api/utils/util.py index eddc13113..81ebce507 100644 --- a/api/reviewer_api/utils/util.py +++ b/api/reviewer_api/utils/util.py @@ -136,7 +136,9 @@ def getbatchconfig(): _limit = _batchconfig["limit"] if "limit" in _batchconfig else 250 return _begin, _size, _limit -def is_single_redline_package(bcgovcode): +def is_single_redline_package(bcgovcode, redlinetype): + if (redlinetype == "oipc"): + return True if REDLINE_SINGLE_PKG_MINISTRIES not in (None, ""): _pkg_ministries = REDLINE_SINGLE_PKG_MINISTRIES.replace(" ", "").split(',') if bcgovcode.upper() in _pkg_ministries: From 6d989edf7738b792753e22f61295e1a67cdd5729 Mon Sep 17 00:00:00 2001 From: Milos Despotovic Date: Thu, 21 Dec 2023 17:20:02 -0800 Subject: [PATCH 16/45] Adjust url based on redline type and layer --- web/src/apiManager/services/foiOSSService.tsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/web/src/apiManager/services/foiOSSService.tsx b/web/src/apiManager/services/foiOSSService.tsx index b11d537b3..da9faadca 100644 --- a/web/src/apiManager/services/foiOSSService.tsx +++ b/web/src/apiManager/services/foiOSSService.tsx @@ -49,12 +49,22 @@ export const getFOIS3DocumentRedlinePreSignedUrl = ( documentList: any[], callback: any, errorCallback: any, - type: any = "redline" + redlineType: any = "redline", + layer: any = "redline" ) => { let apiurl; apiurl = API.FOI_GET_S3DOCUMENT_PRESIGNEDURL_REDLINE + "/" + ministryrequestID; - if (type === "oipc") { + // set redlinetype param + if (redlineType === "oipc") { apiurl = apiurl + "/oipc" + } else if (redlineType === "redline") { + apiurl = apiurl + "/redline" + } + // set layer param + if (layer === "oipc") { + apiurl = apiurl + "/oipc" + } else if (layer === "redline") { + apiurl = apiurl + "/redline" } httpPOSTRequest({url: apiurl, data: {"divdocumentList":documentList}, token: UserService.getToken() || ''}) From 1afa4965c560ef962e24f34cb1d49613989830ea Mon Sep 17 00:00:00 2001 From: Milos Despotovic Date: Thu, 21 Dec 2023 17:21:10 -0800 Subject: [PATCH 17/45] Update redlining for oipc --- web/src/components/FOI/Home/Redlining.js | 43 +++++++++++++++--------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/web/src/components/FOI/Home/Redlining.js b/web/src/components/FOI/Home/Redlining.js index 1760a5109..52b652acd 100644 --- a/web/src/components/FOI/Home/Redlining.js +++ b/web/src/components/FOI/Home/Redlining.js @@ -321,12 +321,13 @@ const Redlining = React.forwardRef( redlineForOipcBtn.onclick = () => { // Save to s3 setModalFor("oipc"); - setModalTitle("Redline for OIPC"); + setModalTitle("Redline for OIPC Review"); setModalMessage([ "Are you sure want to create the redline PDF for OIPC review?",
,
, + This redline will be created from the active layer with s.14 annotations redacted. When you create the redline PDF, your web browser page will automatically refresh , @@ -702,7 +703,7 @@ const Redlining = React.forwardRef( (error) => { console.log("Error:", error); }, - currentLayer.name + currentLayer.name.toLowerCase() ); fetchPageFlag(requestid, currentLayer.redactionlayerid, (error) => console.log(error) @@ -1128,7 +1129,9 @@ const Redlining = React.forwardRef( const checkSavingRedlineButton = (_instance) => { let _enableSavingRedline = isReadyForSignOff() && isValidRedlineDownload(); - const _enableSavingOipcRedline = redactionLayers.find((l) => l.redactionlayerid === 3).count > 0; + const _enableSavingOipcRedline = + redactionLayers.find((l) => l.redactionlayerid === 3).count > 0 && + isReadyForSignOff(); setEnableSavingRedline( _enableSavingRedline && @@ -1138,7 +1141,13 @@ const Redlining = React.forwardRef( RequestStates["Peer Review"], ].includes(requestStatus) ); - setEnableSavingOipcRedline(_enableSavingOipcRedline); + setEnableSavingOipcRedline( + _enableSavingOipcRedline && + [ + RequestStates["Records Review"], + RequestStates["Ministry Sign Off"] + ].includes(requestStatus) + ); setEnableSavingFinal( _enableSavingRedline && requestStatus == RequestStates["Response"] ); @@ -1150,9 +1159,14 @@ const Redlining = React.forwardRef( RequestStates["Records Review"], RequestStates["Ministry Sign Off"], RequestStates["Peer Review"], - ].includes(requestStatus) || - _enableSavingOipcRedline; - document.getElementById("redline_for_oipc").disabled = !_enableSavingOipcRedline + ].includes(requestStatus); + document.getElementById("redline_for_oipc").disabled = + !_enableSavingOipcRedline || + ![ + RequestStates["Records Review"], + RequestStates["Ministry Sign Off"], + ].includes(requestStatus) || + !isReadyForSignOff(); document.getElementById("final_package").disabled = !_enableSavingRedline || requestStatus !== RequestStates["Response"]; } @@ -2715,7 +2729,7 @@ const Redlining = React.forwardRef( } }, [redlineDocumentAnnotations, redlineStitchObject, redlineStitchInfo]); - const saveRedlineDocument = async (docViewer, annotationManager, _instance) => { + const saveRedlineDocument = async (docViewer, annotationManager, _instance, redlineType = "redline") => { toastId.current = toast(`Start saving redline...`, { autoClose: false, closeButton: false, @@ -2726,8 +2740,6 @@ const Redlining = React.forwardRef( const divisions = getDivisionsForSaveRedline(divisionFilesList); const divisionDocuments = getDivisionDocumentMappingForRedline(divisions); const documentids = documentList.map((obj) => obj.documentid); - let redlinetype = "redline"; - if (currentLayer.name.toLowerCase() == "oipc") redlinetype = "oipc"; getFOIS3DocumentRedlinePreSignedUrl( requestid, //normalizeforPdfStitchingReq(divisionDocuments), @@ -2746,9 +2758,9 @@ const Redlining = React.forwardRef( res.issingleredlinepackage ); let incompatableList = prepareRedlineIncompatibleMapping(res); - if (currentLayer.name.toLowerCase() == "oipc") { + if (currentLayer.name.toLowerCase() == "oipc" || redlineType == "oipc") { fetchDocumentOipcRedlineAnnotations(requestid, documentids); - } else { + } else if (currentLayer.name.toLowerCase() == "redline") { fetchDocumentRedlineAnnotations(requestid, documentids); } setRedlineZipperMessage({ @@ -2864,7 +2876,8 @@ const Redlining = React.forwardRef( (error) => { console.log("Error fetching document:", error); }, - redlinetype + redlineType, + currentLayer.name.toLowerCase() ); }; @@ -2874,10 +2887,10 @@ const Redlining = React.forwardRef( setRedlineCategory(modalFor); switch (modalFor) { case "oipc": - saveRedlineDocument(docViewer, annotManager, docInstance); + saveRedlineDocument(docViewer, annotManager, docInstance, "oipc"); break; case "redline": - saveRedlineDocument(docViewer, annotManager, docInstance); + saveRedlineDocument(docViewer, annotManager, docInstance, "redline"); break; case "responsepackage": saveResponsePackage(docViewer, annotManager, docInstance); From 3a22444babd2abf8192264fd48173e7da8cc8bd7 Mon Sep 17 00:00:00 2001 From: Milos Despotovic Date: Thu, 21 Dec 2023 17:22:05 -0800 Subject: [PATCH 18/45] Remove disabled add OIPC button --- web/src/components/FOI/Home/LayerDropdown.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/FOI/Home/LayerDropdown.tsx b/web/src/components/FOI/Home/LayerDropdown.tsx index 44231712f..d6fdf9c6a 100644 --- a/web/src/components/FOI/Home/LayerDropdown.tsx +++ b/web/src/components/FOI/Home/LayerDropdown.tsx @@ -68,7 +68,7 @@ const LayerDropdown = ({ variant="outlined" > {layers.map((option: any) => ( - + { option.redactionlayerid > 2 && option.count === 0 From 57f790c16302b388b9fb62b40bc5386664c0dfaf Mon Sep 17 00:00:00 2001 From: Milos Despotovic Date: Thu, 21 Dec 2023 17:32:33 -0800 Subject: [PATCH 19/45] Revert disable layer button change --- web/src/components/FOI/Home/LayerDropdown.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/FOI/Home/LayerDropdown.tsx b/web/src/components/FOI/Home/LayerDropdown.tsx index d6fdf9c6a..44231712f 100644 --- a/web/src/components/FOI/Home/LayerDropdown.tsx +++ b/web/src/components/FOI/Home/LayerDropdown.tsx @@ -68,7 +68,7 @@ const LayerDropdown = ({ variant="outlined" > {layers.map((option: any) => ( - + { option.redactionlayerid > 2 && option.count === 0 From a5825c1ae4f28872d7808985760dc6d7848f50c6 Mon Sep 17 00:00:00 2001 From: Aman-Hundal Date: Fri, 22 Dec 2023 12:13:48 -0800 Subject: [PATCH 20/45] Adjusted redlining.js to adjust category based on current layer (allowing for accurate pathing, filename and saving of oipcreview layer to s3) --- web/src/components/FOI/Home/Redlining.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/src/components/FOI/Home/Redlining.js b/web/src/components/FOI/Home/Redlining.js index 1760a5109..26081c54c 100644 --- a/web/src/components/FOI/Home/Redlining.js +++ b/web/src/components/FOI/Home/Redlining.js @@ -702,7 +702,7 @@ const Redlining = React.forwardRef( (error) => { console.log("Error:", error); }, - currentLayer.name + currentLayer.name.toLowerCase() ); fetchPageFlag(requestid, currentLayer.redactionlayerid, (error) => console.log(error) @@ -1292,7 +1292,7 @@ const Redlining = React.forwardRef( "Error occurred while fetching redaction details, please refresh browser and try again" ); }, - currentLayer.name + currentLayer.name.toLowerCase() ); } }; @@ -2753,7 +2753,7 @@ const Redlining = React.forwardRef( } setRedlineZipperMessage({ ministryrequestid: requestid, - category: "redline", + category: currentLayer.name.toLowerCase() === "oipc" ? "oipcreviewredline" : "redline", attributes: [], requestnumber: res.requestnumber, bcgovcode: res.bcgovcode, From ee9c37d486776cde4054d2385c11d77ed593423e Mon Sep 17 00:00:00 2001 From: Aman-Hundal Date: Fri, 22 Dec 2023 14:16:42 -0800 Subject: [PATCH 21/45] Adjusted FE API calls to create folders with specific names depending on the redline package created (redline, oipcredline, oipcreviewredline) --- web/src/apiManager/services/foiOSSService.tsx | 4 ++-- web/src/components/FOI/Home/Redlining.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/web/src/apiManager/services/foiOSSService.tsx b/web/src/apiManager/services/foiOSSService.tsx index b11d537b3..76490e574 100644 --- a/web/src/apiManager/services/foiOSSService.tsx +++ b/web/src/apiManager/services/foiOSSService.tsx @@ -53,8 +53,8 @@ export const getFOIS3DocumentRedlinePreSignedUrl = ( ) => { let apiurl; apiurl = API.FOI_GET_S3DOCUMENT_PRESIGNEDURL_REDLINE + "/" + ministryrequestID; - if (type === "oipc") { - apiurl = apiurl + "/oipc" + if (type === "oipcreviewredline") { + apiurl = apiurl + "/oipcreviewredline" } httpPOSTRequest({url: apiurl, data: {"divdocumentList":documentList}, token: UserService.getToken() || ''}) diff --git a/web/src/components/FOI/Home/Redlining.js b/web/src/components/FOI/Home/Redlining.js index 26081c54c..33b629efd 100644 --- a/web/src/components/FOI/Home/Redlining.js +++ b/web/src/components/FOI/Home/Redlining.js @@ -2727,7 +2727,7 @@ const Redlining = React.forwardRef( const divisionDocuments = getDivisionDocumentMappingForRedline(divisions); const documentids = documentList.map((obj) => obj.documentid); let redlinetype = "redline"; - if (currentLayer.name.toLowerCase() == "oipc") redlinetype = "oipc"; + if (currentLayer.name.toLowerCase() == "oipc") redlinetype = "oipcreviewredline"; getFOIS3DocumentRedlinePreSignedUrl( requestid, //normalizeforPdfStitchingReq(divisionDocuments), From 7c1b1cb52ee2b83cc393ba1173a317c7f21949a3 Mon Sep 17 00:00:00 2001 From: "sumathi.thirumani" Date: Fri, 22 Dec 2023 14:55:04 -0800 Subject: [PATCH 22/45] Adjusted logic to show OIPC only if reopen and response package created. --- api/reviewer_api/models/PDFStitchPackage.py | 12 +++++++ api/reviewer_api/services/documentservice.py | 33 ++++++++++++------- .../services/pdfstitchpackageservice.py | 4 +++ 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/api/reviewer_api/models/PDFStitchPackage.py b/api/reviewer_api/models/PDFStitchPackage.py index e8f24c5db..9bcfda945 100644 --- a/api/reviewer_api/models/PDFStitchPackage.py +++ b/api/reviewer_api/models/PDFStitchPackage.py @@ -1,6 +1,7 @@ from .db import db, ma from datetime import datetime as datetime2 from .default_method_result import DefaultMethodResult +from sqlalchemy import func import logging class PDFStitchPackage(db.Model): @@ -35,6 +36,17 @@ def getpdfstitchpackage(cls, requestid, category): logging.error(ex) finally: db.session.close() + + @classmethod + def isresponsepackagecreated(cls, requestid, generatedat): + try: + query = db.session.query(func.count(PDFStitchPackage.pdfstitchpackageid)).filter(PDFStitchPackage.ministryrequestid == requestid, PDFStitchPackage.category == "responsepackage", PDFStitchPackage.createdat <= generatedat) + packagecount = query.scalar() + return True if packagecount > 0 else False + except Exception as ex: + logging.error(ex) + finally: + db.session.close() class PDFStitchPackageSchema(ma.Schema): class Meta: diff --git a/api/reviewer_api/services/documentservice.py b/api/reviewer_api/services/documentservice.py index 3f7d965a4..690fc8b38 100644 --- a/api/reviewer_api/services/documentservice.py +++ b/api/reviewer_api/services/documentservice.py @@ -477,15 +477,24 @@ def deleterequestdocument(self, documentid, documentversion): return def validate_oipcreviewlayer(self, request_json, requestid): - validoipcreviewlayer = False - latest_response_package = pdfstitchpackageservice().getpdfstitchpackage(requestid, 'responsepackage') - if (request_json['isoipcreview'] == True and any(oipc['reasonid'] == 2 for oipc in request_json['oipcdetails']) and request_json['currentState'] == StateName.closed.value and any(state['status'] == StateName.response.value for state in request_json['stateTransition']) and latest_response_package not in ({}, None)): - for state in request_json['stateTransition']: - if (state['status'] == StateName.response.value): - latest_response_state = state - break - latest_response_package_created_at = datetime2.fromisoformat(latest_response_package['createdat']) - latest_response_state_created_at = datetime2.strptime(latest_response_state['created_at'], "%a, %d %b %Y %H:%M:%S %Z").replace(tzinfo=timezone.utc) - if (latest_response_package_created_at > latest_response_state_created_at): - validoipcreviewlayer = True - return validoipcreviewlayer + #check for OIPC & Reason + if 'isoipcreview' in request_json and request_json['isoipcreview'] == True and any(oipc['reasonid'] == 2 for oipc in request_json['oipcdetails']): + #Check for Reopen + if 'isreopened' in request_json and request_json['isreopened'] == True: + #Check is Response Package generated before closure. + generatedbefore = self.__get_close_datetime(request_json) + if generatedbefore is not None: + is_responsepackage_generated = pdfstitchpackageservice().isresponsepackagecreated(requestid, generatedbefore) + return is_responsepackage_generated + return False + + def __get_close_datetime(self, request_json): + generatedbefore = None + isresponsephasecompleted = False + for state in request_json['stateTransition']: + if state['status'] == StateName.closed.value and generatedbefore is None: + generatedbefore = state['created_at'] + if state['status'] == StateName.response.value and isresponsephasecompleted == False: + isresponsephasecompleted = True + return generatedbefore if isresponsephasecompleted == True else None + diff --git a/api/reviewer_api/services/pdfstitchpackageservice.py b/api/reviewer_api/services/pdfstitchpackageservice.py index 9da2609a3..e3fca76d3 100644 --- a/api/reviewer_api/services/pdfstitchpackageservice.py +++ b/api/reviewer_api/services/pdfstitchpackageservice.py @@ -14,3 +14,7 @@ def getrecordschanged(self, requestid, category): jobid = job.get("pdfstitchjobid") return PDFStitchJob.getrecordschanged(requestid, category, jobid) return {"recordchanged": False} + + def isresponsepackagecreated(self, requestid, generatedat): + """Returns the active records""" + return PDFStitchPackage.isresponsepackagecreated(requestid, generatedat) From b725527d2c75ff9b3540d95fa11e0e6069c0dc86 Mon Sep 17 00:00:00 2001 From: "sumathi.thirumani" Date: Tue, 26 Dec 2023 15:02:28 -0800 Subject: [PATCH 23/45] Defensive logic for copy of redaction + page flag copy. --- api/reviewer_api/models/Annotations.py | 12 +++++++++++ api/reviewer_api/models/DocumentPageflags.py | 21 +++++++++++++++++++ .../services/annotationservice.py | 19 ++++++++++------- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/api/reviewer_api/models/Annotations.py b/api/reviewer_api/models/Annotations.py index 8f25ca52b..2d2252e40 100644 --- a/api/reviewer_api/models/Annotations.py +++ b/api/reviewer_api/models/Annotations.py @@ -9,6 +9,7 @@ from reviewer_api.utils.util import split, getbatchconfig from .Documents import Document from .DocumentMaster import DocumentMaster +from sqlalchemy import func class Annotation(db.Model): @@ -309,6 +310,17 @@ def saveannotations(cls, annots, redactionlayerid, userinfo) -> DefaultMethodRes else: return DefaultMethodResult(False, "Invalid Annotation Request", -1) + @classmethod + def isannotationscopied(cls, documentids, targetlayer): + try: + query = db.session.query(func.count(Annotation.annotationid)).filter(Annotation.documentid.in_(documentids), Annotation.redactionlayerid == targetlayer) + annotationcount = query.scalar() + return True if annotationcount > 0 else False + except Exception as ex: + logging.error(ex) + finally: + db.session.close() + @classmethod def copyannotations(cls, documentids, sourceredactionlayers, targetlayer) -> DefaultMethodResult: try: diff --git a/api/reviewer_api/models/DocumentPageflags.py b/api/reviewer_api/models/DocumentPageflags.py index 86014ddeb..2df7839d4 100644 --- a/api/reviewer_api/models/DocumentPageflags.py +++ b/api/reviewer_api/models/DocumentPageflags.py @@ -249,6 +249,27 @@ def getpageflagsbydocids( finally: db.session.close() + @classmethod + def copydocumentpageflags(cls, ministryrequestid, sourcelayers, targetlayer) -> DefaultMethodResult: + try: + sql = """ + insert into "DocumentPageflags" (foiministryrequestid, documentid, documentversion, "pageflag", "attributes", + created_at, createdby, updated_at, updatedby, redactionlayerid) + select foiministryrequestid, documentid, documentversion, "pageflag", "attributes", created_at, + createdby, updated_at, updatedby, :targetlayer from "DocumentPageflags" a + where foiministryrequestid = :ministryrequestid and redactionlayerid in :sourcelayers; + """ + db.session.execute( + text(sql), + {"ministryrequestid": ministryrequestid, "sourcelayers": tuple(sourcelayers), "targetlayer": targetlayer}, + ) + db.session.commit() + return DefaultMethodResult(True, "Document pageflags are copied to layer", targetlayer) + except Exception as ex: + logging.error(ex) + finally: + db.session.close() + class DocumentPageflagSchema(ma.Schema): class Meta: diff --git a/api/reviewer_api/services/annotationservice.py b/api/reviewer_api/services/annotationservice.py index 39974c2cf..2edaadafa 100644 --- a/api/reviewer_api/services/annotationservice.py +++ b/api/reviewer_api/services/annotationservice.py @@ -3,6 +3,8 @@ from reviewer_api.models.Documents import Document from reviewer_api.models.Annotations import Annotation from reviewer_api.models.AnnotationSections import AnnotationSection +from reviewer_api.models.DocumentPageflags import DocumentPageflag + from reviewer_api.schemas.annotationrequest import SectionAnnotationSchema from reviewer_api.models.default_method_result import DefaultMethodResult @@ -144,14 +146,15 @@ def getannotationsections(self, ministryid): def copyannotation(self, ministryrequestid, sourcelayers, targetlayer): documentids = Document.getdocumentidsbyrequest(ministryrequestid) - print(ministryrequestid) - print(sourcelayers) - print(targetlayer) - print(documentids) - annotresponse = Annotation.copyannotations(documentids, sourcelayers, targetlayer) - if annotresponse.success == True: - AnnotationSection.copyannotationsections(ministryrequestid, sourcelayers, targetlayer) - return DefaultMethodResult(True, "Copied Annotations", ministryrequestid) + #Additional Check to ensure double copy do not happen + iscopied = Annotation.isannotationscopied(documentids, targetlayer) + if iscopied == False: + annotresponse = Annotation.copyannotations(documentids, sourcelayers, targetlayer) + if annotresponse.success == True: + AnnotationSection.copyannotationsections(ministryrequestid, sourcelayers, targetlayer) + DocumentPageflag.copydocumentpageflags(ministryrequestid, sourcelayers, targetlayer) + return DefaultMethodResult(True, "Copied Annotations", ministryrequestid) + return DefaultMethodResult(False, "Annotations already exist", targetlayer) def saveannotation(self, annotationschema, userinfo): From 275c134acbe2c41021daa0047d5364b3deb20768 Mon Sep 17 00:00:00 2001 From: Milos Despotovic Date: Wed, 27 Dec 2023 20:46:12 -0800 Subject: [PATCH 24/45] Disable pageflag changes on redline when OIPC exists --- web/src/components/FOI/Home/ContextMenu.tsx | 155 +++++++++++--------- 1 file changed, 85 insertions(+), 70 deletions(-) diff --git a/web/src/components/FOI/Home/ContextMenu.tsx b/web/src/components/FOI/Home/ContextMenu.tsx index 24c5ad7cf..67bcc5358 100644 --- a/web/src/components/FOI/Home/ContextMenu.tsx +++ b/web/src/components/FOI/Home/ContextMenu.tsx @@ -29,6 +29,7 @@ const ContextMenu = ({ const [openConsultPopup, setOpenConsultPopup] = useState(false) const [flagId, setFlagId] = React.useState(0); const currentLayer = useAppSelector((state: any) => state.documents?.currentLayer); + const redactionLayers = useAppSelector((state: any) => state.documents?.redactionLayers); const popoverEnter = (e: any) => { @@ -95,79 +96,93 @@ const ContextMenu = ({ // return (({others , programareas }) => (others ? { others, programareas } : {others: [], programareas}))(consult); // } - const showPageFlagList = () => pageFlagList?.map((pageFlag: any, index: number) => { - return (pageFlag?.name === 'Page Left Off' ? -
{ - if (selectedPages.length === 1) { - savePageFlags(pageFlag.pageflagid) - } - }}> -
-
- {pageFlag?.name} - - - -
-
: - <> - - - {(pageFlag?.name == 'Consult' ? - <> -
openConsultModal(pageFlag.pageflagid)}> - {/*
*/} - - {pageFlag?.name} - - - -
- {/* closePopup()} - disableRestoreFocus - > -
- {ministryOrgCodes(pageFlag, selectedFile.documentid, selectedFile.version)} - {otherMinistryOrgCodes(pageFlag, selectedFile.documentid, selectedFile.version)} -
addOtherPublicBody(pageFlag.pageflagid)}> - - - - Add Other -
-
-
*/} - - : -
savePageFlags(pageFlag.pageflagid)}> - - {pageFlag?.name} + const showPageFlagList = () => { + const oipcLayer = redactionLayers.find((l: any) => l.redactionlayerid === 3) + if (oipcLayer && oipcLayer.count > 0 && currentLayer.name.toLowerCase() === "redline") { + return ( + + +
+ Disabled for OIPC review
- )}
- - ) - }); + ) + } + return pageFlagList?.map((pageFlag: any, index: number) => { + return (pageFlag?.name === 'Page Left Off' ? +
{ + if (selectedPages.length === 1) { + savePageFlags(pageFlag.pageflagid) + } + }}> +
+
+ {pageFlag?.name} + + + +
+
: + <> + + + {(pageFlag?.name == 'Consult' ? + <> +
openConsultModal(pageFlag.pageflagid)}> + {/*
*/} + + {pageFlag?.name} + + + +
+ {/* closePopup()} + disableRestoreFocus + > +
+ {ministryOrgCodes(pageFlag, selectedFile.documentid, selectedFile.version)} + {otherMinistryOrgCodes(pageFlag, selectedFile.documentid, selectedFile.version)} +
addOtherPublicBody(pageFlag.pageflagid)}> + + + + Add Other +
+
+
*/} + + : +
savePageFlags(pageFlag.pageflagid)}> + + {pageFlag?.name} +
+ )} + + + + ) + }) + }; return ( <> From cc74291f6f85aff78160d86049d3c27730d88cc5 Mon Sep 17 00:00:00 2001 From: "sumathi.thirumani" Date: Thu, 28 Dec 2023 12:05:26 -0800 Subject: [PATCH 25/45] Changed to keyword of zipper --- web/src/components/FOI/Home/Redlining.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/web/src/components/FOI/Home/Redlining.js b/web/src/components/FOI/Home/Redlining.js index c9ab53f93..e65a708b8 100644 --- a/web/src/components/FOI/Home/Redlining.js +++ b/web/src/components/FOI/Home/Redlining.js @@ -2602,7 +2602,7 @@ const Redlining = React.forwardRef( ""; //OIPC - Special Block (Redact S.14) : Begin - if(redlineCategory == "oipcreview") { + if(redlineCategory === "oipcreview") { const rarr = []; let annotationManager = docInstance?.Core.annotationManager; let sectionStamps = await annotationSectionsMapping(xfdfString); @@ -2710,6 +2710,14 @@ const Redlining = React.forwardRef( } }, [redlineDocumentAnnotations, redlineStitchObject, redlineStitchInfo]); + const getzipredlinecategory = (layertype) => { + if (currentLayer.name.toLowerCase() === "oipc") { + return layertype === "oipcreview" ? "oipcreviewredline" : "oipcredline"; + } + + return "redline"; + } + const saveRedlineDocument = async (docViewer, annotationManager, _instance, layertype) => { toastId.current = toast(`Start saving redline...`, { autoClose: false, @@ -2742,7 +2750,7 @@ const Redlining = React.forwardRef( fetchDocumentRedlineAnnotations(requestid, documentids, currentLayer.name.toLowerCase()); setRedlineZipperMessage({ ministryrequestid: requestid, - category: currentLayer.name.toLowerCase() === "oipc" ? "oipcreviewredline" : "redline", + category: getzipredlinecategory(layertype), attributes: [], requestnumber: res.requestnumber, bcgovcode: res.bcgovcode, From 6f8a6420c58ea317c575b595fe05f5e86b7b5dd4 Mon Sep 17 00:00:00 2001 From: "sumathi.thirumani" Date: Thu, 28 Dec 2023 12:26:46 -0800 Subject: [PATCH 26/45] change the font color to black. --- web/src/components/FOI/Home/LayerDropdown.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/FOI/Home/LayerDropdown.tsx b/web/src/components/FOI/Home/LayerDropdown.tsx index 44231712f..b53acf602 100644 --- a/web/src/components/FOI/Home/LayerDropdown.tsx +++ b/web/src/components/FOI/Home/LayerDropdown.tsx @@ -68,7 +68,7 @@ const LayerDropdown = ({ variant="outlined" > {layers.map((option: any) => ( - + { option.redactionlayerid > 2 && option.count === 0 From 893b5550b608c80ff6cefd9e2387562567963d20 Mon Sep 17 00:00:00 2001 From: "sumathi.thirumani" Date: Fri, 29 Dec 2023 12:55:22 -0800 Subject: [PATCH 27/45] redaction layer changes + edit bug --- api/reviewer_api/models/AnnotationSections.py | 51 +++++---- api/reviewer_api/models/Annotations.py | 51 +-------- api/reviewer_api/models/RedactionLayers.py | 16 +++ .../resources/foiflowmasterdata.py | 101 ------------------ api/reviewer_api/resources/redaction.py | 6 +- .../services/annotationservice.py | 33 +----- api/reviewer_api/services/radactionservice.py | 15 +-- .../services/redactionlayerservice.py | 9 ++ api/tests/services/test_redaction_services.py | 6 -- .../services/docReviewerService.tsx | 3 +- web/src/components/FOI/Home/Redlining.js | 2 +- 11 files changed, 73 insertions(+), 220 deletions(-) diff --git a/api/reviewer_api/models/AnnotationSections.py b/api/reviewer_api/models/AnnotationSections.py index 4848c850e..d7d460c9a 100644 --- a/api/reviewer_api/models/AnnotationSections.py +++ b/api/reviewer_api/models/AnnotationSections.py @@ -32,11 +32,11 @@ class AnnotationSection(db.Model): ) @classmethod - def __getsectionkey(cls, _annotationname): + def __getsectionkey(cls, _annotationname, _redactionlayerid): try: return ( db.session.query(AnnotationSection.id, AnnotationSection.version) - .filter(and_(AnnotationSection.annotationname == _annotationname)) + .filter(and_(AnnotationSection.annotationname == _annotationname, AnnotationSection.redactionlayerid == _redactionlayerid)) .order_by(AnnotationSection.version.desc()) .first() ) @@ -46,14 +46,14 @@ def __getsectionkey(cls, _annotationname): db.session.close() @classmethod - def __getbulksectionkey(cls, _annotationnames): + def __getbulksectionkey(cls, _annotationnames, _redactionlayerid): apks = {} try: sql = """select distinct on (annotationname) "annotationname", "version", id - from "AnnotationSections" as2 where annotationname IN :annotationnames + from "AnnotationSections" as2 where annotationname IN :annotationnames and redactionlayerid = :redactionlayerid order by annotationname, version desc;""" rs = db.session.execute( - text(sql), {"annotationnames": tuple(_annotationnames)} + text(sql), {"annotationnames": tuple(_annotationnames), "redactionlayerid": _redactionlayerid} ) for row in rs: apks[row["annotationname"]] = { @@ -91,25 +91,25 @@ def copyannotationsections(cls, ministryrequestid, sourcelayers, targetlayer) -> @classmethod def savesections( - cls, annots, _foiministryrequestid, userinfo + cls, annots, redactionlayerid, _foiministryrequestid, userinfo ) -> DefaultMethodResult: begin, size, limit = getbatchconfig() if len(annots) > 0 and len(annots) < begin: - return cls.__chunksavesections(annots, _foiministryrequestid, userinfo) + return cls.__chunksavesections(annots, redactionlayerid, _foiministryrequestid, userinfo) elif len(annots) >= begin and len(annots) <= limit: - return cls.__bulksavesections(annots, _foiministryrequestid, userinfo, size) + return cls.__bulksavesections(annots, redactionlayerid, _foiministryrequestid, userinfo, size) else: return DefaultMethodResult(False, "Invalid Annotation Section Request", -1) @classmethod def __chunksavesections( - cls, annots, _foiministryrequestid, userinfo + cls, annots, redactionlayerid, _foiministryrequestid, userinfo ) -> DefaultMethodResult: successections = [] failedsections = [] try: for annot in annots: - resp = cls.__savesection(annot, _foiministryrequestid, userinfo) + resp = cls.__savesection(annot, redactionlayerid, _foiministryrequestid, userinfo) if resp.success == True: successections.append(annot["name"]) else: @@ -129,7 +129,7 @@ def __chunksavesections( @classmethod def __bulksavesections( - cls, annots, _foiministryrequestid, userinfo, size=100 + cls, annots, redactionlayerid, _foiministryrequestid, userinfo, size=100 ) -> DefaultMethodResult: idxannots = [] try: @@ -148,11 +148,11 @@ def __bulksavesections( @classmethod def __savesection( - cls, annot, _foiministryrequestid, userinfo + cls, annot, redactionlayerid, _foiministryrequestid, userinfo ) -> DefaultMethodResult: - sectkey = cls.__getsectionkey(annot["name"]) + sectkey = cls.__getsectionkey(annot["name"], redactionlayerid) if sectkey is None: - return cls.__newsection(annot, _foiministryrequestid, userinfo) + return cls.__newsection(annot, redactionlayerid, _foiministryrequestid, userinfo) else: return cls.__updatesection( annot, _foiministryrequestid, userinfo, sectkey[0], sectkey[1] @@ -160,7 +160,7 @@ def __savesection( @classmethod def __newsection( - cls, annot, _foiministryrequestid, userinfo + cls, annot, redactionlayerid, _foiministryrequestid, userinfo ) -> DefaultMethodResult: try: values = [ @@ -171,6 +171,7 @@ def __newsection( "version": 1, "createdby": userinfo, "isactive": True, + "redactionlayerid": redactionlayerid, } ] insertstmt = insert(AnnotationSection).values(values) @@ -178,6 +179,7 @@ def __newsection( index_elements=[ AnnotationSection.annotationname, AnnotationSection.version, + AnnotationSection.redactionlayerid ], set_={ "isactive": False, @@ -245,7 +247,7 @@ def __bulknewsections(cls, annots, _pkvannots, _foiministryrequestid, userinfo): @classmethod def __updatesection( - cls, annot, _foiministryrequestid, userinfo, id=None, version=None + cls, annot, _redactionlayerid, _foiministryrequestid, userinfo, id=None, version=None ) -> DefaultMethodResult: try: if id is None or version is None: @@ -261,13 +263,14 @@ def __updatesection( "createdby": userinfo, "isactive": True, "version": version + 1, + "redactionlayerid": _redactionlayerid } ] insertstmt = insert(AnnotationSection).values(values) secttmt = insertstmt.on_conflict_do_nothing() db.session.execute(secttmt) db.session.commit() - cls.__archivesection(annot["name"], userinfo) + cls.__archivesection(annot["name"], _redactionlayerid, userinfo) return DefaultMethodResult( True, "Annotation sections are updated", annot["name"] ) @@ -277,22 +280,23 @@ def __updatesection( db.session.close() @classmethod - def __archivesection(cls, _annotationname, userinfo) -> DefaultMethodResult: - return cls.__bulkarchivesections([_annotationname], userinfo) + def __archivesection(cls, _annotationname, _redactionlayerid, userinfo) -> DefaultMethodResult: + return cls.__bulkarchivesections([_annotationname], _redactionlayerid, userinfo) @classmethod - def __bulkarchivesections(cls, idxannots, userinfo) -> DefaultMethodResult: + def __bulkarchivesections(cls, idxannots, _redactionlayerid, userinfo) -> DefaultMethodResult: try: sql = """update "AnnotationSections" a set isactive = false, updatedby = :userinfo, updated_at=now() from (select distinct on (annotationname) "annotationname", "version", id from "AnnotationSections" where annotationname in :idxannots order by annotationname, version desc) as b where a.annotationname = b.annotationname + and a.redactionlayerid = :redactionlayerid and a."version" < b.version and a.isactive = true;""" db.session.execute( text(sql), - {"idxannots": tuple(idxannots), "userinfo": json.dumps(userinfo)}, + {"idxannots": tuple(idxannots), "userinfo": json.dumps(userinfo), "redactionlayerid": _redactionlayerid}, ) db.session.commit() return DefaultMethodResult( @@ -352,7 +356,7 @@ def getsectionmapping(cls, documentid, documentversion): return mapping @classmethod - def getsectionmappingbyrequestid(cls, ministryrequestid): + def getsectionmappingbyrequestid(cls, ministryrequestid, redactionlayerid): mapping = [] try: sql = """select as2.annotationname , @@ -365,9 +369,10 @@ def getsectionmappingbyrequestid(cls, ministryrequestid): order by docs.documentid, docs.version desc) as d on (d.documentid = a.documentid and d.version = a.documentversion) where as2.annotationname = a.annotationname and a.isactive = true + and as2.redactionlayerid = a.redactionlayerid and as2.redactionlayerid = :redactionlayerid and as2.foiministryrequestid = :ministryrequestid and as2.isactive = true; """ - rs = db.session.execute(text(sql), {"ministryrequestid": ministryrequestid}) + rs = db.session.execute(text(sql), {"ministryrequestid": ministryrequestid, "redactionlayerid": redactionlayerid}) for row in rs: mapping.append( diff --git a/api/reviewer_api/models/Annotations.py b/api/reviewer_api/models/Annotations.py index 2d2252e40..84829b462 100644 --- a/api/reviewer_api/models/Annotations.py +++ b/api/reviewer_api/models/Annotations.py @@ -39,28 +39,7 @@ class Annotation(db.Model): "RedactionLayer", backref=backref("RedactionLayers"), uselist=False ) - @classmethod - def getannotations(cls, _documentid, _documentversion): - try: - annotation_schema = AnnotationSchema(many=True) - query = ( - db.session.query(Annotation) - .filter( - and_( - Annotation.documentid == _documentid, - Annotation.documentversion == _documentversion, - Annotation.isactive == True, - ) - ) - .order_by(Annotation.annotationid.asc()) - .all() - ) - return annotation_schema.dump(query) - except Exception as ex: - logging.error(ex) - finally: - db.session.close() - + @classmethod def getrequestannotations(cls, ministryrequestid, mappedlayerids): sql = """select a.* @@ -225,27 +204,7 @@ def getredactionsbypage( finally: db.session.close() - @classmethod - def getannotationinfo(cls, _documentid, _documentversion): - try: - annotation_schema = AnnotationSchema(many=True) - query = ( - db.session.query(Annotation.annotationname) - .filter( - and_( - Annotation.documentid == _documentid, - Annotation.documentversion == _documentversion, - Annotation.isactive == True, - ) - ) - .order_by(Annotation.annotationid.asc()) - .all() - ) - return annotation_schema.dump(query) - except Exception as ex: - logging.error(ex) - finally: - db.session.close() + @classmethod def getannotationid(cls, _annotationname): @@ -266,11 +225,11 @@ def getannotationid(cls, _annotationname): db.session.close() @classmethod - def __getannotationkey(cls, _annotationname): + def __getannotationkey(cls, _annotationname, _redactionlayerid): try: return ( db.session.query(Annotation.annotationid, Annotation.version) - .filter(and_(Annotation.annotationname == _annotationname)) + .filter(and_(Annotation.annotationname == _annotationname, Annotation.redactionlayerid == _redactionlayerid)) .order_by(Annotation.version.desc()) .first() ) @@ -390,7 +349,7 @@ def __bulksaveannotations( @classmethod def __saveannotation(cls, annot, redactionlayerid, userinfo) -> DefaultMethodResult: - annotkey = cls.__getannotationkey(annot["name"]) + annotkey = cls.__getannotationkey(annot["name"], redactionlayerid) if annotkey is None: return cls.__newannotation(annot, redactionlayerid, userinfo) else: diff --git a/api/reviewer_api/models/RedactionLayers.py b/api/reviewer_api/models/RedactionLayers.py index 827bcadbb..b2e2c8eae 100644 --- a/api/reviewer_api/models/RedactionLayers.py +++ b/api/reviewer_api/models/RedactionLayers.py @@ -5,6 +5,7 @@ import logging + class RedactionLayer(db.Model): __tablename__ = "RedactionLayers" # Defining the columns @@ -65,6 +66,21 @@ def getredlineredactionlayer(cls): finally: db.session.close() + @classmethod + def getlayers(cls): + try: + layer_schema = RedactionLayerSchema(many=True) + query = ( + db.session.query(RedactionLayer) + .filter_by(isactive=True) + .all() + ) + return layer_schema.dump(query) + except Exception as ex: + logging.error(ex) + finally: + db.session.close() + class RedactionLayerSchema(ma.Schema): class Meta: diff --git a/api/reviewer_api/resources/foiflowmasterdata.py b/api/reviewer_api/resources/foiflowmasterdata.py index 7066cd112..abc7dca53 100644 --- a/api/reviewer_api/resources/foiflowmasterdata.py +++ b/api/reviewer_api/resources/foiflowmasterdata.py @@ -154,107 +154,6 @@ def post(): except BusinessException as exception: return {"status": exception.status_code, "message": exception.message}, 500 -""" -@cors_preflight("POST,OPTIONS") -@API.route("/foiflow/oss/presigned/redline/") -class FOIFlowS3PresignedRedline(Resource): - @staticmethod - @TRACER.trace() - @cross_origin(origins=allowedorigins()) - @auth.require - @auth.ismemberofgroups(getrequiredmemberships()) - def post(ministryrequestid): - try: - data = request.get_json() - documentmapper = redactionservice().getdocumentmapper( - data["divdocumentList"][0]["documentlist"][0]["filepath"].split("/")[3] - ) - attribute = json.loads(documentmapper["attributes"]) - - # current_app.logger.debug("Inside Presigned api!!") - formsbucket = documentmapper["bucket"] - accesskey = attribute["s3accesskey"] - secretkey = attribute["s3secretkey"] - s3client = boto3.client( - "s3", - config=Config(signature_version="s3v4"), - endpoint_url="https://{0}/".format(s3host), - aws_access_key_id=accesskey, - aws_secret_access_key=secretkey, - region_name=s3region, - ) - for div in data["divdocumentList"]: - if len(div["documentlist"]) > 0: - division_name = div["documentlist"][0]["divisions"][0]["name"] - # generate save url for stitched file - filepathlist = div["documentlist"][0]["filepath"].split("/")[4:] - filepath_put = "{0}/redline/{1}/{0} - Redline - {1}.pdf".format( - filepathlist[0], division_name - ) - - # filename_put, file_extension_put = os.path.splitext(filepath_put) - # filepath_put = filename_put+'.pdf' - s3path_save = s3client.generate_presigned_url( - ClientMethod="get_object", - Params={ - "Bucket": formsbucket, - "Key": "{0}".format(filepath_put), - "ResponseContentType": "application/pdf", - }, - ExpiresIn=3600, - HttpMethod="PUT", - ) - - # for save/put - stitch by division - div["s3path_save"] = s3path_save - - # retrieve annotations for stitch by division - div[ - "annotationXML" - ] = redactionservice().getannotationsbyrequestdivision( - ministryrequestid, div["divisionid"] - ) - - for doc in div["documentlist"]: - filepathlist = doc["filepath"].split("/")[4:] - - # for load/get - filepath_get = "/".join(filepathlist) - filename_get, file_extension_get = os.path.splitext( - filepath_get - ) - file_extension_get = ( - file_extension_get.replace(".", "") - if file_extension_get.lower() in imageextensions - else "pdf" - ) - - doc["s3path_load"] = s3client.generate_presigned_url( - ClientMethod="get_object", - Params={ - "Bucket": formsbucket, - "Key": "{0}.{1}".format( - filename_get, file_extension_get - ), - "ResponseContentType": "{0}/{1}".format( - "image" - if file_extension_get.lower() in imageextensions - else "application", - file_extension_get, - ), - }, - ExpiresIn=3600, - HttpMethod="GET", - ) - elif len(div["incompatableList"]) > 0: - filepathlist = div["incompatableList"][0]["filepath"].split("/")[4:] - data["requestnumber"] = filepathlist[0] - data["bcgovcode"] = formsbucket.split("-")[0] - return json.dumps(data), 200 - except BusinessException as exception: - return {"status": exception.status_code, "message": exception.message}, 500 -""" - @cors_preflight("POST,OPTIONS") @API.route("/foiflow/oss/presigned///") class FOIFlowS3PresignedRedline(Resource): diff --git a/api/reviewer_api/resources/redaction.py b/api/reviewer_api/resources/redaction.py index caf2b4e90..865adf024 100644 --- a/api/reviewer_api/resources/redaction.py +++ b/api/reviewer_api/resources/redaction.py @@ -223,7 +223,7 @@ def post(requestid, redactionlayerid): @cors_preflight("GET,OPTIONS") -@API.route("/annotation//info") +@API.route("/annotation///info") class AnnotationMetadata(Resource): """Retrieves annotations for a document""" @@ -231,9 +231,9 @@ class AnnotationMetadata(Resource): @TRACER.trace() @cross_origin(origins=allowedorigins()) @auth.require - def get(ministryrequestid): + def get(ministryrequestid, redactionlayer): try: - result = redactionservice().getannotationinfobyrequest(ministryrequestid) + result = redactionservice().getannotationinfobyrequest(ministryrequestid, redactionlayer) return json.dumps(result), 200 except KeyError as error: return {'status': False, 'message': CUSTOM_KEYERROR_MESSAGE + str(error)}, 400 diff --git a/api/reviewer_api/services/annotationservice.py b/api/reviewer_api/services/annotationservice.py index 2edaadafa..98609db93 100644 --- a/api/reviewer_api/services/annotationservice.py +++ b/api/reviewer_api/services/annotationservice.py @@ -21,14 +21,7 @@ class annotationservice: """FOI Annotation management service""" - - def getannotations(self, documentid, documentversion, pagenumber): - annotations = Annotation.getannotations(documentid, documentversion) - annotationlist = [] - for entry in annotations: - annotationlist.append(entry["annotation"]) - return self.__generateannotationsxml(annotationlist) - + def getrequestannotations(self, ministryrequestid, mappedlayerids): annotations = Annotation.getrequestannotations( ministryrequestid, mappedlayerids @@ -91,28 +84,11 @@ def getrequestdivisionannotations(self, ministryrequestid, divisionid): # annotationobj[documentid] = self.__generateannotationsxml(annotationobj[documentid]) return annotationobj - def getannotationinfo(self, documentid, documentversion, pagenumber): - annotations = Annotation.getannotationinfo(documentid, documentversion) - annotationsections = AnnotationSection.getsectionmapping( - documentid, documentversion - ) - annotationlist = [] - for entry in annotations: - section = self.__getsection(annotationsections, entry["annotationname"]) - if self.__issection(annotationsections, entry["annotationname"]) == False: - if section is not None: - entry["sections"] = { - "annotationname": section["sectionannotationname"], - "ids": list( - map(lambda id: id["id"], json.loads(section["ids"])) - ), - } - annotationlist.append(entry) - return annotationlist - def getrequestannotationinfo(self, ministryrequestid): + def getrequestannotationinfo(self, ministryrequestid, redactionlayer): + redactionlayerid = redactionlayerservice().getredactionlayerid(redactionlayer) annotationsections = AnnotationSection.getsectionmappingbyrequestid( - ministryrequestid + ministryrequestid, redactionlayerid ) for entry in annotationsections: entry["sections"] = { @@ -167,6 +143,7 @@ def saveannotation(self, annotationschema, userinfo): if "sections" in annotationschema: sectionresponse = AnnotationSection.savesections( annots, + _redactionlayerid, annotationschema["sections"]["foiministryrequestid"], userinfo, ) diff --git a/api/reviewer_api/services/radactionservice.py b/api/reviewer_api/services/radactionservice.py index f29444f40..aa7bcddb3 100644 --- a/api/reviewer_api/services/radactionservice.py +++ b/api/reviewer_api/services/radactionservice.py @@ -19,11 +19,7 @@ class redactionservice: zipperstreamkey = getenv("ZIPPER_STREAM_KEY") - def getannotations(self, documentid, documentversion, pagenumber): - return annotationservice().getannotations( - documentid, documentversion, pagenumber - ) - + def getannotationsbyrequest( self, ministryrequestid, _redactionlayer, page=None, size=None ): @@ -49,13 +45,10 @@ def getannotationsbyrequestdivision(self, ministryrequestid, divisionid): ministryrequestid, divisionid ) - def getannotationinfo(self, documentid, documentversion, pagenumber): - return annotationservice().getannotationinfo( - documentid, documentversion, pagenumber - ) - def getannotationinfobyrequest(self, requestid): - return annotationservice().getrequestannotationinfo(requestid) + + def getannotationinfobyrequest(self, requestid, redactionlayer): + return annotationservice().getrequestannotationinfo(requestid, redactionlayer) def copyannotation(self, ministryrequestid, targetlayer): sourcelayers = [] diff --git a/api/reviewer_api/services/redactionlayerservice.py b/api/reviewer_api/services/redactionlayerservice.py index 33d957cf5..072fc4bf5 100644 --- a/api/reviewer_api/services/redactionlayerservice.py +++ b/api/reviewer_api/services/redactionlayerservice.py @@ -12,6 +12,15 @@ def getdefaultredactionlayerid(self): _redactionlayer = RedactionLayer.getredlineredactionlayer() return _redactionlayer["redactionlayerid"] + def getredactionlayerid(self, name): + _name = self.__normalise(name) + layers = RedactionLayer.getlayers() + for layer in layers: + if (self.__normalise(layer['name']) == _name): + return layer["redactionlayerid"] + return 0 + + def getmappedredactionlayers(self, redactionlayer): mpxlayers = [] mpxlayers.append(redactionlayer["redactionlayerid"]) diff --git a/api/tests/services/test_redaction_services.py b/api/tests/services/test_redaction_services.py index 1699b52a2..d5b34dcb3 100644 --- a/api/tests/services/test_redaction_services.py +++ b/api/tests/services/test_redaction_services.py @@ -19,12 +19,6 @@ def test_save_annotation(session): pytest.approxrequestidtoupdate = requestid assert response.success == True -def test_get_annotations(session): - documentid = 1 - documentversion = 1 - pagenumber = 1 - queue = redactionservice().getannotations(documentid, documentversion, pagenumber) - assert len(queue) > 0 def test_deactive_annotation(session): annotationname = 'test123' diff --git a/web/src/apiManager/services/docReviewerService.tsx b/web/src/apiManager/services/docReviewerService.tsx index a67945ff6..657409e2d 100644 --- a/web/src/apiManager/services/docReviewerService.tsx +++ b/web/src/apiManager/services/docReviewerService.tsx @@ -82,10 +82,11 @@ export const fetchDocumentAnnotations = ( }; export const fetchAnnotationsInfo = ( ministryrequestid: number, + redactionlayer: string, //callback: any, errorCallback: any ) => { - let apiUrlGet: string = `${API.DOCREVIEWER_ANNOTATION}/${ministryrequestid}/info` + let apiUrlGet: string = `${API.DOCREVIEWER_ANNOTATION}/${ministryrequestid}/${redactionlayer}/info` httpGETRequest(apiUrlGet, {}, UserService.getToken()) .then((res:any) => { diff --git a/web/src/components/FOI/Home/Redlining.js b/web/src/components/FOI/Home/Redlining.js index e65a708b8..56903eda0 100644 --- a/web/src/components/FOI/Home/Redlining.js +++ b/web/src/components/FOI/Home/Redlining.js @@ -514,7 +514,7 @@ const Redlining = React.forwardRef( if (Object.entries(individualDoc["file"])?.length <= 0) individualDoc = localDocumentInfo; - fetchAnnotationsInfo(requestid, (error) => { + fetchAnnotationsInfo(requestid, currentLayer.name.toLowerCase(), (error) => { //problem console.log("Error:", error); }); From f9ae998a256e193781fb031643a17ebcbd5ecee5 Mon Sep 17 00:00:00 2001 From: "sumathi.thirumani" Date: Fri, 29 Dec 2023 14:16:24 -0800 Subject: [PATCH 28/45] Removed unused functional methods + edit bug fix changes. --- api/reviewer_api/models/Annotations.py | 68 ++----------------- .../services/annotationservice.py | 14 +--- api/reviewer_api/services/radactionservice.py | 8 +-- 3 files changed, 8 insertions(+), 82 deletions(-) diff --git a/api/reviewer_api/models/Annotations.py b/api/reviewer_api/models/Annotations.py index 84829b462..3099d5b6b 100644 --- a/api/reviewer_api/models/Annotations.py +++ b/api/reviewer_api/models/Annotations.py @@ -109,6 +109,7 @@ def get_request_annotations_pagination( ) result = _subquery_annotation.paginate(page=page, per_page=size) return result + @classmethod def get_document_annotations(cls, ministryrequestid, mappedlayerids, documentid): try: @@ -137,45 +138,7 @@ def get_document_annotations(cls, ministryrequestid, mappedlayerids, documentid) finally: db.session.close() - @classmethod - def getrequestdivisionannotations(cls, ministryrequestid, divisionid): - sql = """ - select a.* - from "Annotations" a - join ( - select distinct on (docs.documentid) docs.* - from "Documents" docs - where docs.foiministryrequestid = :ministryrequestid - order by docs.documentid, docs.version desc - ) as d on (d.documentid = a.documentid and d.version = a.documentversion) - inner join "DocumentMaster" dm on dm.documentmasterid = d.documentmasterid or dm.processingparentid = d.documentmasterid - inner join "DocumentAttributes" da - on (da.documentmasterid = dm.documentmasterid or da.documentmasterid = dm.processingparentid) - and da.isactive = true - and (da.attributes ->> 'divisions')::jsonb @> '[{"divisionid": :divisionid}]' - and a.isactive = true - """ - rs = db.session.execute( - text(sql), - {"ministryrequestid": ministryrequestid, "divisionid": divisionid}, - ) - db.session.close() - return [ - { - "annotationid": row["annotationid"], - "annotationname": row["annotationname"], - "documentid": row["documentid"], - "documentversion": row["documentversion"], - "annotation": row["annotation"], - "pagenumber": row["pagenumber"], - "isactive": row["isactive"], - "createdby": row["createdby"], - "created_at": row["created_at"], - "updatedby": row["updatedby"], - "updated_at": row["updated_at"], - } - for row in rs - ] + @classmethod def getredactionsbypage( @@ -205,25 +168,6 @@ def getredactionsbypage( db.session.close() - - @classmethod - def getannotationid(cls, _annotationname): - try: - return ( - db.session.query(Annotation.annotationid) - .filter( - and_( - Annotation.annotationname == _annotationname, - Annotation.isactive == True, - ) - ) - .first()[0] - ) - except Exception as ex: - logging.error(ex) - finally: - db.session.close() - @classmethod def __getannotationkey(cls, _annotationname, _redactionlayerid): try: @@ -239,14 +183,14 @@ def __getannotationkey(cls, _annotationname, _redactionlayerid): db.session.close() @classmethod - def __getbulkannotationkey(cls, _annotationnames): + def __getbulkannotationkey(cls, _annotationnames, _redactionlayerid): apks = {} try: sql = """select distinct on (annotationname) "annotationname", "version", annotationid - from "Annotations" where annotationname IN :annotationnames + from "Annotations" where annotationname IN :annotationnames and redactionlayerid = :redactionlayerid order by annotationname, version desc;""" rs = db.session.execute( - text(sql), {"annotationnames": tuple(_annotationnames)} + text(sql), {"annotationnames": tuple(_annotationnames), "redactionlayerid": _redactionlayerid} ) for row in rs: apks[row["annotationname"]] = { @@ -337,7 +281,7 @@ def __bulksaveannotations( wkannots = split(annots, size) for wkannot in wkannots: annotnames = [d["name"] for d in wkannot] - _pkvannots = cls.__getbulkannotationkey(annotnames) + _pkvannots = cls.__getbulkannotationkey(annotnames, redactionlayerid) cls.__bulknewannotations( wkannot, _pkvannots, redactionlayerid, userinfo ) diff --git a/api/reviewer_api/services/annotationservice.py b/api/reviewer_api/services/annotationservice.py index 98609db93..7aab2c80c 100644 --- a/api/reviewer_api/services/annotationservice.py +++ b/api/reviewer_api/services/annotationservice.py @@ -70,19 +70,7 @@ def __formatannotations(self, annotations): ) return annotationobj - def getrequestdivisionannotations(self, ministryrequestid, divisionid): - annotations = Annotation.getrequestdivisionannotations( - ministryrequestid, divisionid - ) - annotationobj = {} - for annot in annotations: - if annot["documentid"] not in annotationobj: - annotationobj[annot["documentid"]] = [] - annotationobj[annot["documentid"]].append(annot["annotation"]) - for documentid in annotationobj: - annotationobj[documentid] = annotationobj[documentid] - # annotationobj[documentid] = self.__generateannotationsxml(annotationobj[documentid]) - return annotationobj + def getrequestannotationinfo(self, ministryrequestid, redactionlayer): diff --git a/api/reviewer_api/services/radactionservice.py b/api/reviewer_api/services/radactionservice.py index aa7bcddb3..b4bb21ccd 100644 --- a/api/reviewer_api/services/radactionservice.py +++ b/api/reviewer_api/services/radactionservice.py @@ -40,13 +40,7 @@ def getannotationsbydocument(self, ministryrequestid, _redactionlayer, documenti ) return annotationservice().getdocumentannotations(ministryrequestid, mappedlayerids, documentid) - def getannotationsbyrequestdivision(self, ministryrequestid, divisionid): - return annotationservice().getrequestdivisionannotations( - ministryrequestid, divisionid - ) - - - + def getannotationinfobyrequest(self, requestid, redactionlayer): return annotationservice().getrequestannotationinfo(requestid, redactionlayer) From ee0d1373e0860f782bd622fb8b7df11198886c07 Mon Sep 17 00:00:00 2001 From: "sumathi.thirumani" Date: Fri, 29 Dec 2023 16:31:15 -0800 Subject: [PATCH 29/45] Removed unused functional methods + edit bug fix changes. --- api/reviewer_api/models/AnnotationSections.py | 78 +++++-------------- api/reviewer_api/models/DocumentPageflags.py | 6 +- .../resources/documentpageflag.py | 6 +- api/reviewer_api/resources/pageflag.py | 6 +- api/reviewer_api/resources/section.py | 6 +- .../services/annotationservice.py | 18 +---- .../services/documentpageflagservice.py | 12 +-- api/reviewer_api/services/pageflagservice.py | 4 +- api/reviewer_api/services/sectionservice.py | 6 +- .../services/docReviewerService.tsx | 12 +-- .../components/FOI/Home/DocumentSelector.tsx | 4 +- web/src/components/FOI/Home/Home.js | 2 +- web/src/components/FOI/Home/Redlining.js | 16 ++-- 13 files changed, 64 insertions(+), 112 deletions(-) diff --git a/api/reviewer_api/models/AnnotationSections.py b/api/reviewer_api/models/AnnotationSections.py index d7d460c9a..a6922dcf3 100644 --- a/api/reviewer_api/models/AnnotationSections.py +++ b/api/reviewer_api/models/AnnotationSections.py @@ -136,11 +136,11 @@ def __bulksavesections( wkannots = split(annots, size) for wkannot in wkannots: annotnames = [d["name"] for d in wkannot] - _pkvsections = cls.__getbulksectionkey(annotnames) + _pkvsections = cls.__getbulksectionkey(annotnames, redactionlayerid) cls.__bulknewsections( - wkannot, _pkvsections, _foiministryrequestid, userinfo + wkannot, _pkvsections, redactionlayerid, _foiministryrequestid, userinfo ) - cls.__bulkarchivesections(annotnames, userinfo) + cls.__bulkarchivesections(annotnames, redactionlayerid, userinfo) idxannots.extend(annotnames) return DefaultMethodResult(True, "Annotations added", ",".join(idxannots)) except Exception as ex: @@ -155,7 +155,7 @@ def __savesection( return cls.__newsection(annot, redactionlayerid, _foiministryrequestid, userinfo) else: return cls.__updatesection( - annot, _foiministryrequestid, userinfo, sectkey[0], sectkey[1] + annot, redactionlayerid, _foiministryrequestid, userinfo, sectkey[0], sectkey[1] ) @classmethod @@ -199,6 +199,7 @@ def __newsection( if idxsect["isactive"] == False: return cls.__updatesection( annot, + redactionlayerid, _foiministryrequestid, userinfo, idxsect["id"], @@ -213,7 +214,7 @@ def __newsection( db.session.close() @classmethod - def __bulknewsections(cls, annots, _pkvannots, _foiministryrequestid, userinfo): + def __bulknewsections(cls, annots, _pkvannots, redactionlayerid, _foiministryrequestid, userinfo): datalist = [] idxannots = [] try: @@ -228,7 +229,8 @@ def __bulknewsections(cls, annots, _pkvannots, _foiministryrequestid, userinfo): "section": annot["sectionsschema"], "createdby": userinfo, "isactive": True, - "version": pkkey["version"] + 1 + "version": pkkey["version"] + 1, + "redactionlayerid": redactionlayerid if pkkey is not None and "version" in pkkey else 1, "id": pkkey["id"] @@ -308,14 +310,14 @@ def __bulkarchivesections(cls, idxannots, _redactionlayerid, userinfo) -> Defaul db.session.close() @classmethod - def bulkdeletesections(cls, idxannots, userinfo) -> DefaultMethodResult: + def bulkdeletesections(cls, idxannots, redactionlayerid, userinfo) -> DefaultMethodResult: try: sql = """update "AnnotationSections" a set isactive = false, updatedby = :userinfo, updated_at=now() - where a.annotationname in :idxannots + where a.annotationname in :idxannots and redactionlayerid = :redactionlayerid and a.isactive = true;""" db.session.execute( text(sql), - {"idxannots": tuple(idxannots), "userinfo": json.dumps(userinfo)}, + {"idxannots": tuple(idxannots), "redactionlayerid": redactionlayerid, "userinfo": json.dumps(userinfo)}, ) db.session.commit() return DefaultMethodResult( @@ -326,35 +328,6 @@ def bulkdeletesections(cls, idxannots, userinfo) -> DefaultMethodResult: finally: db.session.close() - @classmethod - def getsectionmapping(cls, documentid, documentversion): - mapping = [] - try: - sql = """select as2.annotationname as "sectionannotationname", - cast("section" AS json) ->> 'redactannotation' as redactannotation, - cast("section" AS json) ->> 'ids' as ids - from "AnnotationSections" as2, "Annotations" a where as2.annotationname = a.annotationname and as2.isactive = true - and a.isactive = true and a.documentid = :documentid and a.documentversion = :documentversion; - """ - rs = db.session.execute( - text(sql), - {"documentid": documentid, "documentversion": documentversion}, - ) - - for row in rs: - mapping.append( - { - "sectionannotationname": row["sectionannotationname"], - "redactannotation": row["redactannotation"], - "ids": row["ids"], - } - ) - except Exception as ex: - logging.error(ex) - finally: - db.session.close() - return mapping - @classmethod def getsectionmappingbyrequestid(cls, ministryrequestid, redactionlayerid): mapping = [] @@ -388,28 +361,10 @@ def getsectionmappingbyrequestid(cls, ministryrequestid, redactionlayerid): db.session.close() return mapping - @classmethod - def get_by_annotationame(cls, _annotationname): - try: - annotation_section_schema = AnnotationSectionSchema(many=False) - query = ( - db.session.query(AnnotationSection) - .filter( - and_( - AnnotationSection.annotationname == _annotationname, - AnnotationSection.isactive == True, - ) - ) - .first() - ) - return annotation_section_schema.dump(query) - except Exception as ex: - logging.error(ex) - finally: - db.session.close() + @classmethod - def get_by_ministryid(cls, ministryrequestid): + def get_by_ministryid(cls, ministryrequestid, redactionlayerid): try: annotation_section_schema = AnnotationSectionSchema(many=True) redaction = aliased(Annotation) @@ -417,18 +372,21 @@ def get_by_ministryid(cls, ministryrequestid): db.session.query(AnnotationSection) .join( Annotation, - Annotation.annotationname == AnnotationSection.annotationname, + Annotation.annotationname == AnnotationSection.annotationname ) .join( redaction, redaction.annotationname - == cast(AnnotationSection.section, JSON)["redactannotation"].astext, + == cast(AnnotationSection.section, JSON)["redactannotation"].astext ) .filter( and_( AnnotationSection.foiministryrequestid == ministryrequestid, AnnotationSection.isactive == True, Annotation.isactive == True, + Annotation.redactionlayerid == AnnotationSection.redactionlayerid, + Annotation.redactionlayerid == redaction.redactionlayerid, + Annotation.redactionlayerid == redactionlayerid, redaction.isactive == True, ) ) diff --git a/api/reviewer_api/models/DocumentPageflags.py b/api/reviewer_api/models/DocumentPageflags.py index 2df7839d4..d65a9c009 100644 --- a/api/reviewer_api/models/DocumentPageflags.py +++ b/api/reviewer_api/models/DocumentPageflags.py @@ -199,14 +199,14 @@ def getpageflag_by_request(cls, _foiministryrequestid, redactionlayerid): return pageflags @classmethod - def getpublicbody_by_request(cls, _foiministryrequestid): + def getpublicbody_by_request(cls, _foiministryrequestid, _redactionlayerid): pageflags = [] try: sql = """select distinct on (documentid) documentid, attributes from "DocumentPageflags" dp - where foiministryrequestid = :foiministryrequestid order by documentid, documentversion desc; + where foiministryrequestid = :foiministryrequestid and redactionlayerid = :redactionlayerid order by documentid, documentversion desc; """ rs = db.session.execute( - text(sql), {"foiministryrequestid": _foiministryrequestid} + text(sql), {"foiministryrequestid": _foiministryrequestid, "redactionlayerid": _redactionlayerid} ) for row in rs: diff --git a/api/reviewer_api/resources/documentpageflag.py b/api/reviewer_api/resources/documentpageflag.py index c74f4b612..35deffc0b 100644 --- a/api/reviewer_api/resources/documentpageflag.py +++ b/api/reviewer_api/resources/documentpageflag.py @@ -75,7 +75,7 @@ def get(requestid, documentid, documentversion, redactionlayerid): @cors_preflight('GET,OPTIONS') -@API.route('/ministryrequest//pageflag/') +@API.route('/ministryrequest//pageflag/') class GetDocumentPageflag(Resource): """Get document page flag list. """ @@ -84,9 +84,9 @@ class GetDocumentPageflag(Resource): @cross_origin(origins=allowedorigins()) @auth.require @auth.ismemberofgroups(getrequiredmemberships()) - def get(requestid, redactionlayerid): + def get(requestid, redactionlayer): try: - result = documentpageflagservice().getpageflags(requestid, redactionlayerid) + result = documentpageflagservice().getpageflags(requestid, redactionlayer) return json.dumps(result), 200 except KeyError as error: return {'status': False, 'message': CUSTOM_KEYERROR_MESSAGE + str(error)}, 400 diff --git a/api/reviewer_api/resources/pageflag.py b/api/reviewer_api/resources/pageflag.py index bb34bc4e1..00dfaa026 100644 --- a/api/reviewer_api/resources/pageflag.py +++ b/api/reviewer_api/resources/pageflag.py @@ -44,7 +44,7 @@ def get(): @cors_preflight('GET,OPTIONS') -@API.route('/pageflags/ministryrequest/') +@API.route('/pageflags/ministryrequest//') class GetSections(Resource): """Get Pageflags list. """ @@ -52,9 +52,9 @@ class GetSections(Resource): @cross_origin(origins=allowedorigins()) @auth.require @auth.ismemberofgroups(getrequiredmemberships()) - def get(requestid): + def get(requestid, redactionlayer): try: - data = pageflagservice().getpageflag_by_request(requestid) + data = pageflagservice().getpageflag_by_request(requestid, redactionlayer) return json.dumps(data) , 200 except BusinessException as exception: return {'status': exception.status_code, 'message':exception.message}, 500 \ No newline at end of file diff --git a/api/reviewer_api/resources/section.py b/api/reviewer_api/resources/section.py index ef584c479..411fbda28 100644 --- a/api/reviewer_api/resources/section.py +++ b/api/reviewer_api/resources/section.py @@ -47,7 +47,7 @@ def get(): @cors_preflight('GET,OPTIONS') -@API.route('/sections/ministryrequest/') +@API.route('/sections/ministryrequest//') class GetSections(Resource): """Add document to deleted list. """ @@ -55,9 +55,9 @@ class GetSections(Resource): @TRACER.trace() @cross_origin(origins=allowedorigins()) @auth.require - def get(ministryrequestid): + def get(ministryrequestid, redactionlayer): try: - data = sectionservice().getsections_by_ministryid(ministryrequestid) + data = sectionservice().getsections_by_ministryid(ministryrequestid, redactionlayer) return json.dumps(data) , 200 except BusinessException as exception: return {'status': exception.status_code, 'message':exception.message}, 500 diff --git a/api/reviewer_api/services/annotationservice.py b/api/reviewer_api/services/annotationservice.py index 7aab2c80c..908c7a893 100644 --- a/api/reviewer_api/services/annotationservice.py +++ b/api/reviewer_api/services/annotationservice.py @@ -92,20 +92,8 @@ def getredactedsectionsbyrequest(self, ministryrequestid): ) } - def __issection(self, annotationsections, annotationname): - for entry in annotationsections: - if entry["sectionannotationname"] == annotationname: - return True - return False - - def __getsection(self, annotationsections, annotationname): - for entry in annotationsections: - if entry["redactannotation"] == annotationname: - return entry - return None - - def getannotationsections(self, ministryid): - annotationsections = AnnotationSection.get_by_ministryid(ministryid) + def getannotationsections(self, ministryid, redactionlayerid): + annotationsections = AnnotationSection.get_by_ministryid(ministryid, redactionlayerid) return annotationsections def copyannotation(self, ministryrequestid, sourcelayers, targetlayer): @@ -162,7 +150,7 @@ def __deleteannotations(self, annotationnames, redactionlayerid, userinfo): annotationnames, redactionlayerid, userinfo ) if resp.success == True: - AnnotationSection.bulkdeletesections(annotationnames, userinfo) + AnnotationSection.bulkdeletesections(annotationnames, redactionlayerid, userinfo) return resp return DefaultMethodResult(True, "No Annotations marked for delete", -1) diff --git a/api/reviewer_api/services/documentpageflagservice.py b/api/reviewer_api/services/documentpageflagservice.py index a5ce74b76..6c981d290 100644 --- a/api/reviewer_api/services/documentpageflagservice.py +++ b/api/reviewer_api/services/documentpageflagservice.py @@ -9,14 +9,14 @@ class documentpageflagservice: - def getpageflags(self, requestid, redactionlayerid): - layerids = redactionlayerservice().getmappedredactionlayers( - {"redactionlayerid": redactionlayerid} - ) + def getpageflags(self, requestid, redactionlayer): + layerids = [] + layerids.append(redactionlayerservice().getredactionlayerid(redactionlayer)) return DocumentPageflag.getpageflag_by_request(requestid, layerids) - def getpublicbody(self, requestid): - return DocumentPageflag.getpublicbody_by_request(requestid) + def getpublicbody(self, requestid, redactionlayer): + redactionlayerid = redactionlayerservice().getredactionlayerid(redactionlayer) + return DocumentPageflag.getpublicbody_by_request(requestid, redactionlayerid) def getdocumentpageflags( self, requestid, redactionlayerid, documentid=None, version=None diff --git a/api/reviewer_api/services/pageflagservice.py b/api/reviewer_api/services/pageflagservice.py index f5d46307f..95c68216c 100644 --- a/api/reviewer_api/services/pageflagservice.py +++ b/api/reviewer_api/services/pageflagservice.py @@ -22,14 +22,14 @@ def getpageflags(self): return pageflags - def getpageflag_by_request(self, requestid): + def getpageflag_by_request(self, requestid, redactionlayer): pageflags = Pageflag.getall() programareas = requests.request( method='GET', url=requestapiurl + "/api/foiflow/programareas", headers={'Authorization': AuthHelper.getauthtoken(), 'Content-Type': 'application/json'} ).json() - data = documentpageflagservice().getpublicbody(requestid) + data = documentpageflagservice().getpublicbody(requestid, redactionlayer) for entry in pageflags: if entry["name"] == "Consult": entry["programareas"] = programareas diff --git a/api/reviewer_api/services/sectionservice.py b/api/reviewer_api/services/sectionservice.py index 0dd34398e..7608cc63f 100644 --- a/api/reviewer_api/services/sectionservice.py +++ b/api/reviewer_api/services/sectionservice.py @@ -1,5 +1,6 @@ from reviewer_api.models.Sections import Section from reviewer_api.services.annotationservice import annotationservice +from reviewer_api.services.redactionlayerservice import redactionlayerservice import json class sectionservice: @@ -8,9 +9,10 @@ class sectionservice: def getsections(self): return Section.getall() - def getsections_by_ministryid(self, ministryid): + def getsections_by_ministryid(self, ministryid, redactionlayer): globalsections = self.getsections() - annotationsections = annotationservice().getannotationsections(ministryid) + redactionlayerid = redactionlayerservice().getredactionlayerid(redactionlayer) + annotationsections = annotationservice().getannotationsections(ministryid, redactionlayerid) requestsections = [] for annotationsection in annotationsections: _annotationsection = json.loads(json.dumps(annotationsection["section"])) diff --git a/web/src/apiManager/services/docReviewerService.tsx b/web/src/apiManager/services/docReviewerService.tsx index 657409e2d..357346831 100644 --- a/web/src/apiManager/services/docReviewerService.tsx +++ b/web/src/apiManager/services/docReviewerService.tsx @@ -222,12 +222,13 @@ export const deleteRedaction = ( export const fetchSections = ( foiministryrquestid: number, + currentlayername: string, callback: any, errorCallback: any ) => { let apiUrl: string = replaceUrl(API.DOCREVIEWER_SECTIONS, '', foiministryrquestid); - - httpGETRequest(apiUrl, {}, UserService.getToken()) + + httpGETRequest(apiUrl+"/"+currentlayername, {}, UserService.getToken()) .then((res:any) => { if (res.data || res.data === "") { store.dispatch(setSections(res.data) as any); @@ -242,11 +243,12 @@ export const fetchSections = ( export const fetchPageFlagsMasterData = ( foiministryrquestid:string, + redactionlayer:string, callback: any, errorCallback: any ) => { let apiUrlGet: string = replaceUrl( - API.DOCREVIEWER_GET_ALL_PAGEFLAGS, + API.DOCREVIEWER_GET_ALL_PAGEFLAGS+ "/"+redactionlayer, "", foiministryrquestid ); @@ -291,7 +293,7 @@ export const savePageFlag = ( export const fetchPageFlag = ( foiministryrquestid: string, - redactionlayerid: number, + redactionlayer: string, //callback: any, errorCallback: any ) => { @@ -299,7 +301,7 @@ export const fetchPageFlag = ( API.DOCREVIEWER_GET_PAGEFLAGS, "", foiministryrquestid - ) + "/" + redactionlayerid; + ) + "/" + redactionlayer; httpGETRequest(apiUrlGet, {}, UserService.getToken()) .then((res:any) => { diff --git a/web/src/components/FOI/Home/DocumentSelector.tsx b/web/src/components/FOI/Home/DocumentSelector.tsx index 96515e6e6..e9159a927 100644 --- a/web/src/components/FOI/Home/DocumentSelector.tsx +++ b/web/src/components/FOI/Home/DocumentSelector.tsx @@ -79,6 +79,7 @@ const DocumentSelector = ({ useEffect(() => { fetchPageFlagsMasterData( requestid, + currentLayer.name.toLowerCase(), (data: any) => setPageData(data), (error: any) => console.log(error) ); @@ -93,12 +94,13 @@ const DocumentSelector = ({ const updatePageFlags = () => { fetchPageFlagsMasterData( requestid, + currentLayer.name.toLowerCase(), (data: any) => setPageData(data), (error: any) => console.log(error) ); fetchPageFlag( requestid, - currentLayer.redactionlayerid, + currentLayer.name.toLowerCase(), (error: any) => console.log(error) ) } diff --git a/web/src/components/FOI/Home/Home.js b/web/src/components/FOI/Home/Home.js index 9149454f0..ef3afb3aa 100644 --- a/web/src/components/FOI/Home/Home.js +++ b/web/src/components/FOI/Home/Home.js @@ -105,7 +105,7 @@ function Home() { store.dispatch(setCurrentLayer(currentLayer)); fetchPageFlag( parseInt(foiministryrequestid), - currentLayer.redactionlayerid, + currentLayer.name.toLowerCase(), (error) => console.log(error) ); }, diff --git a/web/src/components/FOI/Home/Redlining.js b/web/src/components/FOI/Home/Redlining.js index 56903eda0..c1952d47d 100644 --- a/web/src/components/FOI/Home/Redlining.js +++ b/web/src/components/FOI/Home/Redlining.js @@ -265,7 +265,7 @@ const Redlining = React.forwardRef( useEffect(() => { let initializeWebViewer = async () => { let currentDocumentS3Url = currentDocument?.currentDocumentS3Url; - fetchSections(requestid, (error) => console.log(error)); + fetchSections(requestid, currentLayer.name.toLowerCase(), (error) => console.log(error)); let response = await fetchPDFTronLicense(null, (error) => console.log(error) ); @@ -705,7 +705,7 @@ const Redlining = React.forwardRef( }, currentLayer.name.toLowerCase() ); - fetchPageFlag(requestid, currentLayer.redactionlayerid, (error) => + fetchPageFlag(requestid, currentLayer.name.toLowerCase(), (error) => console.log(error) ); } @@ -820,7 +820,7 @@ const Redlining = React.forwardRef( (data) => { fetchPageFlag( requestid, - currentLayer.redactionlayerid, + currentLayer.name.toLowerCase(), (error) => console.log(error) ); }, @@ -990,7 +990,7 @@ const Redlining = React.forwardRef( (data) => { fetchPageFlag( requestid, - currentLayer.redactionlayerid, + currentLayer.name.toLowerCase(), (error) => console.log(error) ); }, @@ -1023,7 +1023,7 @@ const Redlining = React.forwardRef( (data) => { fetchPageFlag( requestid, - currentLayer.redactionlayerid, + currentLayer.name.toLowerCase(), (error) => console.log(error) ); }, @@ -1469,7 +1469,7 @@ const Redlining = React.forwardRef( astr, (data) => { setPageSelections([]); - fetchPageFlag(requestid, currentLayer.redactionlayerid, (error) => + fetchPageFlag(requestid, currentLayer.name.toLowerCase(), (error) => console.log(error) ); }, @@ -1606,7 +1606,7 @@ const Redlining = React.forwardRef( setPageSelections([]); fetchPageFlag( requestid, - currentLayer.redactionlayerid, + currentLayer.name.toLowerCase(), (error) => console.log(error) ); }, @@ -1754,7 +1754,7 @@ const Redlining = React.forwardRef( astr, (data) => { setPageSelections([]); - fetchPageFlag(requestid, currentLayer.redactionlayerid, (error) => + fetchPageFlag(requestid, currentLayer.name.toLowerCase(), (error) => console.log(error) ); }, From 2f767262a7489035007aa930c91a5a44fe8cb788 Mon Sep 17 00:00:00 2001 From: "sumathi.thirumani" Date: Wed, 3 Jan 2024 01:34:37 -0800 Subject: [PATCH 30/45] Removed unused functional methods + edit bug fix changes. --- web/src/components/FOI/Home/Redlining.js | 72 +++++++++++++++--------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/web/src/components/FOI/Home/Redlining.js b/web/src/components/FOI/Home/Redlining.js index c1952d47d..dc2125b08 100644 --- a/web/src/components/FOI/Home/Redlining.js +++ b/web/src/components/FOI/Home/Redlining.js @@ -2479,20 +2479,41 @@ const Redlining = React.forwardRef( return stampJson; } - const annotationSectionsMapping = async (xfdfString) => { + const annotationSectionsMapping = async (xfdfString, formattedAnnotationXML) => { let annotationManager = docInstance?.Core.annotationManager; let annotList = await annotationManager.importAnnotations(xfdfString); let sectionStamps = {}; - for (const annot of annotList) { - let parentRedaction = annot.getCustomData("parentRedaction"); - if (parentRedaction) { - if (annot.Subject == "Free Text") { - let parentRedactionId = parentRedaction.replace(/"/g, '"').replace(/\\/g, "") - sectionStamps[parentRedactionId] = getAnnotationSections(annot); - } + let annotationpagenumbers = annotationpagemapping(formattedAnnotationXML); + for (const annot of annotList) { + let parentRedaction = annot.getCustomData("parentRedaction"); + if (parentRedaction) { + if (annot.Subject == "Free Text") { + let parentRedactionId = parentRedaction.replace(/"/g, '"').replace(/\\/g, "") + let sections = getAnnotationSections(annot); + if (sections.some(item => item.section === 's. 14')) { + sectionStamps[parentRedactionId] = annotationpagenumbers[parentRedactionId]; + } } } - return sectionStamps; + } + return sectionStamps; + } + + const annotationpagemapping = (formattedAnnotationXML) => { + let xmlstring = + '' + + formattedAnnotationXML + + ""; + const parser = new DOMParser(); + const xmlDoc = parser.parseFromString(xmlstring,"text/xml"); + let annotnodes = xmlDoc.documentElement.childNodes; + let annotationpages = {}; + for (let i = 0; i < annotnodes.length ; i++) { + if(annotnodes[i].nodeName === "redact") { + annotationpages[annotnodes[i].getAttribute("name")] = parseInt(annotnodes[i].getAttribute("page"))+1; + } + } + return annotationpages; } const formatAnnotationsForDocument = ( @@ -2605,31 +2626,27 @@ const Redlining = React.forwardRef( if(redlineCategory === "oipcreview") { const rarr = []; let annotationManager = docInstance?.Core.annotationManager; - let sectionStamps = await annotationSectionsMapping(xfdfString); + let s14_sectionStamps = await annotationSectionsMapping(xfdfString, formattedAnnotationXML); let rects = []; - for (const [key, value] of Object.entries(sectionStamps)) { - let s14check_annotationid = key; - let s14check_sections = sectionStamps[key]; - for (const [s14key, s14value] in s14check_sections) { - if (s14check_sections[s14key]["section"] === "s. 14") { - let s14annoation = annotationManager.getAnnotationById(s14check_annotationid); - if ( s14annoation.Subject === "Redact") { - rects = rects.concat( + for (const [key, value] of Object.entries(s14_sectionStamps)) { + let s14annoation = annotationManager.getAnnotationById(key); + if ( s14annoation.Subject === "Redact") { + rects = rects.concat( s14annoation.getQuads().map((q) => { - return { - page: s14annoation.getPageNumber(), - recto: q.toRect() + return { + pageno: s14_sectionStamps[key], + recto: q.toRect(), + vpageno: s14annoation.getPageNumber() }; }) ); } - } - } + + } for (const rect of rects) { - let height = docViewer.getPageHeight(rect.page); - rarr.push(await PDFNet.Redactor.redactionCreate(rect.page, (await PDFNet.Rect.init(rect.recto.x1,height-rect.recto.y1,rect.recto.x2,height-rect.recto.y2)), false, '')); - + let height = docViewer.getPageHeight(rect.vpageno); + rarr.push(await PDFNet.Redactor.redactionCreate(rect.pageno, (await PDFNet.Rect.init(rect.recto.x1,height-rect.recto.y1,rect.recto.x2,height-rect.recto.y2)), false, '')); } if (rarr.length > 0) { const app = {}; @@ -2641,6 +2658,7 @@ const Redlining = React.forwardRef( } } + //OIPC - Special Block : End stitchObject .getFileData({ @@ -2940,7 +2958,7 @@ const Redlining = React.forwardRef( annotationManager.exportAnnotations().then(async (xfdfString) => { //parse annotation xml let jObj = parser.parseFromString(xfdfString); // Assume xmlText contains the example XML - let annots = jObj.getElementsByTagName("annots"); + let annots = xfdfString.getElementsByTagName("annots"); let sectionStamps = {}; let stampJson = {}; From 1e3d013488c605239a998880218efc7e4c275c54 Mon Sep 17 00:00:00 2001 From: "sumathi.thirumani" Date: Wed, 3 Jan 2024 12:09:12 -0800 Subject: [PATCH 31/45] final package bugfix generation. --- web/src/components/FOI/Home/Redlining.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/src/components/FOI/Home/Redlining.js b/web/src/components/FOI/Home/Redlining.js index dc2125b08..22f922183 100644 --- a/web/src/components/FOI/Home/Redlining.js +++ b/web/src/components/FOI/Home/Redlining.js @@ -2508,9 +2508,9 @@ const Redlining = React.forwardRef( const xmlDoc = parser.parseFromString(xmlstring,"text/xml"); let annotnodes = xmlDoc.documentElement.childNodes; let annotationpages = {}; - for (let i = 0; i < annotnodes.length ; i++) { - if(annotnodes[i].nodeName === "redact") { - annotationpages[annotnodes[i].getAttribute("name")] = parseInt(annotnodes[i].getAttribute("page"))+1; + for (const element of annotnodes) { + if(element.nodeName === "redact") { + annotationpages[element.getAttribute("name")] = parseInt(element.getAttribute("page"))+1; } } return annotationpages; @@ -2958,7 +2958,7 @@ const Redlining = React.forwardRef( annotationManager.exportAnnotations().then(async (xfdfString) => { //parse annotation xml let jObj = parser.parseFromString(xfdfString); // Assume xmlText contains the example XML - let annots = xfdfString.getElementsByTagName("annots"); + let annots = jObj.getElementsByTagName("annots"); let sectionStamps = {}; let stampJson = {}; From 7f21dba65c2aeb6c7af72374fc484a51e772f5cf Mon Sep 17 00:00:00 2001 From: Milos Despotovic Date: Thu, 4 Jan 2024 09:59:39 -0800 Subject: [PATCH 32/45] Change to use validoipcreviewlayer --- web/src/components/FOI/Home/Redlining.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/web/src/components/FOI/Home/Redlining.js b/web/src/components/FOI/Home/Redlining.js index 22f922183..9e08e87bc 100644 --- a/web/src/components/FOI/Home/Redlining.js +++ b/web/src/components/FOI/Home/Redlining.js @@ -107,6 +107,7 @@ const Redlining = React.forwardRef( const documentList = useAppSelector( (state) => state.documents?.documentList ); + const validoipcreviewlayer = useAppSelector((state) => state.documents?.requestinfo?.validoipcreviewlayer); const [docViewer, setDocViewer] = useState(null); const [annotManager, setAnnotManager] = useState(null); @@ -670,9 +671,8 @@ const Redlining = React.forwardRef( setMerge(true); setFetchAnnotResponse(data); } else { - const oipcLayer = redactionLayers.find((l) => l.redactionlayerid === 3) //Set to read only if oipc layer exists - if (oipcLayer && oipcLayer.count > 0 && currentLayer.name.toLowerCase() !== "oipc") { + if (validoipcreviewlayer && currentLayer.name.toLowerCase() !== "oipc") { annotManager.enableReadOnlyMode(); } else { annotManager.disableReadOnlyMode(); @@ -744,8 +744,7 @@ const Redlining = React.forwardRef( // from the server or individual changes from other users // Disable creating annotations in redline layer if OIPC layer is present - const oipcLayer = redactionLayers.find((l) => l.redactionlayerid === 3) - if (oipcLayer && oipcLayer.count > 0 && currentLayer.name.toLowerCase() === "redline") { + if (validoipcreviewlayer && currentLayer.name.toLowerCase() === "redline") { return; } From 2700f7c53be76f2a83bb475473b1213da3952c8a Mon Sep 17 00:00:00 2001 From: "sumathi.thirumani" Date: Thu, 4 Jan 2024 14:35:24 -0800 Subject: [PATCH 33/45] Release of 4953 --- api/reviewer_api/models/AnnotationSections.py | 6 ++++-- api/reviewer_api/services/annotationservice.py | 13 ++++++++----- api/reviewer_api/services/redactionlayerservice.py | 3 +++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/api/reviewer_api/models/AnnotationSections.py b/api/reviewer_api/models/AnnotationSections.py index a6922dcf3..f97588a82 100644 --- a/api/reviewer_api/models/AnnotationSections.py +++ b/api/reviewer_api/models/AnnotationSections.py @@ -399,7 +399,7 @@ def get_by_ministryid(cls, ministryrequestid, redactionlayerid): db.session.close() @classmethod - def getredactedsectionsbyrequest(cls, ministryrequestid): + def getredactedsectionsbyrequest(cls, ministryrequestid, redactionlayerid): try: sql = """select section from public."Sections" where sectionid in (select distinct (json_array_elements((as1.section::json->>'ids')::json)->>'id')::integer @@ -409,11 +409,13 @@ def getredactedsectionsbyrequest(cls, ministryrequestid): join public."DocumentMaster" dm on dm.documentmasterid = d.documentmasterid and dm.ministryrequestid = :ministryrequestid left join public."DocumentDeleted" dd on dm.filepath ilike dd.filepath || '%' and dd.ministryrequestid = :ministryrequestid where as1.foiministryrequestid = :ministryrequestid and as1.isactive = true + and as1.redactionlayerid = a.redactionlayerid + and as1.redactionlayerid = :redactionlayerid and (dd.deleted is null or dd.deleted is false) and a.isactive = true) and sectionid != 25 order by sortorder""" - rs = db.session.execute(text(sql), {"ministryrequestid": ministryrequestid}) + rs = db.session.execute(text(sql), {"ministryrequestid": ministryrequestid, "redactionlayerid": redactionlayerid}) sectionstring = "" for row in rs: sectionstring = sectionstring + row["section"] + ", " diff --git a/api/reviewer_api/services/annotationservice.py b/api/reviewer_api/services/annotationservice.py index 908c7a893..e94a5e716 100644 --- a/api/reviewer_api/services/annotationservice.py +++ b/api/reviewer_api/services/annotationservice.py @@ -86,11 +86,14 @@ def getrequestannotationinfo(self, ministryrequestid, redactionlayer): return annotationsections def getredactedsectionsbyrequest(self, ministryrequestid): - return { - "sections": AnnotationSection.getredactedsectionsbyrequest( - ministryrequestid - ) - } + sections = {} + layers = redactionlayerservice().getall() + for layer in layers: + if layer["name"] in ["Redline", "OIPC"]: + redactedsections = AnnotationSection.getredactedsectionsbyrequest(ministryrequestid, layer["redactionlayerid"]) + if redactedsections not in (None, ""): + sections[layer["name"]] = redactedsections + return sections def getannotationsections(self, ministryid, redactionlayerid): annotationsections = AnnotationSection.get_by_ministryid(ministryid, redactionlayerid) diff --git a/api/reviewer_api/services/redactionlayerservice.py b/api/reviewer_api/services/redactionlayerservice.py index 072fc4bf5..6a9b2210d 100644 --- a/api/reviewer_api/services/redactionlayerservice.py +++ b/api/reviewer_api/services/redactionlayerservice.py @@ -6,6 +6,9 @@ class redactionlayerservice: def getredactionlayers(self, ministryrequestid): return RedactionLayer.getall(ministryrequestid) + + def getall(self): + return RedactionLayer.getlayers() def getdefaultredactionlayerid(self): From 35e69b18a9c1cfa7665835a96283f0dd389d40ed Mon Sep 17 00:00:00 2001 From: Milos Despotovic Date: Thu, 4 Jan 2024 15:41:08 -0800 Subject: [PATCH 34/45] Update ContextMenu to use validoipcreviewlayer --- web/src/components/FOI/Home/ContextMenu.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/web/src/components/FOI/Home/ContextMenu.tsx b/web/src/components/FOI/Home/ContextMenu.tsx index 67bcc5358..9c8f44c85 100644 --- a/web/src/components/FOI/Home/ContextMenu.tsx +++ b/web/src/components/FOI/Home/ContextMenu.tsx @@ -29,7 +29,7 @@ const ContextMenu = ({ const [openConsultPopup, setOpenConsultPopup] = useState(false) const [flagId, setFlagId] = React.useState(0); const currentLayer = useAppSelector((state: any) => state.documents?.currentLayer); - const redactionLayers = useAppSelector((state: any) => state.documents?.redactionLayers); + const validoipcreviewlayer = useAppSelector((state: any) => state.documents?.requestinfo?.validoipcreviewlayer); const popoverEnter = (e: any) => { @@ -97,8 +97,7 @@ const ContextMenu = ({ // } const showPageFlagList = () => { - const oipcLayer = redactionLayers.find((l: any) => l.redactionlayerid === 3) - if (oipcLayer && oipcLayer.count > 0 && currentLayer.name.toLowerCase() === "redline") { + if (validoipcreviewlayer && currentLayer.name.toLowerCase() === "redline") { return ( From 7d0cb7a289cbcccf85e1553bda22259489388386 Mon Sep 17 00:00:00 2001 From: Milos Despotovic Date: Thu, 4 Jan 2024 16:02:46 -0800 Subject: [PATCH 35/45] Fix bug --- web/src/components/FOI/Home/Redlining.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/FOI/Home/Redlining.js b/web/src/components/FOI/Home/Redlining.js index 9e08e87bc..5df8e2828 100644 --- a/web/src/components/FOI/Home/Redlining.js +++ b/web/src/components/FOI/Home/Redlining.js @@ -672,7 +672,7 @@ const Redlining = React.forwardRef( setFetchAnnotResponse(data); } else { //Set to read only if oipc layer exists - if (validoipcreviewlayer && currentLayer.name.toLowerCase() !== "oipc") { + if (validoipcreviewlayer && currentLayer.name.toLowerCase() === "redline") { annotManager.enableReadOnlyMode(); } else { annotManager.disableReadOnlyMode(); From 4db6e18fb5aa278bdc335dc39a5fd321495a6afa Mon Sep 17 00:00:00 2001 From: Milos Despotovic Date: Thu, 4 Jan 2024 18:01:12 -0800 Subject: [PATCH 36/45] Set default layer --- web/src/components/FOI/Home/Home.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/src/components/FOI/Home/Home.js b/web/src/components/FOI/Home/Home.js index ef3afb3aa..233c7d041 100644 --- a/web/src/components/FOI/Home/Home.js +++ b/web/src/components/FOI/Home/Home.js @@ -25,6 +25,7 @@ import IconButton from "@mui/material/IconButton"; function Home() { const user = useAppSelector((state) => state.user.userDetail); + const validoipcreviewlayer = useAppSelector((state) => state.documents?.requestinfo?.validoipcreviewlayer); const [files, setFiles] = useState([]); // added incompatibleFiles to capture incompatible files for download redline const [incompatibleFiles, setIncompatibleFiles] = useState([]); @@ -101,7 +102,7 @@ function Home() { (data) => { let redline = data.find((l) => l.name === "Redline"); let oipc = data.find((l) => l.name === "OIPC"); - let currentLayer = oipc.count > 0 ? oipc : redline; + let currentLayer = oipc.count > 0 && validoipcreviewlayer ? oipc : redline; store.dispatch(setCurrentLayer(currentLayer)); fetchPageFlag( parseInt(foiministryrequestid), From 4bdc8179459d34ed87956154d787a0c0b9f20731 Mon Sep 17 00:00:00 2001 From: Milos Despotovic Date: Thu, 4 Jan 2024 18:31:22 -0800 Subject: [PATCH 37/45] Fix setting current layer --- web/src/components/FOI/Home/Home.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/FOI/Home/Home.js b/web/src/components/FOI/Home/Home.js index 233c7d041..4332c42e7 100644 --- a/web/src/components/FOI/Home/Home.js +++ b/web/src/components/FOI/Home/Home.js @@ -102,7 +102,7 @@ function Home() { (data) => { let redline = data.find((l) => l.name === "Redline"); let oipc = data.find((l) => l.name === "OIPC"); - let currentLayer = oipc.count > 0 && validoipcreviewlayer ? oipc : redline; + let currentLayer = validoipcreviewlayer ? oipc : redline; store.dispatch(setCurrentLayer(currentLayer)); fetchPageFlag( parseInt(foiministryrequestid), From 392b6aba5ddbdb195fe6a57b1040a92f53879a36 Mon Sep 17 00:00:00 2001 From: Milos Despotovic Date: Fri, 5 Jan 2024 11:14:02 -0800 Subject: [PATCH 38/45] Set default layer to OIPC if valid --- web/src/components/FOI/Home/Home.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web/src/components/FOI/Home/Home.js b/web/src/components/FOI/Home/Home.js index 4332c42e7..c2584940f 100644 --- a/web/src/components/FOI/Home/Home.js +++ b/web/src/components/FOI/Home/Home.js @@ -97,6 +97,9 @@ function Home() { console.log(error); } ); + }, []); + + useEffect(() => { fetchRedactionLayerMasterData( foiministryrequestid, (data) => { @@ -112,7 +115,7 @@ function Home() { }, (error) => console.log(error) ); - }, []); + }, [validoipcreviewlayer]) const openFOIPPAModal = (pageNos) => { redliningRef?.current?.addFullPageRedaction(pageNos); From 8ad8fc0c5e644a7b4822f03a6de3cc4338c2c676 Mon Sep 17 00:00:00 2001 From: "sumathi.thirumani" Date: Wed, 10 Jan 2024 14:41:18 -0800 Subject: [PATCH 39/45] Merge changes from oipc to leverage use of layername. --- web/src/components/FOI/Home/Redlining.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/web/src/components/FOI/Home/Redlining.js b/web/src/components/FOI/Home/Redlining.js index 9bd9f4fd1..cf30a56ba 100644 --- a/web/src/components/FOI/Home/Redlining.js +++ b/web/src/components/FOI/Home/Redlining.js @@ -773,7 +773,7 @@ const Redlining = React.forwardRef( ); fetchPageFlag( requestid, - currentLayer.redactionlayerid, + currentLayer.name.toLowerCase(), docsForStitcing.map(d => d.file.documentid), (error) => console.log(error) ); @@ -893,7 +893,7 @@ const Redlining = React.forwardRef( (data) => { fetchPageFlag( requestid, - currentLayer.redactionlayerid, + currentLayer.name.toLowerCase(), docsForStitcing.map(d => d.file.documentid), (error) => console.log(error) ); @@ -1064,7 +1064,7 @@ const Redlining = React.forwardRef( (data) => { fetchPageFlag( requestid, - currentLayer.redactionlayerid, + currentLayer.name.toLowerCase(), docsForStitcing.map(d => d.file.documentid), (error) => console.log(error) ); @@ -1098,7 +1098,7 @@ const Redlining = React.forwardRef( (data) => { fetchPageFlag( requestid, - currentLayer.redactionlayerid, + currentLayer.name.toLowerCase(), docsForStitcing.map(d => d.file.documentid), (error) => console.log(error) ); @@ -1527,7 +1527,7 @@ const Redlining = React.forwardRef( setPageSelections([]); fetchPageFlag( requestid, - currentLayer.redactionlayerid, + currentLayer.name.toLowerCase(), docsForStitcing.map(d => d.file.documentid), (error) => console.log(error) ); @@ -1665,7 +1665,7 @@ const Redlining = React.forwardRef( setPageSelections([]); fetchPageFlag( requestid, - currentLayer.redactionlayerid, + currentLayer.name.toLowerCase(), docsForStitcing.map(d => d.file.documentid), (error) => console.log(error) ); @@ -1816,7 +1816,7 @@ const Redlining = React.forwardRef( setPageSelections([]); fetchPageFlag( requestid, - currentLayer.redactionlayerid, + currentLayer.name.toLowerCase(), docsForStitcing.map(d => d.file.documentid), (error) => console.log(error) ); From 41d2fa6de3597b8557e47b14df04c4420ad2e7b3 Mon Sep 17 00:00:00 2001 From: "sumathi.thirumani" Date: Wed, 10 Jan 2024 16:42:40 -0800 Subject: [PATCH 40/45] Changes to allow copy only for first time of oipc layer. --- web/src/components/FOI/Home/Home.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/FOI/Home/Home.js b/web/src/components/FOI/Home/Home.js index 2f42f86c2..6a7c410f4 100644 --- a/web/src/components/FOI/Home/Home.js +++ b/web/src/components/FOI/Home/Home.js @@ -108,7 +108,7 @@ function Home() { (data) => { let redline = data.find((l) => l.name === "Redline"); let oipc = data.find((l) => l.name === "OIPC"); - let currentLayer = validoipcreviewlayer ? oipc : redline; + let currentLayer = validoipcreviewlayer && oipc.count > 0 ? oipc : redline; store.dispatch(setCurrentLayer(currentLayer)); }, (error) => console.log(error) From 9dc36c0906e5e58b8a9d47658a8c4b29ef0b22c7 Mon Sep 17 00:00:00 2001 From: "sumathi.thirumani" Date: Wed, 10 Jan 2024 17:44:15 -0800 Subject: [PATCH 41/45] OIPC Integration --- web/src/components/FOI/Home/Redlining.js | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/web/src/components/FOI/Home/Redlining.js b/web/src/components/FOI/Home/Redlining.js index cf30a56ba..735467afa 100644 --- a/web/src/components/FOI/Home/Redlining.js +++ b/web/src/components/FOI/Home/Redlining.js @@ -111,7 +111,8 @@ const Redlining = React.forwardRef( const documentList = useAppSelector( (state) => state.documents?.documentList ); - + const validoipcreviewlayer = useAppSelector((state) => state.documents?.requestinfo?.validoipcreviewlayer); + const [docViewer, setDocViewer] = useState(null); const [annotManager, setAnnotManager] = useState(null); const [annots, setAnnots] = useState(null); @@ -282,7 +283,7 @@ const Redlining = React.forwardRef( useEffect(() => { let initializeWebViewer = async () => { let currentDocumentS3Url = currentDocument?.currentDocumentS3Url; - fetchSections(requestid, (error) => console.log(error)); + fetchSections(requestid, currentLayer.name.toLowerCase(), (error) => console.log(error)); let response = await fetchPDFTronLicense(null, (error) => console.log(error) ); @@ -556,7 +557,7 @@ const Redlining = React.forwardRef( Promise.all(objpreptasks); - fetchAnnotationsInfo(requestid, (error) => { + fetchAnnotationsInfo(requestid, currentLayer.name.toLowerCase(), (error) => { console.log("Error:", error); }); }); @@ -735,9 +736,8 @@ const Redlining = React.forwardRef( setFetchAnnotResponse(data); } else { //oipc changes - begin - const oipcLayer = redactionLayers.find((l) => l.redactionlayerid === 3) //Set to read only if oipc layer exists - if (oipcLayer && oipcLayer.count > 0 && currentLayer.name.toLowerCase() !== "oipc") { + if (validoipcreviewlayer && currentLayer.name.toLowerCase() === "redline") { annotManager.enableReadOnlyMode(); } else { annotManager.disableReadOnlyMode(); @@ -769,7 +769,7 @@ const Redlining = React.forwardRef( (error) => { console.log("Error:", error); }, - currentLayer.name + currentLayer.name.toLowerCase() ); fetchPageFlag( requestid, @@ -813,8 +813,7 @@ const Redlining = React.forwardRef( // from the server or individual changes from other users //oipc changes - begin - const oipcLayer = redactionLayers.find((l) => l.redactionlayerid === 3) - if (oipcLayer && oipcLayer.count > 0 && currentLayer.name.toLowerCase() === "redline") { + if (validoipcreviewlayer && currentLayer.name.toLowerCase() === "redline") { return; } //oipc changes - end @@ -1321,7 +1320,7 @@ const Redlining = React.forwardRef( "Error occurred while fetching redaction details, please refresh browser and try again" ); }, - currentLayer.name + currentLayer.name.toLowerCase() ); } }; @@ -2400,13 +2399,13 @@ const Redlining = React.forwardRef( return divIncompatableMapping }; - const fetchDocumentRedlineAnnotations = async (requestid, documentids) => { + const fetchDocumentRedlineAnnotations = async (requestid, documentids, layer) => { let documentRedlineAnnotations = {}; let docCounter = 0; for (let documentid of documentids) { fetchDocumentAnnotations( requestid, - "Redline", + layer, documentid, async (data) => { docCounter++; @@ -2610,7 +2609,7 @@ const Redlining = React.forwardRef( ); let IncompatableList = prepareRedlineIncompatibleMapping(res); setIncompatableList(IncompatableList); - fetchDocumentRedlineAnnotations(requestid, documentids); + fetchDocumentRedlineAnnotations(requestid, documentids, currentLayer.name.toLowerCase()); setRedlineZipperMessage({ ministryrequestid: requestid, category: getzipredlinecategory(layertype), From 45130c86b009d63ba20134972f4d8f2fda578a02 Mon Sep 17 00:00:00 2001 From: "sumathi.thirumani" Date: Thu, 11 Jan 2024 11:02:43 -0800 Subject: [PATCH 42/45] Remove redundant logging. --- web/src/components/FOI/Home/utils.js | 4 ++-- web/src/helper/helper.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/web/src/components/FOI/Home/utils.js b/web/src/components/FOI/Home/utils.js index d3c43aca0..3966fd772 100644 --- a/web/src/components/FOI/Home/utils.js +++ b/web/src/components/FOI/Home/utils.js @@ -189,7 +189,7 @@ export const sortDocObjects = (_pdftronDocObjs, doclist) => { _soCtr < __refinedpdftronDocObjs?.length, _dlCtr < doclist?.length; _dlCtr++, _soCtr++ ) { - console.log("I LOGGED"); //#IMPORTANT -- TOTAL TIMES THIS CONSOLE MESSAGE LOGGED SHOUDL BE EQUAL TO TOTAL DOCLIST LENTH !IMportant, else slow!!! + //console.log("I LOGGED"); #IMPORTANT -- TOTAL TIMES THIS CONSOLE MESSAGE LOGGED SHOUDL BE EQUAL TO TOTAL DOCLIST LENTH !IMportant, else slow!!! if ( __refinedpdftronDocObjs[_soCtr] != null && __refinedpdftronDocObjs[_soCtr] != undefined @@ -218,7 +218,7 @@ export const sortDocObjectsForRedline = (_pdftronDocObjs, doclist) => { _soCtr < __refinedpdftronDocObjs?.length, _dlCtr < doclist?.length; _dlCtr++, _soCtr++ ) { - console.log("REDLINE I LOGGED"); //#IMPORTANT -- TOTAL TIMES THIS CONSOLE MESSAGE LOGGED SHOUDL BE EQUAL TO TOTAL DOCLIST LENTH !IMportant, else slow!!! + //console.log("REDLINE I LOGGED"); #IMPORTANT -- TOTAL TIMES THIS CONSOLE MESSAGE LOGGED SHOUDL BE EQUAL TO TOTAL DOCLIST LENTH !IMportant, else slow!!! if ( __refinedpdftronDocObjs[_soCtr] != null && __refinedpdftronDocObjs[_soCtr] != undefined diff --git a/web/src/helper/helper.ts b/web/src/helper/helper.ts index 782957159..9db792331 100644 --- a/web/src/helper/helper.ts +++ b/web/src/helper/helper.ts @@ -97,7 +97,7 @@ const getSessionData = (key: any) => { return sessionObject.sessionData; } else { sessionStorage.removeItem(key); - console.log(`${key} session expired`); + //console.log(`${key} session expired`); return null; } } else { From 75e64de2ec4dd1e99c946d45a4cc62e1b34e7914 Mon Sep 17 00:00:00 2001 From: "sumathi.thirumani" Date: Thu, 11 Jan 2024 14:45:59 -0800 Subject: [PATCH 43/45] OIPC Integration + additional changes. --- web/src/components/FOI/Home/DocumentSelector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/FOI/Home/DocumentSelector.tsx b/web/src/components/FOI/Home/DocumentSelector.tsx index 362479a45..6a189ec6b 100644 --- a/web/src/components/FOI/Home/DocumentSelector.tsx +++ b/web/src/components/FOI/Home/DocumentSelector.tsx @@ -108,7 +108,7 @@ const DocumentSelector = React.forwardRef(({ (data: any) => setPageData(data), (error: any) => console.log(error) ); - }, []); + }, [currentLayer]); useEffect(() => { if(requestInfo.requesttype == "personal" && ["MSD", "MCF"].includes(requestInfo.bcgovcode)) { From 4356c7fed7663e8db0007b1ab2d0645fc3b1a477 Mon Sep 17 00:00:00 2001 From: "sumathi.thirumani" Date: Thu, 11 Jan 2024 16:17:28 -0800 Subject: [PATCH 44/45] OIPC Integration + additional changes. --- api/reviewer_api/services/documentservice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/reviewer_api/services/documentservice.py b/api/reviewer_api/services/documentservice.py index e89663daf..44e006b2b 100644 --- a/api/reviewer_api/services/documentservice.py +++ b/api/reviewer_api/services/documentservice.py @@ -483,7 +483,7 @@ def getfilepathbydocumentid(self, documentid): def validate_oipcreviewlayer(self, request_json, requestid): #check for OIPC & Reason - if 'isoipcreview' in request_json and request_json['isoipcreview'] == True and any(oipc['reasonid'] == 2 for oipc in request_json['oipcdetails']): + if 'isoipcreview' in request_json and request_json['isoipcreview'] == True and any((oipc['reasonid'] == 2 and oipc['outcomeid'] is None)for oipc in request_json['oipcdetails']): #Check for Reopen if 'isreopened' in request_json and request_json['isreopened'] == True: #Check is Response Package generated before closure. From e46b8b6da92284af1c06b4d56afacbe10a0aa73b Mon Sep 17 00:00:00 2001 From: "sumathi.thirumani" Date: Thu, 18 Jan 2024 11:22:00 -0800 Subject: [PATCH 45/45] Changes to disable OIPC review for redline layer. --- web/src/components/FOI/Home/Redlining.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/components/FOI/Home/Redlining.js b/web/src/components/FOI/Home/Redlining.js index 735467afa..bfabb5bd5 100644 --- a/web/src/components/FOI/Home/Redlining.js +++ b/web/src/components/FOI/Home/Redlining.js @@ -273,7 +273,7 @@ const Redlining = React.forwardRef( ].includes(requestStatus) ); const [enableSavingOipcRedline, setEnableSavingOipcRedline] = useState( - redactionLayers.find((l) => l.redactionlayerid === 3)?.count > 0 + validoipcreviewlayer === true && currentLayer.name.toLowerCase() === "oipc" ) const [enableSavingFinal, setEnableSavingFinal] = useState( isReadyForSignOff() && requestStatus == RequestStates["Response"] @@ -1206,7 +1206,7 @@ const Redlining = React.forwardRef( let _enableSavingRedline = isReadyForSignOff() && isValidRedlineDownload(); //oipc changes - begin const _enableSavingOipcRedline = - redactionLayers.find((l) => l.redactionlayerid === 3).count > 0 && + (validoipcreviewlayer === true && currentLayer.name.toLowerCase() === "oipc") && isReadyForSignOff(); //oipc changes - end setEnableSavingRedline(