diff --git a/MCS.FOI.S3FileConversion/MCS.FOI.MSGToPDF/MCS.FOI.MSGToPDF.csproj b/MCS.FOI.S3FileConversion/MCS.FOI.MSGToPDF/MCS.FOI.MSGToPDF.csproj index fec4662d0..7d8eb110a 100644 --- a/MCS.FOI.S3FileConversion/MCS.FOI.MSGToPDF/MCS.FOI.MSGToPDF.csproj +++ b/MCS.FOI.S3FileConversion/MCS.FOI.MSGToPDF/MCS.FOI.MSGToPDF.csproj @@ -12,7 +12,7 @@ - + diff --git a/MCS.FOI.S3FileConversion/MCS.FOI.MSGToPDF/MSGFileProcessor.cs b/MCS.FOI.S3FileConversion/MCS.FOI.MSGToPDF/MSGFileProcessor.cs index 0a1e83052..23a020998 100644 --- a/MCS.FOI.S3FileConversion/MCS.FOI.MSGToPDF/MSGFileProcessor.cs +++ b/MCS.FOI.S3FileConversion/MCS.FOI.MSGToPDF/MSGFileProcessor.cs @@ -1,4 +1,5 @@ using MsgReader.Outlook; +using MsgReader; using Serilog; using Syncfusion.HtmlConverter; using Syncfusion.Pdf; @@ -8,10 +9,14 @@ using System.Text; using System.Text.RegularExpressions; using Syncfusion.DocIO; +using System.Collections.Generic; +using Syncfusion.Pdf.HtmlToPdf; +using System.ComponentModel.DataAnnotations; +using System; namespace MCS.FOI.MSGToPDF { - public class MSGFileProcessor : IMSGFileProcessor , IDisposable + public class MSGFileProcessor : IMSGFileProcessor, IDisposable { public Stream SourceStream { get; set; } public bool IsSinglePDFOutput { get; set; } @@ -40,7 +45,7 @@ public MSGFileProcessor(Stream sourceStream) output = new(); attachmentsObj = new(); try - { + { if (SourceStream != null && SourceStream.Length > 0) { for (int attempt = 1; attempt <= FailureAttemptCount; attempt++) @@ -48,18 +53,180 @@ public MSGFileProcessor(Stream sourceStream) try { using var msg = new MsgReader.Outlook.Storage.Message(SourceStream); + Dictionary fileNameHash = new(); + foreach (Object attachment in msg.Attachments) + { + attachmentStream = new(); + if (attachment.GetType().FullName.ToLower().Contains("message")) + { + var _attachment = (Storage.Message)attachment; + var filename = _attachment.FileName; + var extension = Path.GetExtension(filename); + if (!string.IsNullOrEmpty(extension)) + { + _attachment.Save(attachmentStream); + Dictionary attachmentInfo = new Dictionary(); + + if (fileNameHash.ContainsKey(filename)) + { + + filename = Path.GetFileNameWithoutExtension(filename) + '1' + extension; + } + fileNameHash.Add(filename, true); + attachmentInfo.Add("filename", _attachment.FileName); + attachmentInfo.Add("s3filename", filename); + attachmentInfo.Add("size", attachmentStream.Capacity.ToString()); + attachmentInfo.Add("lastmodified", _attachment.LastModificationTime.ToString()); + attachmentInfo.Add("created", _attachment.CreationTime.ToString()); + attachmentsObj.Add(attachmentStream, attachmentInfo); + } + } + else + { + var _attachment = (Storage.Attachment)attachment; + var filename = _attachment.FileName; + var extension = Path.GetExtension(filename); + + if (!string.IsNullOrEmpty(extension)) + { + attachmentStream.Write(_attachment.Data, 0, _attachment.Data.Length); + Dictionary attachmentInfo = new Dictionary(); + + if (fileNameHash.ContainsKey(filename)) + { + + filename = Path.GetFileNameWithoutExtension(filename) + '1' + extension; + } + Console.WriteLine("attachmentname: " + _attachment.FileName); + Console.WriteLine("attachmentpos: " + _attachment.RenderingPosition); + Console.WriteLine("attachmentmime: " + extension); + Console.WriteLine("attachmentsize: " + _attachment.Data.Length.ToString()); + fileNameHash.Add(filename, true); + attachmentInfo.Add("filename", _attachment.FileName); + attachmentInfo.Add("s3filename", filename); + attachmentInfo.Add("cid", _attachment.ContentId); + attachmentInfo.Add("size", _attachment.Data.Length.ToString()); + attachmentInfo.Add("lastmodified", _attachment.LastModificationTime.ToString()); + attachmentInfo.Add("created", _attachment.CreationTime.ToString()); + attachmentsObj.Add(attachmentStream, attachmentInfo); + } + } + } if (msg.BodyRtf != null) { - byte[] byteArray = Encoding.ASCII.GetBytes(System.Net.WebUtility.HtmlDecode(msg.BodyRtf)); + var msgReader = new Reader(); + + var inputStream = new MemoryStream(); + string body = msgReader.ExtractMsgEmailBody(SourceStream, ReaderHyperLinks.Both, "text/html; charset=utf-8", false); + var bodyreplaced = Regex.Replace(Regex.Replace(body.Replace("
", "
").Replace("", "").Replace("", ""), "=(?(?!utf-8)[\\w|-]+)", "=\"${tagname}\""), "", ""); + const string rtfInlineObject = "[*[RTFINLINEOBJECT]*]"; + const string imgString = "(); + foreach (Object attachment in msg.Attachments) + { + if (!attachment.GetType().FullName.ToLower().Contains("message")) + { + var _attachment = (Storage.Attachment)attachment; + if (htmlInline) + { + if (!String.IsNullOrEmpty(_attachment.ContentId) && bodyreplaced.Contains(_attachment.ContentId)) + { + inlineAttachments.Add(_attachment); + } + } + else if (rtfInline) + { + inlineAttachments.Add(_attachment); + } + } + } + foreach (var inlineAttachment in inlineAttachments.OrderBy(m => m.RenderingPosition)) + { + if (htmlInline) + { + var match = Regex.Match(bodyreplaced, ""); + var width = float.Parse(Regex.Match(match.Value, "(?<=width=\")\\d+").Value); + var height = float.Parse(Regex.Match(match.Value, "(?<=height=\")\\d+").Value); + const float maxSize = 750; + if (width > maxSize) + { + float scale = maxSize / width; + width = maxSize; + height = scale * height; + } + else if (height > maxSize) + { + float scale = maxSize / height; + height = maxSize; + width = scale * width; + } + bodyreplaced = Regex.Replace(bodyreplaced, "", ""); + foreach (KeyValuePair> attachment in attachmentsObj) + { + if (attachment.Value["cid"] == inlineAttachment.ContentId) + { + attachmentsObj.Remove(attachment.Key); + } + } + } + else if (rtfInline) + { + if (inlineAttachment.OleAttachment) + { + bodyreplaced = ReplaceFirstOccurrence(bodyreplaced, rtfInlineObject, ""); + foreach (KeyValuePair> attachment in attachmentsObj) + { + if (attachment.Value["filename"] == inlineAttachment.FileName) + { + attachmentsObj.Remove(attachment.Key); + } + } + } + else + { + bodyreplaced = ReplaceFirstOccurrence(bodyreplaced, rtfInlineObject, " [**Inline Attachment - " + inlineAttachment.FileName + "**]"); + } + } + } + } + + Console.WriteLine(bodyreplaced); + byte[] byteArray = Encoding.ASCII.GetBytes(bodyreplaced); using (MemoryStream messageStream = new MemoryStream(byteArray)) { - using (WordDocument rtfDoc = new WordDocument(messageStream, Syncfusion.DocIO.FormatType.Rtf)) + //messageStream.WriteTo(fs4); + using (WordDocument rtfDoc = new WordDocument(messageStream, Syncfusion.DocIO.FormatType.Html)) { + //rtfDoc.Save(fs3, FormatType.Docx); // Replace leading tabs, issue with syncfusion rtfDoc.ReplaceFirst = true; var regex = new Regex(@"(\r)*(\n)*(\t)+", RegexOptions.Multiline); var occurences = rtfDoc.Replace(regex, "\r\n"); + List pictures = rtfDoc.FindAllItemsByProperty(EntityType.Picture, "", ""); + + foreach (WPicture picture in pictures) + { + picture.LockAspectRatio = true; + const float maxSize = 500; + if (picture.Height > maxSize && picture.Height >= picture.Width) + { + var scale = (maxSize / picture.Height) * 100; + picture.HeightScale = scale; + picture.WidthScale = scale; + } + if (picture.Width > maxSize) + { + var scale = (maxSize / picture.Width) * 100; + picture.HeightScale = scale; + picture.WidthScale = scale; + } + } + //Gets all the hyperlink fields in the document List fields = rtfDoc.FindAllItemsByProperty(EntityType.Field, "FieldType", FieldType.FieldHyperlink.ToString()); @@ -138,7 +305,8 @@ public MSGFileProcessor(Stream sourceStream) } } - } else + } + else { WordDocument doc = GetEmailMetatdata(msg); doc.LastParagraph.AppendText("This email does not have a message body."); @@ -154,66 +322,15 @@ public MSGFileProcessor(Stream sourceStream) } } - + //string htmlString = GenerateHtmlfromMsg(msg); //bool isConverted; //(output, isConverted) = ConvertHTMLtoPDF(htmlString, output); - Dictionary fileNameHash = new(); - foreach (Object attachment in msg.Attachments) - { - attachmentStream = new(); - if (attachment.GetType().FullName.ToLower().Contains("message")) - { - var _attachment = (Storage.Message)attachment; - var filename = _attachment.FileName; - var extension = Path.GetExtension(filename); - if (!string.IsNullOrEmpty(extension)) - { - _attachment.Save(attachmentStream); - Dictionary attachmentInfo = new Dictionary(); - - if (fileNameHash.ContainsKey(filename)) - { - - filename = Path.GetFileNameWithoutExtension(filename) + '1' + extension; - } - fileNameHash.Add(filename, true); - attachmentInfo.Add("filename", _attachment.FileName); - attachmentInfo.Add("s3filename", filename); - attachmentInfo.Add("size", attachmentStream.Capacity.ToString()); - attachmentInfo.Add("lastmodified", _attachment.LastModificationTime.ToString()); - attachmentInfo.Add("created", _attachment.CreationTime.ToString()); - attachmentsObj.Add(attachmentStream, attachmentInfo); - } - } - else - { - var _attachment = (Storage.Attachment)attachment; - var filename = _attachment.FileName; - var extension = Path.GetExtension(filename); - - if (!string.IsNullOrEmpty(extension)) - { - attachmentStream.Write(_attachment.Data, 0, _attachment.Data.Length); - Dictionary attachmentInfo = new Dictionary(); + - if (fileNameHash.ContainsKey(filename)) - { - filename = Path.GetFileNameWithoutExtension(filename) + '1' + extension; - } - fileNameHash.Add(filename, true); - attachmentInfo.Add("filename", _attachment.FileName); - attachmentInfo.Add("s3filename", filename); - attachmentInfo.Add("size", _attachment.Data.Length.ToString()); - attachmentInfo.Add("lastmodified", _attachment.LastModificationTime.ToString()); - attachmentInfo.Add("created", _attachment.CreationTime.ToString()); - attachmentsObj.Add(attachmentStream, attachmentInfo); - } - } - } break; } catch (Exception e) @@ -345,7 +462,7 @@ private string GenerateHtmlfromMsg(Storage.Message msg) //Message body string message = @"" + msg.BodyText?.Replace("\n", "").Replace("<br>", "")?.Replace("<br/>", ""); - + message = message.Replace("<a", ""); htmlString.Append(@"Message Body: " + message.Replace("<br>", "").Replace("<br/>", "") + ""); @@ -371,10 +488,12 @@ private string GenerateHtmlfromMsg(Storage.Message msg) //Initialize HTML to PDF converter with Blink rendering engine HtmlToPdfConverter htmlConverter = new HtmlToPdfConverter(HtmlRenderingEngine.Blink); BlinkConverterSettings settings = new BlinkConverterSettings(); - settings.EnableHyperLink = false; + settings.EnableHyperLink = true; + settings.SinglePageLayout = SinglePageLayout.FitHeight; //Set command line arguments to run without sandbox. settings.CommandLineArguments.Add("--no-sandbox"); settings.CommandLineArguments.Add("--disable-setuid-sandbox"); + settings.Scale = 1.0F; htmlConverter.ConverterSettings = settings; //Convert HTML string to PDF PdfDocument document = htmlConverter.Convert(strHTML, ""); @@ -382,7 +501,7 @@ private string GenerateHtmlfromMsg(Storage.Message msg) document.Save(output); document.Close(true); isConverted = true; - + } catch (Exception ex) { @@ -489,6 +608,15 @@ protected WordDocument GetEmailMetatdata(MsgReader.Outlook.Storage.Message msg) return doc; } + private static string ReplaceFirstOccurrence(string text, string search, string replace) + { + var index = text.IndexOf(search, StringComparison.Ordinal); + if (index < 0) + return text; + + return text.Substring(0, index) + replace + text.Substring(index + search.Length); + } + protected virtual void Dispose(bool disposing) { if (disposing) diff --git a/api/reviewer_api/models/AnnotationSections.py b/api/reviewer_api/models/AnnotationSections.py index e5c6152b6..fd7b3815f 100644 --- a/api/reviewer_api/models/AnnotationSections.py +++ b/api/reviewer_api/models/AnnotationSections.py @@ -1,4 +1,4 @@ -from .db import db, ma +from .db import db, ma from .default_method_result import DefaultMethodResult from sqlalchemy import or_, and_ from sqlalchemy.orm import aliased @@ -12,25 +12,30 @@ from reviewer_api.utils.util import split, getbatchconfig import json + class AnnotationSection(db.Model): - __tablename__ = 'AnnotationSections' + __tablename__ = "AnnotationSections" # Defining the columns - id = db.Column(db.Integer, primary_key=True,autoincrement=True) + id = db.Column(db.Integer, primary_key=True, autoincrement=True) version = db.Column(db.Integer, primary_key=True, nullable=False) - foiministryrequestid =db.Column(db.Integer, nullable=True) + foiministryrequestid = db.Column(db.Integer, nullable=True) section = db.Column(JSON, unique=False, nullable=False) createdby = db.Column(JSON, unique=False, nullable=True) created_at = db.Column(db.DateTime, default=datetime.now) updatedby = db.Column(JSON, unique=False, nullable=True) updated_at = db.Column(db.DateTime, nullable=True) isactive = db.Column(db.Boolean, unique=False, nullable=False) - annotationname =db.Column(db.Integer, ForeignKey('Annotations.annotationname')) + annotationname = db.Column(db.Integer, ForeignKey("Annotations.annotationname")) - @classmethod def __getsectionkey(cls, _annotationname): try: - return db.session.query(AnnotationSection.id, AnnotationSection.version).filter(and_(AnnotationSection.annotationname == _annotationname)).order_by(AnnotationSection.version.desc()).first() + return ( + db.session.query(AnnotationSection.id, AnnotationSection.version) + .filter(and_(AnnotationSection.annotationname == _annotationname)) + .order_by(AnnotationSection.version.desc()) + .first() + ) except Exception as ex: logging.error(ex) finally: @@ -43,9 +48,14 @@ def __getbulksectionkey(cls, _annotationnames): sql = """select distinct on (annotationname) "annotationname", "version", id from "AnnotationSections" as2 where annotationname IN :annotationnames order by annotationname, version desc;""" - rs = db.session.execute(text(sql), {'annotationnames': tuple(_annotationnames)}) + rs = db.session.execute( + text(sql), {"annotationnames": tuple(_annotationnames)} + ) for row in rs: - apks[row['annotationname']] = {"id": row['id'], "version": row['version']} + apks[row["annotationname"]] = { + "id": row["id"], + "version": row["version"], + } except Exception as ex: logging.error(ex) finally: @@ -53,106 +63,153 @@ def __getbulksectionkey(cls, _annotationnames): return apks @classmethod - def savesections(cls, annots, _foiministryrequestid, userinfo)-> DefaultMethodResult: + def savesections( + cls, annots, _foiministryrequestid, userinfo + ) -> DefaultMethodResult: begin, size, limit = getbatchconfig() if len(annots) > 0 and len(annots) < begin: return cls.__chunksavesections(annots, _foiministryrequestid, userinfo) elif len(annots) >= begin and len(annots) <= limit: return cls.__bulksavesections(annots, _foiministryrequestid, userinfo, size) else: - return DefaultMethodResult(False, 'Invalid Annotation Section Request', -1) - + return DefaultMethodResult(False, "Invalid Annotation Section Request", -1) @classmethod - def __chunksavesections(cls, annots, _foiministryrequestid, userinfo)->DefaultMethodResult: + def __chunksavesections( + cls, annots, _foiministryrequestid, userinfo + ) -> DefaultMethodResult: successections = [] failedsections = [] try: for annot in annots: resp = cls.__savesection(annot, _foiministryrequestid, userinfo) if resp.success == True: - successections.append(annot["name"]) + successections.append(annot["name"]) else: failedsections.append(annot["name"]) if len(failedsections) < 1: - return DefaultMethodResult(True, 'Annotation sections are added', ','.join(successections)) + return DefaultMethodResult( + True, "Annotation sections are added", ",".join(successections) + ) else: - return DefaultMethodResult(True, 'Annotation sections are failed', ','.join(failedsections)) + return DefaultMethodResult( + True, "Annotation sections are failed", ",".join(failedsections) + ) except Exception as ex: logging.error(ex) finally: db.session.close() @classmethod - def __bulksavesections(cls, annots, _foiministryrequestid, userinfo, size=100)->DefaultMethodResult: - idxannots = [] + def __bulksavesections( + cls, annots, _foiministryrequestid, userinfo, size=100 + ) -> DefaultMethodResult: + idxannots = [] try: - wkannots = split(annots, size) + wkannots = split(annots, size) for wkannot in wkannots: - annotnames = [d['name'] for d in wkannot] - _pkvsections = cls.__getbulksectionkey(annotnames) - cls.__bulknewsections(wkannot, _pkvsections, _foiministryrequestid, userinfo) + annotnames = [d["name"] for d in wkannot] + _pkvsections = cls.__getbulksectionkey(annotnames) + cls.__bulknewsections( + wkannot, _pkvsections, _foiministryrequestid, userinfo + ) cls.__bulkarchivesections(annotnames, userinfo) idxannots.extend(annotnames) - return DefaultMethodResult(True, 'Annotations added', ','.join(idxannots)) + return DefaultMethodResult(True, "Annotations added", ",".join(idxannots)) except Exception as ex: logging.error(ex) @classmethod - def __savesection(cls, annot, _foiministryrequestid, userinfo) -> DefaultMethodResult: + def __savesection( + cls, annot, _foiministryrequestid, userinfo + ) -> DefaultMethodResult: sectkey = cls.__getsectionkey(annot["name"]) if sectkey is None: return cls.__newsection(annot, _foiministryrequestid, userinfo) else: - return cls.__updatesection(annot, _foiministryrequestid, userinfo, sectkey[0], sectkey[1]) - + return cls.__updatesection( + annot, _foiministryrequestid, userinfo, sectkey[0], sectkey[1] + ) @classmethod - def __newsection(cls, annot, _foiministryrequestid, userinfo) -> DefaultMethodResult: + def __newsection( + cls, annot, _foiministryrequestid, userinfo + ) -> DefaultMethodResult: try: - values = [{ - "annotationname": annot["name"], - "foiministryrequestid": _foiministryrequestid, - "section": annot["sectionsschema"], - "version": 1, - "createdby": userinfo, - "isactive": True - }] + values = [ + { + "annotationname": annot["name"], + "foiministryrequestid": _foiministryrequestid, + "section": annot["sectionsschema"], + "version": 1, + "createdby": userinfo, + "isactive": True, + } + ] insertstmt = insert(AnnotationSection).values(values) - upsertstmt = insertstmt.on_conflict_do_update(index_elements=[AnnotationSection.annotationname, AnnotationSection.version], set_={"isactive": False,"updatedby":userinfo,"updated_at":datetime.now()}).returning(AnnotationSection.isactive, AnnotationSection.id, AnnotationSection.version) + upsertstmt = insertstmt.on_conflict_do_update( + index_elements=[ + AnnotationSection.annotationname, + AnnotationSection.version, + ], + set_={ + "isactive": False, + "updatedby": userinfo, + "updated_at": datetime.now(), + }, + ).returning( + AnnotationSection.isactive, + AnnotationSection.id, + AnnotationSection.version, + ) sectproxy = db.session.execute(upsertstmt) - result = [dict(row) for row in sectproxy] - db.session.commit() + result = [dict(row) for row in sectproxy] + db.session.commit() if len(result) > 0: - idxsect = result[0] - if idxsect['isactive'] == False: - return cls.__updatesection(annot, _foiministryrequestid, userinfo, idxsect['id'], idxsect['version']) - return DefaultMethodResult(True, 'Annotation Sections are added', annot["name"]) + idxsect = result[0] + if idxsect["isactive"] == False: + return cls.__updatesection( + annot, + _foiministryrequestid, + userinfo, + idxsect["id"], + idxsect["version"], + ) + return DefaultMethodResult( + True, "Annotation Sections are added", annot["name"] + ) except Exception as ex: logging.error(ex) finally: - db.session.close() + db.session.close() @classmethod def __bulknewsections(cls, annots, _pkvannots, _foiministryrequestid, userinfo): datalist = [] idxannots = [] try: - for annot in annots: - pkkey = _pkvannots[annot["name"]] if _pkvannots not in (None, {}) else None - datalist.append({ - "annotationname": annot["name"], - "foiministryrequestid": _foiministryrequestid, - "section": annot["sectionsschema"], - "createdby": userinfo, - "isactive": True, - "version": pkkey['version'] + 1 if pkkey is not None and "version" in pkkey else 1, - "id": pkkey['id'] if pkkey is not None and "id" in pkkey else None - - }) + for annot in annots: + pkkey = ( + _pkvannots[annot["name"]] if _pkvannots not in (None, {}) else None + ) + datalist.append( + { + "annotationname": annot["name"], + "foiministryrequestid": _foiministryrequestid, + "section": annot["sectionsschema"], + "createdby": userinfo, + "isactive": True, + "version": pkkey["version"] + 1 + if pkkey is not None and "version" in pkkey + else 1, + "id": pkkey["id"] + if pkkey is not None and "id" in pkkey + else None, + } + ) idxannots.append(annot["name"]) db.session.bulk_insert_mappings(AnnotationSection, datalist) - db.session.commit() + db.session.commit() return idxannots except Exception as ex: logging.error(ex) @@ -160,81 +217,107 @@ def __bulknewsections(cls, annots, _pkvannots, _foiministryrequestid, userinfo): db.session.close() @classmethod - def __updatesection(cls, annot, _foiministryrequestid, userinfo, id= None, version=None) -> DefaultMethodResult: + def __updatesection( + cls, annot, _foiministryrequestid, userinfo, id=None, version=None + ) -> DefaultMethodResult: try: if id is None or version is None: - return DefaultMethodResult(True, 'Unable to Save Annotation Section', annot["name"]) - values = [{ - "id" : id, - "annotationname": annot["name"], - "foiministryrequestid": _foiministryrequestid, - "section": annot["sectionsschema"], - "createdby": userinfo, - "isactive": True, - "version": version + 1 - }] + return DefaultMethodResult( + True, "Unable to Save Annotation Section", annot["name"] + ) + values = [ + { + "id": id, + "annotationname": annot["name"], + "foiministryrequestid": _foiministryrequestid, + "section": annot["sectionsschema"], + "createdby": userinfo, + "isactive": True, + "version": version + 1, + } + ] insertstmt = insert(AnnotationSection).values(values) secttmt = insertstmt.on_conflict_do_nothing() - db.session.execute(secttmt) + db.session.execute(secttmt) db.session.commit() - cls.__archivesection(annot["name"], userinfo) - return DefaultMethodResult(True, 'Annotation sections are updated', annot["name"]) + cls.__archivesection(annot["name"], userinfo) + return DefaultMethodResult( + True, "Annotation sections are updated", annot["name"] + ) except Exception as ex: logging.error(ex) finally: - db.session.close() + db.session.close() @classmethod - def __archivesection(cls, _annotationname, userinfo)->DefaultMethodResult: + def __archivesection(cls, _annotationname, userinfo) -> DefaultMethodResult: return cls.__bulkarchivesections([_annotationname], userinfo) @classmethod - def __bulkarchivesections(cls, idxannots, userinfo)->DefaultMethodResult: + def __bulkarchivesections(cls, idxannots, 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) b + order by annotationname, version desc) as b where a.annotationname = b.annotationname and a."version" < b.version and a.isactive = true;""" - db.session.execute(text(sql), {'idxannots': tuple(idxannots), 'userinfo': json.dumps(userinfo)}) + db.session.execute( + text(sql), + {"idxannots": tuple(idxannots), "userinfo": json.dumps(userinfo)}, + ) db.session.commit() - return DefaultMethodResult(True, 'Annotation sections are updated', ','.join(idxannots)) + return DefaultMethodResult( + True, "Annotation sections are updated", ",".join(idxannots) + ) except Exception as ex: logging.error(ex) finally: db.session.close() - @classmethod - def bulkdeletesections(cls, idxannots, userinfo)->DefaultMethodResult: + def bulkdeletesections(cls, idxannots, userinfo) -> DefaultMethodResult: try: sql = """update "AnnotationSections" a set isactive = false, updatedby = :userinfo, updated_at=now() where a.annotationname in :idxannots and a.isactive = true;""" - db.session.execute(text(sql), {'idxannots': tuple(idxannots), 'userinfo': json.dumps(userinfo)}) + db.session.execute( + text(sql), + {"idxannots": tuple(idxannots), "userinfo": json.dumps(userinfo)}, + ) db.session.commit() - return DefaultMethodResult(True, 'Annotation sections are deleted', ','.join(idxannots)) + return DefaultMethodResult( + True, "Annotation sections are deleted", ",".join(idxannots) + ) except Exception as ex: logging.error(ex) finally: db.session.close() @classmethod - def getsectionmapping(cls, documentid, documentversion): + def getsectionmapping(cls, documentid, documentversion): mapping = [] - try: + 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}) - + 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"]}) + mapping.append( + { + "sectionannotationname": row["sectionannotationname"], + "redactannotation": row["redactannotation"], + "ids": row["ids"], + } + ) except Exception as ex: logging.error(ex) finally: @@ -249,18 +332,24 @@ def getsectionmappingbyrequestid(cls, ministryrequestid): cast("section" AS json) ->> 'redactannotation' as redactannotation, cast("section" AS json) ->> 'ids' as ids from "AnnotationSections" as2, "Annotations" a - join (select distinct on (d.documentid) d.* - from "Documents" d - where d.foiministryrequestid = :ministryrequestid - order by d.documentid, d.version desc) d + 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) where as2.annotationname = a.annotationname and a.isactive = true and as2.foiministryrequestid = :ministryrequestid and as2.isactive = true; """ - rs = db.session.execute(text(sql), {'ministryrequestid': ministryrequestid}) + rs = db.session.execute(text(sql), {"ministryrequestid": ministryrequestid}) for row in rs: - mapping.append({"annotationname":row["redactannotation"], "sectionannotation":row["annotationname"], "ids": row["ids"]}) + mapping.append( + { + "annotationname": row["redactannotation"], + "sectionannotation": row["annotationname"], + "ids": row["ids"], + } + ) except Exception as ex: logging.error(ex) finally: @@ -271,7 +360,16 @@ def getsectionmappingbyrequestid(cls, ministryrequestid): 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() + 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) @@ -283,11 +381,27 @@ def get_by_ministryid(cls, ministryrequestid): try: annotation_section_schema = AnnotationSectionSchema(many=True) redaction = aliased(Annotation) - query = db.session.query(AnnotationSection).join( - Annotation, Annotation.annotationname == AnnotationSection.annotationname - ).join( - redaction, redaction.annotationname == cast(AnnotationSection.section, JSON)['redactannotation'].astext - ).filter(and_(AnnotationSection.foiministryrequestid == ministryrequestid, AnnotationSection.isactive == True, Annotation.isactive == True, redaction.isactive == True)).all() + query = ( + db.session.query(AnnotationSection) + .join( + Annotation, + Annotation.annotationname == AnnotationSection.annotationname, + ) + .join( + redaction, + redaction.annotationname + == cast(AnnotationSection.section, JSON)["redactannotation"].astext, + ) + .filter( + and_( + AnnotationSection.foiministryrequestid == ministryrequestid, + AnnotationSection.isactive == True, + Annotation.isactive == True, + redaction.isactive == True, + ) + ) + .all() + ) return annotation_section_schema.dump(query) except Exception as ex: logging.error(ex) @@ -297,7 +411,7 @@ def get_by_ministryid(cls, ministryrequestid): @classmethod def getredactedsectionsbyrequest(cls, ministryrequestid): try: - sql = '''select section from public."Sections" where sectionid in + sql = """select section from public."Sections" where sectionid in (select distinct (json_array_elements((as1.section::json->>'ids')::json)->>'id')::integer from public."AnnotationSections" as1 join public."Annotations" a on a.annotationname = as1.annotationname @@ -308,11 +422,11 @@ def getredactedsectionsbyrequest(cls, ministryrequestid): 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}) + order by sortorder""" + rs = db.session.execute(text(sql), {"ministryrequestid": ministryrequestid}) sectionstring = "" for row in rs: - sectionstring = sectionstring + row["section"] + ', ' + sectionstring = sectionstring + row["section"] + ", " sectionstring = sectionstring[:-2] return sectionstring except Exception as ex: @@ -323,4 +437,13 @@ def getredactedsectionsbyrequest(cls, ministryrequestid): class AnnotationSectionSchema(ma.Schema): class Meta: - fields = ('annotationname', 'foiministryrequestid', 'section', 'id', 'createdby', 'created_at', 'updatedby', 'updated_at') + fields = ( + "annotationname", + "foiministryrequestid", + "section", + "id", + "createdby", + "created_at", + "updatedby", + "updated_at", + ) diff --git a/api/reviewer_api/models/Annotations.py b/api/reviewer_api/models/Annotations.py index bf18fef06..eb3511a6d 100644 --- a/api/reviewer_api/models/Annotations.py +++ b/api/reviewer_api/models/Annotations.py @@ -1,4 +1,4 @@ -from .db import db, ma +from .db import db, ma from .default_method_result import DefaultMethodResult from sqlalchemy import or_, and_, text from sqlalchemy.dialects.postgresql import JSON, insert @@ -8,14 +8,15 @@ import json from reviewer_api.utils.util import split, getbatchconfig + class Annotation(db.Model): - __tablename__ = 'Annotations' + __tablename__ = "Annotations" # Defining the columns - annotationid = db.Column(db.Integer, primary_key=True,autoincrement=True) + annotationid = db.Column(db.Integer, primary_key=True, autoincrement=True) version = db.Column(db.Integer, primary_key=True, nullable=False) annotationname = db.Column(db.String(120), unique=False, nullable=False) - documentid = db.Column(db.Integer, db.ForeignKey('Documents.documentid')) - documentversion = db.Column(db.Integer, db.ForeignKey('Documents.version')) + documentid = db.Column(db.Integer, db.ForeignKey("Documents.documentid")) + documentversion = db.Column(db.Integer, db.ForeignKey("Documents.version")) annotation = db.Column(db.Text, unique=False, nullable=False) pagenumber = db.Column(db.Integer, nullable=False) isactive = db.Column(db.Boolean, unique=False, nullable=False) @@ -23,17 +24,34 @@ class Annotation(db.Model): created_at = db.Column(db.DateTime, default=datetime.now) updatedby = db.Column(JSON, unique=False, nullable=True) updated_at = db.Column(db.DateTime, nullable=True) - - sections = relationship('AnnotationSection', primaryjoin="and_(Annotation.annotationname==AnnotationSection.annotationname)") - redactionlayerid = db.Column(db.Integer, db.ForeignKey('RedactionLayers.redactionlayerid')) - redactionlayer = relationship("RedactionLayer", backref=backref("RedactionLayers"), uselist=False) + sections = relationship( + "AnnotationSection", + primaryjoin="and_(Annotation.annotationname==AnnotationSection.annotationname)", + ) + redactionlayerid = db.Column( + db.Integer, db.ForeignKey("RedactionLayers.redactionlayerid") + ) + redactionlayer = relationship( + "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() + 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) @@ -42,12 +60,12 @@ def getannotations(cls, _documentid, _documentversion): @classmethod def getrequestannotations(cls, ministryrequestid, mappedlayerids): - sql = '''select a.* + sql = """select a.* from "Annotations" a - join (select distinct on (d.documentid) d.* - from "Documents" d - where d.foiministryrequestid = :ministryrequestid - order by d.documentid, d.version desc) d + 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) join "DocumentMaster" dm on dm.documentmasterid = d.documentmasterid left join "DocumentDeleted" dd on dm.filepath ilike dd.filepath || '%' @@ -55,70 +73,93 @@ def getrequestannotations(cls, ministryrequestid, mappedlayerids): and (dd.deleted is false or dd.deleted is null) and a.redactionlayerid in :_mappedlayerids and a.isactive = true - ''' - rs = db.session.execute(text(sql), {'ministryrequestid': ministryrequestid, '_mappedlayerids':tuple(mappedlayerids)}) + """ + rs = db.session.execute( + text(sql), + { + "ministryrequestid": ministryrequestid, + "_mappedlayerids": tuple(mappedlayerids), + }, + ) 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] + 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 getrequestdivisionannotations(cls, ministryrequestid, divisionid): - sql = ''' + sql = """ select a.* from "Annotations" a join ( - select distinct on (d.documentid) d.* - from "Documents" d - where d.foiministryrequestid = :ministryrequestid - order by d.documentid, d.version desc - ) d on (d.documentid = a.documentid and d.version = a.documentversion) + 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}) + """ + 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] + 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(cls, _documentid, _documentversion, _pagenum, redactionlayerid): + def getredactionsbypage( + cls, _documentid, _documentversion, _pagenum, redactionlayerid + ): try: annotation_schema = AnnotationSchema(many=True) - query = db.session.query(Annotation).filter( - and_( - Annotation.documentid == _documentid, - Annotation.documentversion == _documentversion, - Annotation.isactive==True, - Annotation.pagenumber == _pagenum-1, - Annotation.redactionlayerid == redactionlayerid, - Annotation.annotation.ilike('% DefaultMethodResult: + def saveannotations(cls, annots, redactionlayerid, userinfo) -> DefaultMethodResult: begin, size, limit = getbatchconfig() if len(annots) > 0 and len(annots) < begin: return cls.__chunksaveannotations(annots, redactionlayerid, userinfo) elif len(annots) >= begin and len(annots) <= limit: return cls.__bulksaveannotations(annots, redactionlayerid, userinfo, size) else: - return DefaultMethodResult(False, 'Invalid Annotation Request', -1) + return DefaultMethodResult(False, "Invalid Annotation Request", -1) @classmethod - def __chunksaveannotations(cls, annots, redactionlayerid, userinfo)->DefaultMethodResult: + def __chunksaveannotations( + cls, annots, redactionlayerid, userinfo + ) -> DefaultMethodResult: successannots = [] failedannots = [] try: for annot in annots: resp = cls.__saveannotation(annot, redactionlayerid, userinfo) if resp is not None and resp.success == True: - successannots.append(annot["name"]) + successannots.append(annot["name"]) else: - failedannots.append(annot["name"]) + failedannots.append(annot["name"]) if len(failedannots) < 1: - return DefaultMethodResult(True, 'Annotations added', ','.join(successannots)) + return DefaultMethodResult( + True, "Annotations added", ",".join(successannots) + ) else: - return DefaultMethodResult(True, 'Annotations failed', ','.join(failedannots)) + return DefaultMethodResult( + True, "Annotations failed", ",".join(failedannots) + ) except Exception as ex: logging.error(ex) @classmethod - def __bulksaveannotations(cls, annots, redactionlayerid, userinfo, size=100)->DefaultMethodResult: - idxannots = [] + def __bulksaveannotations( + cls, annots, redactionlayerid, userinfo, size=100 + ) -> DefaultMethodResult: + idxannots = [] try: - wkannots = split(annots, size) + wkannots = split(annots, size) for wkannot in wkannots: - annotnames = [d['name'] for d in wkannot] - _pkvannots = cls.__getbulkannotationkey(annotnames) - cls.__bulknewannotations(wkannot, _pkvannots, redactionlayerid, userinfo) + annotnames = [d["name"] for d in wkannot] + _pkvannots = cls.__getbulkannotationkey(annotnames) + cls.__bulknewannotations( + wkannot, _pkvannots, redactionlayerid, userinfo + ) cls.__bulkarchiveannotation(annotnames, redactionlayerid, userinfo) idxannots.extend(annotnames) - return DefaultMethodResult(True, 'Annotations added', ','.join(idxannots)) + return DefaultMethodResult(True, "Annotations added", ",".join(idxannots)) except Exception as ex: logging.error(ex) @@ -221,137 +300,206 @@ def __saveannotation(cls, annot, redactionlayerid, userinfo) -> DefaultMethodRes if annotkey is None: return cls.__newannotation(annot, redactionlayerid, userinfo) else: - return cls.__updateannotation(annot, redactionlayerid, userinfo, annotkey[0], annotkey[1]) - + return cls.__updateannotation( + annot, redactionlayerid, userinfo, annotkey[0], annotkey[1] + ) @classmethod def __newannotation(cls, annot, redactionlayerid, userinfo) -> DefaultMethodResult: try: - values = [{ - "annotationname": annot["name"], - "documentid": annot["docid"], - "documentversion": 1, - "annotation": annot["xml"], - "pagenumber": annot["page"], - "createdby": userinfo, - "isactive": True, - "version": 1, - "redactionlayerid" : redactionlayerid - }] + values = [ + { + "annotationname": annot["name"], + "documentid": annot["docid"], + "documentversion": 1, + "annotation": annot["xml"], + "pagenumber": annot["page"], + "createdby": userinfo, + "isactive": True, + "version": 1, + "redactionlayerid": redactionlayerid, + } + ] insertstmt = insert(Annotation).values(values) - upsertstmt = insertstmt.on_conflict_do_update(index_elements=[Annotation.annotationname, Annotation.version], set_={"isactive": False,"updatedby":userinfo,"updated_at":datetime.now()}).returning(Annotation.isactive, Annotation.annotationid, Annotation.version) + upsertstmt = insertstmt.on_conflict_do_update( + index_elements=[Annotation.annotationname, Annotation.version], + set_={ + "isactive": False, + "updatedby": userinfo, + "updated_at": datetime.now(), + }, + ).returning( + Annotation.isactive, Annotation.annotationid, Annotation.version + ) annotproxy = db.session.execute(upsertstmt) - result = [dict(row) for row in annotproxy] - db.session.commit() + result = [dict(row) for row in annotproxy] + db.session.commit() if len(result) > 0: - idxannot = result[0] - if idxannot['isactive'] == False: - return cls.__updateannotation(annot, redactionlayerid, userinfo, idxannot['annotationid'], idxannot['version']) - return DefaultMethodResult(True, 'Annotation added', annot["name"]) + idxannot = result[0] + if idxannot["isactive"] == False: + return cls.__updateannotation( + annot, + redactionlayerid, + userinfo, + idxannot["annotationid"], + idxannot["version"], + ) + return DefaultMethodResult(True, "Annotation added", annot["name"]) except Exception as ex: logging.error(ex) finally: - db.session.close() + db.session.close() @classmethod def __bulknewannotations(cls, annots, _pkvannots, redactionlayerid, userinfo): datalist = [] idxannots = [] try: - for annot in annots: - pkkey = _pkvannots[annot["name"]] if _pkvannots not in (None, {}) else None - datalist.append({ - "annotationname": annot["name"], - "documentid": annot["docid"], - "documentversion": 1, - "annotation": annot["xml"], - "pagenumber": annot["page"], - "createdby": userinfo, - "isactive": True, - "redactionlayerid" : redactionlayerid, - "version": pkkey['version'] + 1 if pkkey is not None and "version" in pkkey else 1, - "annotationid": pkkey['annotationid'] if pkkey is not None and "annotationid" in pkkey else None - }) + for annot in annots: + pkkey = ( + _pkvannots[annot["name"]] if _pkvannots not in (None, {}) else None + ) + datalist.append( + { + "annotationname": annot["name"], + "documentid": annot["docid"], + "documentversion": 1, + "annotation": annot["xml"], + "pagenumber": annot["page"], + "createdby": userinfo, + "isactive": True, + "redactionlayerid": redactionlayerid, + "version": pkkey["version"] + 1 + if pkkey is not None and "version" in pkkey + else 1, + "annotationid": pkkey["annotationid"] + if pkkey is not None and "annotationid" in pkkey + else None, + } + ) idxannots.append(annot["name"]) db.session.bulk_insert_mappings(Annotation, datalist) - db.session.commit() + db.session.commit() return idxannots except Exception as ex: logging.error(ex) finally: - db.session.close() + db.session.close() @classmethod - def __updateannotation(cls, annot, redactionlayerid, userinfo, id=None, version=None) -> DefaultMethodResult: + def __updateannotation( + cls, annot, redactionlayerid, userinfo, id=None, version=None + ) -> DefaultMethodResult: try: if id is None or version is None: - return DefaultMethodResult(True, 'Unable to Save Annotation', annot["name"]) - values = [{ - "annotationid" : id, - "annotationname": annot["name"], - "documentid": annot["docid"], - "documentversion": annot["docversion"], - "annotation": annot["xml"], - "pagenumber": annot["page"], - "createdby": userinfo, - "isactive": True, - "version": version + 1, - "redactionlayerid" : redactionlayerid - }] + return DefaultMethodResult( + True, "Unable to Save Annotation", annot["name"] + ) + values = [ + { + "annotationid": id, + "annotationname": annot["name"], + "documentid": annot["docid"], + "documentversion": annot["docversion"], + "annotation": annot["xml"], + "pagenumber": annot["page"], + "createdby": userinfo, + "isactive": True, + "version": version + 1, + "redactionlayerid": redactionlayerid, + } + ] insertstmt = insert(Annotation).values(values) annotstmt = insertstmt.on_conflict_do_nothing() - db.session.execute(annotstmt) - db.session.commit() + db.session.execute(annotstmt) + db.session.commit() cls.__archiveannotation(annot["name"], redactionlayerid, userinfo) - return DefaultMethodResult(True, 'Annotation updated', annot["name"]) + return DefaultMethodResult(True, "Annotation updated", annot["name"]) except Exception as ex: logging.error(ex) finally: - db.session.close() - + db.session.close() + @classmethod - def __archiveannotation(cls, _annotationname, redactionlayerid, userinfo)->DefaultMethodResult: - return cls.__bulkarchiveannotation([_annotationname], redactionlayerid, userinfo) + def __archiveannotation( + cls, _annotationname, redactionlayerid, userinfo + ) -> DefaultMethodResult: + return cls.__bulkarchiveannotation( + [_annotationname], redactionlayerid, userinfo + ) - @classmethod - def __bulkarchiveannotation(cls, idxannots, redactionlayerid, userinfo)->DefaultMethodResult: + def __bulkarchiveannotation( + cls, idxannots, redactionlayerid, userinfo + ) -> DefaultMethodResult: try: sql = """update "Annotations" a set isactive = false, updatedby = :userinfo, updated_at=now() from (select distinct on (annotationname) "annotationname", "version", annotationid from "Annotations" where annotationname in :idxannots and redactionlayerid = :redactionlayerid - order by annotationname, version desc) b + order by annotationname, version desc) as b where a.annotationname = b.annotationname and a."version" < b.version and a.redactionlayerid = :redactionlayerid and a.isactive = True""" - db.session.execute(text(sql), {'idxannots': tuple(idxannots), 'userinfo': json.dumps(userinfo), 'redactionlayerid': redactionlayerid}) + db.session.execute( + text(sql), + { + "idxannots": tuple(idxannots), + "userinfo": json.dumps(userinfo), + "redactionlayerid": redactionlayerid, + }, + ) db.session.commit() - return DefaultMethodResult(True, 'Annotations are updated', ','.join(idxannots)) + return DefaultMethodResult( + True, "Annotations are updated", ",".join(idxannots) + ) except Exception as ex: logging.error(ex) finally: db.session.close() - @classmethod - def bulkdeleteannotations(cls, idxannots, redactionlayerid, userinfo)->DefaultMethodResult: + def bulkdeleteannotations( + cls, idxannots, redactionlayerid, userinfo + ) -> DefaultMethodResult: try: sql = """update "Annotations" a set isactive = false, updatedby = :userinfo, updated_at=now() where a.annotationname in :idxannots and a.redactionlayerid = :redactionlayerid and a.isactive = True""" - db.session.execute(text(sql), {'idxannots': tuple(idxannots), 'userinfo': json.dumps(userinfo), 'redactionlayerid': redactionlayerid}) + db.session.execute( + text(sql), + { + "idxannots": tuple(idxannots), + "userinfo": json.dumps(userinfo), + "redactionlayerid": redactionlayerid, + }, + ) db.session.commit() - return DefaultMethodResult(True, 'Annotations are deleted', ','.join(idxannots)) + return DefaultMethodResult( + True, "Annotations are deleted", ",".join(idxannots) + ) except Exception as ex: logging.error(ex) finally: db.session.close() - class AnnotationSchema(ma.Schema): class Meta: - fields = ('annotationid', 'annotationname', 'documentid', 'documentversion', 'annotation', 'pagenumber', 'redactionlayerid', 'redactionlayer.redactionlayerid', 'isactive', 'createdby', 'created_at', 'updatedby', 'updated_at') \ No newline at end of file + fields = ( + "annotationid", + "annotationname", + "documentid", + "documentversion", + "annotation", + "pagenumber", + "redactionlayerid", + "redactionlayer.redactionlayerid", + "isactive", + "createdby", + "created_at", + "updatedby", + "updated_at", + ) diff --git a/api/reviewer_api/models/RedactionLayers.py b/api/reviewer_api/models/RedactionLayers.py index cef81f608..b628ad1e7 100644 --- a/api/reviewer_api/models/RedactionLayers.py +++ b/api/reviewer_api/models/RedactionLayers.py @@ -1,15 +1,16 @@ -from .db import db, ma +from .db import db, ma from .default_method_result import DefaultMethodResult from datetime import datetime as datetime2 from sqlalchemy import or_, and_, text import logging + class RedactionLayer(db.Model): - __tablename__ = 'RedactionLayers' + __tablename__ = "RedactionLayers" # Defining the columns - redactionlayerid = db.Column(db.Integer, primary_key=True,autoincrement=True) - name = db.Column(db.String(255), unique=False, nullable=False) - description = db.Column(db.String(255), unique=False, nullable=False) + redactionlayerid = db.Column(db.Integer, primary_key=True, autoincrement=True) + name = db.Column(db.String(255), unique=False, nullable=False) + description = db.Column(db.String(255), unique=False, nullable=False) sortorder = db.Column(db.String(100), unique=False, nullable=True) isactive = db.Column(db.Boolean, unique=False, nullable=False) createdby = db.Column(db.String(120), unique=False, nullable=True) @@ -20,7 +21,7 @@ class RedactionLayer(db.Model): @classmethod def getall(cls, ministryrequestid): try: - sql = '''select rl.*, case when sq.count is null then 0 else sq.count end as count + sql = """select rl.*, case when sq.count is null then 0 else sq.count end as count from public."RedactionLayers" rl left join ( select redactionlayerid as rlid, count(redactionlayerid) from public."Annotations" a @@ -30,16 +31,19 @@ def getall(cls, ministryrequestid): where foiministryrequestid = :ministryrequestid and a.isactive = true and (dd.deleted is false or dd.deleted is null) group by redactionlayerid - ) sq on sq.rlid = rl.redactionlayerid - ''' - rs = db.session.execute(text(sql), {'ministryrequestid': ministryrequestid}) - return [{ - 'redactionlayerid': row['redactionlayerid'], - 'name': row['name'], - 'description': row['description'], - 'sortorder': row['sortorder'], - 'count': row['count'], - } for row in rs] + ) as sq on sq.rlid = rl.redactionlayerid + """ + rs = db.session.execute(text(sql), {"ministryrequestid": ministryrequestid}) + return [ + { + "redactionlayerid": row["redactionlayerid"], + "name": row["name"], + "description": row["description"], + "sortorder": row["sortorder"], + "count": row["count"], + } + for row in rs + ] except Exception as ex: logging.error(ex) finally: @@ -49,14 +53,19 @@ def getall(cls, ministryrequestid): def getredlineredactionlayer(cls): try: pageflag_schema = RedactionLayerSchema(many=False) - query = db.session.query(RedactionLayer).filter_by(isactive=True, name ='Redline').order_by(RedactionLayer.sortorder.desc()).first() + query = ( + db.session.query(RedactionLayer) + .filter_by(isactive=True, name="Redline") + .order_by(RedactionLayer.sortorder.desc()) + .first() + ) return pageflag_schema.dump(query) except Exception as ex: logging.error(ex) finally: - db.session.close() - + db.session.close() + class RedactionLayerSchema(ma.Schema): class Meta: - fields = ('redactionlayerid', 'name', 'description','sortorder') \ No newline at end of file + fields = ("redactionlayerid", "name", "description", "sortorder") diff --git a/web/src/apiManager/services/docReviewerService.tsx b/web/src/apiManager/services/docReviewerService.tsx index 0ccb6a6e4..f2c5f603a 100644 --- a/web/src/apiManager/services/docReviewerService.tsx +++ b/web/src/apiManager/services/docReviewerService.tsx @@ -362,11 +362,11 @@ export const fetchPDFTronLicense = ( if (res.data) { callback(res.data); } else { - throw new Error("Error while triggering download final package"); + throw new Error("Error in fetching PDFTronLicense"); } }) .catch((error:any) => { - errorCallback("Error in triggering download final package:",error); + errorCallback("Error in fetching PDFTronLicense:",error); }); return response; }; \ No newline at end of file diff --git a/web/src/components/FOI/Home/ContextMenu.tsx b/web/src/components/FOI/Home/ContextMenu.tsx index df678d909..6e2c5f468 100644 --- a/web/src/components/FOI/Home/ContextMenu.tsx +++ b/web/src/components/FOI/Home/ContextMenu.tsx @@ -33,7 +33,7 @@ const ContextMenu = ({ const popoverEnter = (e: any) => { setOrgListAnchorPosition( - e.currentTarget.getBoundingClientRect() + e?.currentTarget?.getBoundingClientRect() ); setOpenConsultPopup(true) }; diff --git a/web/src/components/FOI/Home/DocumentSelector.tsx b/web/src/components/FOI/Home/DocumentSelector.tsx index f3e262025..6aa7962ac 100644 --- a/web/src/components/FOI/Home/DocumentSelector.tsx +++ b/web/src/components/FOI/Home/DocumentSelector.tsx @@ -306,7 +306,7 @@ const DocumentSelector = ({ } setOpenContextPopup(true); setAnchorPosition( - e.currentTarget.getBoundingClientRect() + e?.currentTarget?.getBoundingClientRect() ); setDisableHover(true); } diff --git a/web/src/components/FOI/Home/Edit.tsx b/web/src/components/FOI/Home/Edit.tsx new file mode 100644 index 000000000..1a7cb8f03 --- /dev/null +++ b/web/src/components/FOI/Home/Edit.tsx @@ -0,0 +1,88 @@ +import { ReactComponent as EditLogo } from "../../../assets/images/icon-pencil-line.svg"; + + const disableMultiSelectEdit = (_selectedAnnotations: any) => { + if (_selectedAnnotations && _selectedAnnotations.length > 0) { + return _selectedAnnotations.some( + (obj: any) => + obj.Subject !== "Redact" && obj.getCustomData("sections") === "" + ); + } + return true; + }; + + //START: Bulk Edit using Multi Select Option + const MultiSelectEdit = ({docInstance, editRedactions}: any) => { + let _selectedAnnotations = + docInstance?.Core?.annotationManager.getSelectedAnnotations(); + const disableEdit = disableMultiSelectEdit(_selectedAnnotations); + const _selectedRedactions = _selectedAnnotations?.filter( + (obj: any) => + obj.Subject !== "Redact" && obj.getCustomData("sections") !== "" + ); + return ( + + ); + }; + //END: Bulk Edit using Multi Select Option + + + const Edit = ({instance, editAnnotation}: any) => { + let _selectedAnnotations = instance?.Core?.annotationManager.getSelectedAnnotations(); + const disableEdit = _selectedAnnotations.some( + (obj: any) => + obj.Subject !== "Redact" && obj.getCustomData("sections") === "" + ); + const _selectedRedaction = _selectedAnnotations.filter( + (obj: any) => obj.Subject === "Redact" + ); + return ( + + ); + }; + + export { + MultiSelectEdit, + Edit + }; \ No newline at end of file diff --git a/web/src/components/FOI/Home/Redlining.js b/web/src/components/FOI/Home/Redlining.js index 9f3cc971a..93ba2b124 100644 --- a/web/src/components/FOI/Home/Redlining.js +++ b/web/src/components/FOI/Home/Redlining.js @@ -52,7 +52,11 @@ import { getStitchedPageNoFromOriginal, createPageFlagPayload, sortByLastModified, + createRedactionSectionsString, + getSections, + getValidSections, } from "./utils"; +import { Edit, MultiSelectEdit } from "./Edit"; import _, { forEach } from "lodash"; const Redlining = React.forwardRef( @@ -126,10 +130,8 @@ const Redlining = React.forwardRef( // State variables for Bulk Edit using Multi Selection option const [editRedacts, setEditRedacts] = useState(null); - const [multiSelectedAnnotations, setMultiSelectedAnnotations] = useState( - [] - ); const [multiSelectFooter, setMultiSelectFooter] = useState(null); + const [enableMultiSelect, setEnableMultiSelect] = useState(false); //xml parser const parser = new XMLParser(); @@ -195,56 +197,15 @@ const Redlining = React.forwardRef( isReadyForSignOff() && requestStatus == RequestStates["Response"] ); - //START: Bulk Edit using Multi Select Option - const MultiSelectEdit = () => { - let _selectedAnnotations = multiSelectedAnnotations; - const disableEdit = - _selectedAnnotations.length === 0 || - _selectedAnnotations?.some( - (obj) => - obj.Subject !== "Redact" && obj.getCustomData("sections") === "" - ); - const _selectedRedactions = _selectedAnnotations?.filter( - (obj) => - obj.Subject !== "Redact" && obj.getCustomData("sections") !== "" - ); - return ( - - ); - }; - //END: Bulk Edit using Multi Select Option - // const [storedannotations, setstoreannotations] = useState(localStorage.getItem("storedannotations") || []) // if using a class, equivalent of componentDidMount useEffect(() => { let initializeWebViewer = async () => { let currentDocumentS3Url = currentDocument?.currentDocumentS3Url; fetchSections(requestid, (error) => console.log(error)); - let response = await fetchPDFTronLicense( - null, - (error) => console.log(error) - ) + let response = await fetchPDFTronLicense(null, (error) => + console.log(error) + ); WebViewer( { licenseKey: response.data.license, @@ -395,45 +356,12 @@ const Redlining = React.forwardRef( ); }); - const Edit = () => { - let _selectedAnnotations = annotationManager.getSelectedAnnotations(); - const disableEdit = _selectedAnnotations.some( - (obj) => - obj.Subject !== "Redact" && obj.getCustomData("sections") === "" - ); - const _selectedRedaction = _selectedAnnotations.filter( - (obj) => obj.Subject === "Redact" - ); - return ( - - ); - }; - instance.UI.annotationPopup.add({ type: "customElement", title: "Edit", - render: () => , + render: () => ( + + ), }); setDocInstance(instance); @@ -511,7 +439,7 @@ const Redlining = React.forwardRef( multiDeleteButton?.addEventListener("click", function () { root = null; setMultiSelectFooter(root); - setMultiSelectedAnnotations([]); + setEnableMultiSelect(false); }); } @@ -521,7 +449,7 @@ const Redlining = React.forwardRef( closeButton?.addEventListener("click", function () { root = null; setMultiSelectFooter(root); - setMultiSelectedAnnotations([]); + setEnableMultiSelect(false); }); } @@ -535,7 +463,7 @@ const Redlining = React.forwardRef( if (isActive) { root = null; setMultiSelectFooter(root); - setMultiSelectedAnnotations([]); + setEnableMultiSelect(false); } }); @@ -553,40 +481,19 @@ const Redlining = React.forwardRef( _multiSelectFooter.firstChild ); } - const listItems = document.querySelectorAll('[role="listitem"]'); + const listItems = + document.querySelectorAll('[role="listitem"]'); listItems.forEach((listItem) => { - let checkbox = listItem.querySelector('input[type="checkbox"]'); + let checkbox = listItem.querySelector( + 'input[type="checkbox"]' + ); if (checkbox) { if (root === null) { root = createRoot(editButton); setMultiSelectFooter(root); } checkbox.addEventListener("click", function () { - checkbox = listItem.querySelector('input[type="checkbox"]'); - const spanWrapper = checkbox.parentElement.parentElement; // Get the parent span element - // Check if the span element has the "ui__choice--checked" class - const isChecked = spanWrapper.classList.contains( - "ui__choice--checked" - ); //actual scenario isChecked = true when you uncheck the checkbox - const selectedIdString = checkbox.id?.split("_"); - if (selectedIdString.length > 0) { - const annotationName = selectedIdString[1]; - const _annot = - annotationManager.getAnnotationById(annotationName); - setMultiSelectedAnnotations((prevSelectedAnnotations) => { - const isExists = prevSelectedAnnotations.find( - (_annotation) => _annotation.Id === annotationName - ); - if (isExists === undefined && !isChecked) { - return [...prevSelectedAnnotations, _annot]; - } else if (isExists && isChecked) { - return prevSelectedAnnotations.filter( - (_annotation) => _annotation.Id !== annotationName - ); - } - return prevSelectedAnnotations; - }); - } + setEnableMultiSelect(true); }); } }); @@ -596,19 +503,10 @@ const Redlining = React.forwardRef( true ); }); - } + }; initializeWebViewer(); }, []); - //START: UE to render MultiSelectEdit part of Bulk Edit using Multi Select Option - useEffect(() => { - if (multiSelectFooter) { - multiSelectFooter.render(); - } - }, [multiSelectedAnnotations]); - - //END: UE to render MultiSelectEdit part of Bulk Edit using Multi Select Option - useEffect(() => { const changeLayer = async () => { if (currentLayer) { @@ -631,11 +529,12 @@ const Redlining = React.forwardRef( ); } await annotManager.ungroupAnnotations(existingAnnotations); - await annotManager.deleteAnnotations(redactions, { - imported: true, - force: true, - source: "layerchange", - }); + await annotManager.hideAnnotations(redactions); + // await annotManager.deleteAnnotations(redactions, { + // imported: true, + // force: true, + // source: "layerchange", + // }); var newAnnots = []; for (const rect of rects) { const annot = new annots.RectangleAnnotation(); @@ -724,14 +623,11 @@ const Redlining = React.forwardRef( let displayedDoc = pageMappedDocs.stitchedPageLookup[annot.getPageNumber()]; let individualPageNo = displayedDoc.page; - annot.setCustomData( - "originalPageNo", - JSON.stringify(individualPageNo - 1) - ); + annot.setCustomData("originalPageNo", `${individualPageNo - 1}`); }); let _annotationtring = docInstance.Core.annotationManager.exportAnnotations({ - annotList: annotations, + annotationList: annotations, useDisplayAuthor: true, }); _annotationtring.then(async (astr) => { @@ -813,11 +709,11 @@ const Redlining = React.forwardRef( if (!!parentRedaction) { annotations[i].setCustomData( "existingId", - parentRedaction?.Id + `${parentRedaction?.Id}` ); annotations[i].setCustomData( "existingFreeTextId", - _selectedAnnotations?.Id + `${_selectedAnnotations?.Id}` ); } } else { @@ -828,14 +724,17 @@ const Redlining = React.forwardRef( }); } annotations[i].NoMove = true; - annotations[i].setCustomData("docid", displayedDoc.docid); + annotations[i].setCustomData( + "docid", + `${displayedDoc.docid}` + ); annotations[i].setCustomData( "docversion", - displayedDoc.docversion + `${displayedDoc.docversion}` ); annotations[i].setCustomData( "redactionlayerid", - currentLayer.redactionlayerid + `${currentLayer.redactionlayerid}` ); annotations[i].IsHoverable = false; }); @@ -843,7 +742,7 @@ const Redlining = React.forwardRef( let annot = annots[0].children[0]; let astr = await docInstance.Core.annotationManager.exportAnnotations({ - annotList: annotations, + annotationList: annotations, useDisplayAuthor: true, }); setNewRedaction({ @@ -856,18 +755,21 @@ const Redlining = React.forwardRef( for (let annot of annotations) { displayedDoc = pageMappedDocs.stitchedPageLookup[Number(annot.PageNumber)]; - annot.setCustomData("docid", displayedDoc.docid); - annot.setCustomData("docversion", displayedDoc.docversion); + annot.setCustomData("docid", `${displayedDoc.docid}`); + annot.setCustomData( + "docversion", + `${displayedDoc.docversion}` + ); annot.setCustomData( "redactionlayerid", - currentLayer.redactionlayerid + `${currentLayer.redactionlayerid}` ); annot.NoMove = true; } let astr = await docInstance.Core.annotationManager.exportAnnotations({ - annotList: annotations, + annotationList: annotations, useDisplayAuthor: true, }); @@ -972,7 +874,7 @@ const Redlining = React.forwardRef( } } }); - docInstance.Core.Annotations.NoMove=true; + docInstance.Core.Annotations.NoMove = true; setAnnots(docInstance.Core.Annotations); } }, @@ -987,25 +889,14 @@ const Redlining = React.forwardRef( useEffect(() => { annotManager?.addEventListener("annotationSelected", (annotations) => { - //START - handled grouped annotation selection due to auto multi select issue part of Bulk Edit using Multi Select Option. - // get freetext annotations with redaction sections alone - const _selectedRedactions = annotations.filter( - (obj) => - obj.Subject !== "Redact" && obj.getCustomData("sections") !== "" - ); - let annotationName = _selectedRedactions[0]?.Id; - if (annotationName) { - setMultiSelectedAnnotations((prevSelectedAnnotations) => { - const isExists = prevSelectedAnnotations.find( - (_annotation) => _annotation.Id === annotationName - ); - if (isExists === undefined) { - return [...prevSelectedAnnotations, _selectedRedactions[0]]; - } - return prevSelectedAnnotations; - }); + if (multiSelectFooter && enableMultiSelect) { + multiSelectFooter.render( + + ); } - //END - handled grouped annotation selection due to auto multi select issue part of Bulk Edit using Multi Select Option. }); annotManager?.removeEventListener( @@ -1028,6 +919,8 @@ const Redlining = React.forwardRef( newRedaction, pageSelections, redlineSaving, + multiSelectFooter, + enableMultiSelect, ]); useImperativeHandle(ref, () => ({ @@ -1200,7 +1093,7 @@ const Redlining = React.forwardRef( _annotation.NoMove = true; if (_annotation.Subject === "Redact") { _annotation.IsHoverable = false; - + if (_annotation.type === "fullPage") { _annotation.NoResize = true; annotManager.bringToBack(_annotation); @@ -1264,29 +1157,22 @@ const Redlining = React.forwardRef( childAnnotation.Y = _redact.Y; childAnnotation.FontSize = _redact.FontSize; if (redactionSectionsIds.length > 0) { - let redactionSections = sections - .filter((s) => redactionSectionsIds.indexOf(s.id) > -1) - .map((s) => s.section) - .join(", "); - if ( - redactionSectionsIds?.length == 1 && - redactionSectionsIds[0] === 25 - ) { - redactionSections = " "; - } + let redactionSections = createRedactionSectionsString( + sections, + redactionSectionsIds + ); childAnnotation.setContents(redactionSections); const displayedDoc = pageMappedDocs.stitchedPageLookup[Number(node.attributes.page) + 1]; childAnnotation.setCustomData( "sections", - JSON.stringify( - sections - .filter((s) => redactionSectionsIds.indexOf(s.id) > -1) - .map((s) => ({ id: s.id, section: s.section })) - ) + JSON.stringify(getSections(sections, redactionSectionsIds)) + ); + childAnnotation.setCustomData("docid", `${displayedDoc.docid}`); + childAnnotation.setCustomData( + "docversion", + `${displayedDoc.docversion}` ); - childAnnotation.setCustomData("docid", displayedDoc.docid); - childAnnotation.setCustomData("docversion", displayedDoc.docversion); } const doc = docViewer.getDocument(); let pageNumber = parseInt(node.attributes.page) + 1; @@ -1328,7 +1214,7 @@ const Redlining = React.forwardRef( }); } setSelectedSections([]); - setMultiSelectedAnnotations([]); + setEnableMultiSelect(false); setEditRedacts(null); }); @@ -1366,16 +1252,10 @@ const Redlining = React.forwardRef( childAnnotation = getCoordinates(childAnnotation, redaction, X); let redactionSectionsIds = selectedSections; if (redactionSectionsIds.length > 0) { - let redactionSections = sections - .filter((s) => redactionSectionsIds.indexOf(s.id) > -1) - .map((s) => s.section) - .join(", "); - if ( - redactionSectionsIds?.length == 1 && - redactionSectionsIds[0] === 25 - ) { - redactionSections = " "; - } + let redactionSections = createRedactionSectionsString( + sections, + redactionSectionsIds + ); childAnnotation.setContents(redactionSections); const displayedDoc = pageMappedDocs.stitchedPageLookup[ @@ -1383,16 +1263,12 @@ const Redlining = React.forwardRef( ]; childAnnotation.setCustomData( "sections", - JSON.stringify( - sections - .filter((s) => redactionSectionsIds.indexOf(s.id) > -1) - .map((s) => ({ id: s.id, section: s.section })) - ) + JSON.stringify(getSections(sections, redactionSectionsIds)) ); - childAnnotation.setCustomData("docid", displayedDoc.docid); + childAnnotation.setCustomData("docid", `${displayedDoc.docid}`); childAnnotation.setCustomData( "docversion", - displayedDoc.docversion + `${displayedDoc.docversion}` ); } const doc = docViewer.getDocument(); @@ -1475,22 +1351,16 @@ const Redlining = React.forwardRef( annot.Author = user?.name || user?.preferred_username || ""; let redactionSectionsIds = defaultSections.length > 0 ? defaultSections : selectedSections; - let redactionSections = sections.filter( - (s) => redactionSectionsIds.indexOf(s.id) > -1 + let redactionSections = createRedactionSectionsString( + sections, + redactionSectionsIds ); - let redactionSectionsString = redactionSections - .map((s) => s.section) - .join(", "); - if (selectedSections?.length == 1 && selectedSections[0] === 25) { - redactionSectionsString = " "; - } - annot.setContents(redactionSectionsString); - annot.setCustomData("parentRedaction", redaction.Id); + annot.setAutoSizeType("auto"); + annot.setContents(redactionSections); + annot.setCustomData("parentRedaction", `${redaction.Id}`); annot.setCustomData( "sections", - JSON.stringify( - redactionSections.map((s) => ({ id: s.id, section: s.section })) - ) + JSON.stringify(getSections(sections, redactionSectionsIds)) ); const doc = docViewer.getDocument(); const pageInfo = doc.getPageInfo(annot.PageNumber); @@ -1509,7 +1379,10 @@ const Redlining = React.forwardRef( "text/html" ); let customData = JSON.parse(txt.documentElement.textContent); - annot.setCustomData("existingId", customData.existingFreeTextId); + annot.setCustomData( + "existingId", + `${customData.existingFreeTextId}` + ); //Setting the existing annotationId in the new annotations for deleting //from backend. let existingFreeTextAnnot = annotManager.getAnnotationById( @@ -1519,8 +1392,8 @@ const Redlining = React.forwardRef( customData.existingId ); if (!!existingFreeTextAnnot && !!existingRedactAnnot) { - existingFreeTextAnnot.setCustomData("isDelete", true); - existingRedactAnnot.setCustomData("isDelete", true); + existingFreeTextAnnot.setCustomData("isDelete", `${true}`); + existingRedactAnnot.setCustomData("isDelete", `${true}`); annotationsToDelete.push(existingFreeTextAnnot); annotationsToDelete.push(existingRedactAnnot); } @@ -1544,7 +1417,10 @@ const Redlining = React.forwardRef( } } - for (let section of redactionSections) { + for (let section of getValidSections( + sections, + redactionSectionsIds + )) { section.count++; } //delete if there are existing fullpage redactions @@ -1564,7 +1440,7 @@ const Redlining = React.forwardRef( annotationList.push(annotManager.getAnnotationById(name)); } astr = await annotManager.exportAnnotations({ - annotList: annotationList, + annotationList: annotationList, useDisplayAuthor: true, }); saveAnnotation( @@ -2102,7 +1978,7 @@ const Redlining = React.forwardRef( // saves the document with annotations in it xfdfString: xfdfString, downloadType: downloadType, - flatten: true + flatten: true, }) .then(async (_data) => { const _arr = new Uint8Array(_data); diff --git a/web/src/components/FOI/Home/utils.js b/web/src/components/FOI/Home/utils.js index e5af50971..7aedd0a3a 100644 --- a/web/src/components/FOI/Home/utils.js +++ b/web/src/components/FOI/Home/utils.js @@ -71,3 +71,27 @@ export const getProgramAreas = (pageFlagList) => { // Helper function to sort files by lastmodified date export const sortByLastModified = (files) => files.sort(docSorting); + +export const getValidSections = (sections, redactionSectionsIds) => { + return sections.filter((s) => redactionSectionsIds.indexOf(s.id) > -1); +}; + +export const getSections = (sections, redactionSectionsIds) => { + return getValidSections(sections, redactionSectionsIds).map((s) => ({ + id: s.id, + section: s.section, + })); +}; + +export const createRedactionSectionsString = ( + sections, + redactionSectionsIds +) => { + let redactionSections = getValidSections(sections, redactionSectionsIds) + .map((s) => s.section) + .join(", "); + if (redactionSectionsIds?.length == 1 && redactionSectionsIds[0] === 25) { + redactionSections = " "; + } + return redactionSections; +};