From c1322071426d4ff80b4d84b3a6e20d0d2b2a9fe5 Mon Sep 17 00:00:00 2001 From: Arindam Kulshi Date: Tue, 7 Jan 2025 11:37:10 -0800 Subject: [PATCH 1/6] added updated docs --- README.md | 5 +-- frontend/README.md | 79 +++++++++++++++++++++++++++++++++++++++++----- user_guide.md | 52 ++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 10 deletions(-) create mode 100644 user_guide.md diff --git a/README.md b/README.md index a3cae32a..f39d2bc8 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,9 @@ ## Overview -Describe the purpose of your project. Add additional sections as necessary to help collaborators and potential collaborators understand and use your project. - +Please see the User Guide to get a overview of this project. + + ## Public Domain Standard Notice This repository constitutes a work of the United States Government and is not subject to domestic copyright protection under 17 USC § 105. This repository is in diff --git a/frontend/README.md b/frontend/README.md index e3a8a973..8dff74e0 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,20 +1,59 @@ # Frontend React App -to run unit tests via Vitest + +Welcome to the **Frontend React App** for the ReportVision project. This guide provides instructions to help you get started with the application and run tests + linting. + + + +## Table of Contents +1. [Introduction](#introduction) +2. [Setup and Installation](#setup-and-installation) +3. [Development Workflow](#development-workflow) +4. [Testing and E2E Commands](#testing-and-e2e-commands) +5. [Frontend Architecture](#project-architecture) +8. [Troubleshooting](#troubleshooting) + + + +## Introduction + +This frontend application is built using **React**, **TypeScript**, and **Vite**. It includes configurations for ESLint and end-to-end (E2E) testing using Playwright. + +--- + +## Setup and Installation + +### Prerequisites +Make sure you have the following installed on your machine: +- [Node.js](https://nodejs.org/) (version 16 or higher recommended) +- [npm](https://www.npmjs.com/) or [yarn](https://yarnpkg.com/) + +### Installation and Run Steps +1. Clone the repository: + ```shell + git clone https://github.com/CDCgov/ReportVision.git + cd ReportVision/frontend + +2. Install Dependencies: ```shell -npm run test +npm install ``` -to the the app in dev mode +3. Start the app in dev mode: ```shell npm run dev ``` -# E2E common commands +4. Run tests to verify installation + +```shell +npm run tests +``` + +### Testing and E2E Commands -Inside that directory, you can run several commands: Runs the end-to-end tests. @@ -52,15 +91,39 @@ Auto generate tests with Codegen. npx playwright codegen ``` -# React + TypeScript + Vite - -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +#### Fast Refresh Currently, two official plugins are available: - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +# Troubleshooting + +### Common Issues +1. Installation Errors: Ensure all prerequisites are installed. +2. Development Server Not Starting: Check for port conflicts or missing dependencies. +3. Tests Failing: Verify Playwright setup and ensure browsers are installed: + +# Project Architecture + +### Description of Key Directories and Files in the frontend: +- **`public/`**: Holds public static files like images, logos, and `index.html`. These files are directly served by the development and production servers. +- **`src/`**: Contains the core application code, including React components, pages, styles, and utilities. + - **`components/`**: Houses UI components. + - **`pages/`**: Organizes page-level components corresponding to application routes. + - **`styles/`**: Includes global and component-specific styles. + - **`utils/`**: Contains helper functions used across the application. + - **`App.tsx`**: The main application component where routes and global providers are defined. + - **`main.tsx`**: The entry point that initializes the React app and mounts it to the DOM. +- **`tests/`**: Includes test files for unit testing and end-to-end testing. +- **`package.json`**: Lists project dependencies and npm scripts for building, running, and testing the application. +- **`vite.config.ts`**: Configuration file for Vite, the build tool used for development and production builds. +- **`tsconfig.json`**: TypeScript configuration, defining compiler options and project structure. + + + ## Expanding the ESLint configuration If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: diff --git a/user_guide.md b/user_guide.md new file mode 100644 index 00000000..e9c312d4 --- /dev/null +++ b/user_guide.md @@ -0,0 +1,52 @@ + +### Overview + +ReportVision is a tool that automates the reading and extracting of labs from PDF's + +### Steps + +1. Annotate Template for a Lab Report +2. Extract Data based on selected annotations +3. Conversion of Extracted Data to PDF's + +### Getting Started + +#### Prerequisites + +1. [Python3.8](https://www.python.org/downloads/) +2. [Node23.1](https://nodejs.org/en/download) +3. [Tesseract5.5](https://formulae.brew.sh/formula/tesseract) (brew install tesseract) +4. [Java21](https://www.oracle.com/java/technologies/downloads/) + +### Installation and Development Guides + +1. For Frontend +2. For Middleware +3. For OCR + +### High Level Architecture + +![](/Users/arindamkulshi/Desktop/Screenshot 2025-01-07 at 10.32.09 AM.png) + +A React-based Single Page Application: This serves as the front-end user interface for the application. + +ReportVision Middleware: Acts as middleware to handle communication between the UI, OCR API, and data storage. +Responsible for coordinating requests, processing logic, and integrating with other components. + +OCR API: Runs the Optical Character Recognition (OCR) process. +Receives data from the backend, performs OCR on the provided input, and returns the extracted information to the backend. + +Data Storage (Postgres):A managed database for data persistence. +Stores data processed by the backend and results generated by the OCR API. +Handles both structured and unstructured data related to the application. + +### Infrastructure aod Cloud Components + +The application is hosted in Azure. Please see our infrastructure guide here to learn more + + + + + + + From bc27a73856e334b67dc61ad16adddc5aa9a09acc Mon Sep 17 00:00:00 2001 From: Arindam Kulshi Date: Tue, 7 Jan 2025 16:02:46 -0800 Subject: [PATCH 2/6] added backend readme --- arcdiagram.png | Bin 0 -> 52525 bytes backend/README.md | 154 ++++++++++++++++++++++++++++++++++++++++++++++ user_guide.md | 10 +-- 3 files changed, 160 insertions(+), 4 deletions(-) create mode 100644 arcdiagram.png create mode 100644 backend/README.md diff --git a/arcdiagram.png b/arcdiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..11a5d9a7b88bd0441ddf46ff302e0536b7cb0970 GIT binary patch literal 52525 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

p9uND~j7~e8 z@|fT-MjB<)Tb13^Oyi8&UBbGYN@=Sbar%Jc6eygjPsdkpAbTZ0&Y-ZWY7;&>; z+)hd%MWjywfO=Xhd%>mrl``(RK^lyXi@<8eMj zVpqZ7HoOJV*PZS6y0Suker=SUOcZ|jn|y6&S;n)BGH7sqM^Yza-mzNa>%&}zM4k`7 zw^X~JOBHRCJ8p}-zv?odG0RaC(vOMazf<=GWOdV4-!Q-8Pqfhak?xVo+^Clj0RnaE<3QWhyQX|hF3q1zsO&B#@}v*L^S&c(oM8DV0*9jVi;)lb()os8yl zH}q8O8!msv@hXdovZT~sj#(NKKKzQ*<@*T3>$gqIG75JRi4jKDDTAJdqOLI9{BFAu zDS4_hi=HF15F+qR;H(vsU6>35*N77`p5**AR-i`7?Xz#XrRqAa zz!1)fXtdlt&Gal)i)T47WDCKSLY@8&8Q;*S2T>(maft`r(KVzlMk@nJZbQg*{$M;* z))~xg+cy_``b@0#htOZgt4TcnzX@Inz#e) zlMf4HWd3rGXb_bW9>!3Oewl-UHjurQG9y$$^pd@+S7MmwYD1N)jn|;w<+IN81#kR+ zMGq^s*8zn0YUA_UFyFHuODgx@9dk<9NZwm-hOrvDn)B+0eg4su2rw?4-(ewBuO#~pcN7`G#XMZ z#Ma(d$kUw*(7rmBXxAm2l#8EI!YO16`RRVz)v=zb`oL1CXqR!S`HveCufa{L(??ZZ zKzJ-0r^p~e-f^djvArR?Jy`W*(fje?7n?WfAkmw-H*juEAjH&2p1{Px1n>e!TIYo# zI^+Q^)U`VbN56-&sDM3fR&JHVlHkazl3rXsjF95ENetymj^f3dpDO_ zR?dm8A&r<)5qGMdzas#)$rRlWQ#m7MHQWq5Lav*9>X8Lpsf&!B&Ci6-&Fy0A9}qWw zuP2WE!$`o<2p^(8>3jd6e$mfuK|$`DMFn&1{%MP}depG88^D`3*nYqkSVpMn##JIV zzh?vx$+QuiVr#%aoslso;&?zQ>fe~PZ}GOh`xs!V20ROV998);c??-DR2Qt0Um#uw zmHt3t-Us&Q+mUE%^HRJmYD_$#qpW^zfY++cRVL?g(%Z+3^cXJzl%73#Id#z*a4{!< z_9p1Mi8_IXlN4L*a{uGha;x|y(zsNv@Z0Q{v~mV)jDnRYjE{U@Hc;=;{SG;Lp+^F{ zbOnhKXSZ5AR_HcnV{Xo}AgI*1H?#Xp*=j+HRoi=569hdk%i0$iM;2q1S=bC!FO&Nz z^6nmmo0gX$$Eei*Sg}(YZ=~>=i1@ZJaEt72+()?{g(aTy! zrc}g2C!ezpph%E8Qif8+u#UJ=rIFh}V#Ad8b2HG5UC)l zEZOzmY8||~BAlxq;AIc_xmTVZ=ziJedDC{~oED^59v~4kN5Xe%yE<7*)-|bO!#aAI?681{)~zd_Bc{o*B3t=0H#SAM zI~tE+Zu$9-$(I$bIt5Wy?M$R36fsznQC5i76C6d~Hz_6VSFPmSG%7E@c$E+H_|?2s zK4=Gt#Uom!-g@RGO+^hU&Br~ORN#G+UVbVZ(^-}m`LiYoWKjooBqOrp{`*!z=T6?W!m?i6RO(YM!(1zGcvVI`%(%d)X% zLB~_Ne9IdEKC9vBW#pxB=E-zDROg1ei6pveB>Zy|w6G_0;!z92{v!eCLkl2_h!r2k zF_mFfJ(`bumusPbxBn(xbu7b!>{w?BFm&YkBXO9f7aIj{GTur>&IxcqtM9WZi*}et zyEQDh#ny=r>Q0OC2b_Dr>mOfAg|A)fH@e`J5lHGQLJ1q=FYUa;rYaGpbuuvH&>`8d zjpkqe7Wd`IDd>m0<82)suIu8ev$oQ=Kgk&({gdHM3Wem?^F)=uZUK>>*H4T{-x6m6 zc#MplHxKC{G@t#UdcGAAo;qbOKdB%p6;ahI&eoA|ThEG@zh9K;)00Xe<7!@3#mAx- z0yRCn?t&^jKQS$=4NFR&WyCN~eKu;myFV(Qwyz+1V?Y}~zA!%w7%(z0k|h42KAh9T zp*&5kpD9DXo@`DHzIN$E*Z&gO_X4jwijyr7dj_X6rM4Lm^2&Nx0d!H0+|f5*Im}M< zhYVhJFdv&kqr7e-W}A;u-}cyTogl3wEF{cyH0lZv)v?XF?zx=w z@H^~)UCS_Ak+p11wQ-9!)A0HDLpQzhF0F+G~ zWOGV8u|7_0-=LD7PTM`XCA}!%^RqATEYm;`Q{&0IPiWt{!;W=7*p<$2s)pg1zWRwO zU8=*9rf6ZMDGV0lso=z^Wcjo2Le zzr$Ex$r5daQzqhKbB0277!vg4?r}LBqI)9ws!*{Zm z`h%s#p_^I3ELfpJ#KwY&brLN%rDPR1V+5@08J~y7w#oL`W=5ku3FPBS_tLrr< z)gcR@6waDqkxXfiptGoN#<0Wm3nWt+AmGh6lfbEZ>lcD+m5O1G4U6?M_uhBk*LHXt zx`p8QstnN^DT$~RHVMmv;LWS>FHVXPIN@0!_CR!?XV11dG<*SsaC{CGb9Aj$ezzwV zHachXYlyPTtYPcxaGH{H?L~||%zP~37KvryK6$Eyv}^dfPQv4rPQh>8MJEKvCE*o1 zX-sHga>%6}*|O}+80_@wK*(c!C>{Ty$4^sMXq1xpCFDEE)oK!5A(WjF{Vf*w>xytr zc>X!^3mf~TR3o?OIE~89;*t!I(#~0{>{qy!TsBfT0IRQ}G-rb?SK><`cFtyWAXC3k zJ4%1v8GM7Mqp@sUX_~r3Ag|c+Q(`Rt_70746Y@GJ8{)LNf~KA+kgjEGXe~5<9NhFU z#@i=Axzm03tNsj<*7{aPFiY*V_eGjCEtFLH8qkgN0!XO$Jk(Q=J2aJ8tt=?Fo7S`I zUtib&T7-X8Mg5^HUo9Y?gj!SK; zZx&AA5F?XBZC~DGJw7$#A24R)_9&W+A3>9Sq)ppaF{AO^L;3GYU{VlIL7{LQH$4l@ z43&l!D^S@TbjGAq^mUROfYL5i``+ga%bPI)=8dr>d){;H4ZefpwzIWN$C{Dz(B;Uq zn^hVZftY=$klX@7H5H7EnyLHlDM-GmvGEd3K^dG6j_2!~PES|khgF4kECa8Obq%ra zqg((wI4wG3Obg*hchUo`>P;5w$mW+?s)BhvR(hJB-^eEabY8d-gWWr-n2#v$f>2-=yR#NB9;~~>2z|p zK5CSbXP9h_?Ld7&!B2>R zLpnzq;yOAc>#>u;X%V3k{nNr+hB7q_5cP|yL$2s9TpmBwYgS(fWx%UP&G?*SOQ2!_ zuPiy9Ky*g}K$ZE}Is}=J!$?k1>RA9jhVuDgKfENhN}5ppGJ5-^9EI%!3$7`SOs8!@ zmbFnn64%3{?JpBXemiw%#SX{hF`*#Z8YrRSlRv+!i;RCi=xp#YDVv|<0(TQkmve;_ zHX-hcWpJfX^tbMdg_m|;536s|<)(PNt}zvf-a(W6(GM>k<99um*OhE85B6Lt4vdL1 zuwSDX@j`kotxKl+nRqXORD-M0ek7^l^c}OCKqE zXthuRJ=cRXfI#jHpog-tfzptIwxQNB{^141l$Yzgw?0Ch1~$AfYc_*~0_fioxPv;D{@>&mD2g?jJ*BJT^~? zI{6#%!-JDh1b37C#yr_u@!jWljb12u>OmE0#LN9c`_INR!ecAwqLhBD&Ez@a$R`H- zu{E#LIW{?ZvkjkUH=xleATYCqQWIS$dj(KMNc5UUHlmEkbYx95{Kd+XWFfzHD7GBC zWsXvb77Hl_**o4_TOLc$KonrhKa!Rn6|onvnfu`eXxyVw$A)X|-WJ%~ha&_nxn3|G zr@yu*@8)ec2D{%5%dsq6>$R)5pu~}yBW!A=HxQow*%s-1_lJz*{qewtk{FSD=w|#qqL=pHwpWDc~Kd}bq&6kd-Heg`VJ&} zbV-wneHd$5fxbZ#t(H&lovH}85_O4v47GZ+O|=#_VBZv5^oFuD^=}hO1Gw0d#QQyO zk3_8~yT4SbOW>Jki8K zjb$#M0v^-(2FCql6pe6*)&0NAT73`y6a-GI2EdX8#lH*MChX611!>u-|xrZ)T1~pE}VD#P@MYh zSqPuJr$TGeCu&zkq!FB2EgHzNr1QM`HSXQeadV0sTZiOMRzm0Miqs~Uwv@3=9xrW@FS}$Ql z=uz;^rD($lumH==%iAcjo&&|f&AY4ijKIW+~MroRXqWwRDwV*a~wwP`v0 zrVN}yBNi>k0{A-;c)OW!#3iV;_Lw?!Yoc+M^^4Z6%muVH;&ArMu=BAV+c<_68`+!IBA>RB$2u<>aY|sXc=B(Zg^+D;YO{FJhx%-dsNBu4s z?ZU_p??q$8nW2|c`ZnW>1NR@^qQ?Xd#H9?X@=YsL>?om9i+?6)0)EKG{JW88@o&)A zH%{Ml3*X;@2?MB$L{v=0qf?jtv;T^I8FXNwsC*^#RD>!rxE`8N zLg7t8g#2%kgapK|o8Q=q;%U?o5L4?ya@pMF5L1hyZm)m=b$fv9PlVjVE06fG82_*C z3wWq#TJ3Ne^?MMu9t%$c-ICjT?ZHimm8|N1WO6%)r0^R@@vM6Qd4Q*m5}t%u@)W+3 z`y_G}>B+)vR#?!Z?5T*tfgJT#intjJPVDKDI4Og}_$-)<5nocl3ciKP7y{}l%*7!B zJ(3y(g_VCj*?VPRX@5>yNQamzA<+S2Rxoz_vl3mQmyIZH8jLTp6mK=YkFvq3lWocg zWvF-|7!hft2HGs}bs&S621+;Eyn&lNvygv<=e%(JD`_*yW(-=Al6VJf@P6SI+&kw8 zLs}(G&p|_i&RQ=ocve;Aht#*g9|``fA(=UBQR15Li^Fqi&8cFFTho1pe#(Y{u3NVG zS93|F0pz`OFYNW-K`@ zh(eyBlLuWMn8m%5?YWqUK(3{Rcq%Txjilg`qwsxywk~!BcCDmLy+CgdI4Nsvj34!x*aF&WE|Q+a-4fqiLyP~J2;NA`ZZ@u^t z+>C2Mr$t~oWzgkVeHXV84WtKm-xwc%CP(in6Z`ErKL7*wD8?h(>!J6q15n7O9_yQi zAP>O{+kA^2T8H5B>rKtBYQ_NXTBlp3aKh=Xjjt^qHfF2d>3fXHsO)`h8SbSFdd<6U z?7cWuT0OuNq1~p~cY3h8bJYxJXQnYD4aZYgte9r?aG9JDG?uDvTjZ~3lrPBIzMO#|(v?QJfV^9e4)rsap; z?*IfYQQ!-K6|@o_Vv(n{X1J%b7V%T^YC(G_`E7?ILKSeuoaV+1s1jP3w#Qrpj?~$y z@f*rTb=z+9Rt8*_wcH?P-W^rxs2rxhVNp%RnR1u>QydusInC!vC?TLXY5?-i1hNb+ z`{y^Oy<&HC#NmR*>KAW3#ue58bi;@bIOri-19DNGo8LhhgwD{z2ux`2%TckL1#-_x z4eLY|K2ErDghaq42M)42e_igF8iAbZ!@WqMmlc%d&s!&`KbQM8gfh{WhZx~wNaji2 zu5N8Q4nKl@9WVD5Bxhtt2px&gjGelR@1T;Wi^;HGqODXIVYtrV*J5NB^%Mceh9sEruVIqu_T zyOt%RW8PGSyb3wju)aefJlm}OR{Rt!;9+>7fe@H_*cLPf>9>QFS@3vb{|$iA*2#JB ztWi7S5)7}-S>=?DVfyveMJ7<>4H|iUv8r#=v!H&CnTMF72l@`GdkwZCS>FfQg(B#n zv*jb|pa%wbXZ+0h>s>w{!OigCO?w9_r31rfO8@t! zIJaurclb(L1M&sFZY4cH!I$+18R>gJZ}uh00*aY!r7iPVR%^8@;3+s&Lpm^K?JHWW zFMy6usYz+@{L`B>Z61^9MWwpO-+(ncpPv2#sT!%d6g*!M-p0osWNmm|wg(Ctb?gfz zW%U^wpzT9%WU0M6V_ov#Ric^j3MW(oEfqi9MQsn$l@dNbWL7xOfhSxZ_zPraDON zOL0sz<HJ@P zx>`AE6nmJ+*jqHXb#udOHlKHzo_w)sV1LciGkB1wCTgqOdYP}^pX+3jpMLbvjCXfj ziM?*E{`82Jf6*iHs&Y|2et)kXhRIcR6}MlV(^}@4VCsXRkT0nW&lF(|%R| z^6Erqm`xVe#Zg2u`D~MJMd;nf^%WGEhQW)7VzuY|h0t#0%<78+aQ^W16+W~WS$|{} z%0_QA_S4db<|)r1xwxUP5Qwmw7#@Yz&lS=mUf<;=k&_4>Uv>_<$jnNs0NQ5X4?TyW z{U5c6)zcjDVX7DpRtK8*I4wRA(_j4l>Q^CT*;bU=FUE!k)blEZJXD1=it5>ijoLXp zRC=@r4r|$dPJXPTk}P+DC{QRYmJkC{AMmY_J^ZMPy<*1_{d5`vGFpp=Yeiq17zOOZ zgk=C;xs8v-lH#0L#i1?xvgwW78djan0<=hw-nGj2Ge}rA()Q0f#;eha?#m~B`~Q+*NJlGu*kh>1HVbgpt)(Rpk8VyOWz^>PYf&yO}hPRi*?rj0bov*{4jGZ@R|&ZgL7e6cLiD zFW-Dm=X99mD2b9Wi>AS|XDFWs0zStnMmB*4pn0BOeZB*fS+RWc=4}aqwT<$q#i|HB z5^qEBO1|oQ{w=NAqVw$r_o((kt6jF1Kr>8xpkpL z5c*win~_>Pe-lN{LAk=n)Kw6GCU#dR28#)tuqMA@dIo{0eUg{zhE}rEO}uQCdYNzhDX05x$Z<*tsaARf&sb+VT9FsLU9Y8g9E16OC}}-?BNB(;C_{Ksw8<#%Wl%2$b1Li*L5U7OgHg&=U)(3# z{DFiQF)+(7gYMh46RI^<#LVTW!X>{HsIU6GTSk)Af)OIa4elm9bv4*qTK(K7^Va{Q zSmMvxqtb^|^T?;z^w)8|{BeIYfTK(VIJ50RS{+2?IUIa6JqivSPYk3s4HG)fNSp9Q z^tJ0mdX;KC7l?~1_I>2U0G)S6_)eSF+OY)Q1spD(`#bn3*HyLkRk;FyPIQEE;xi~i z)J-n89^Y`mC04_Ph7KC)Pi64$s+ujI4}6DEqlDr$;Hu?R73TOyTij!nTXM+*#9;Xy zjmc=*Zy!V(-E9LdT<@qqk(bVSiJqhPLomkbl~%$U*&dvZDU0sYY(Gp`?|{?ei`5IB z;YqGmi~JIIBkxi=y=fSE7{ema%7q(_n1U+2+5qI4-bHnfR)`g$M3XoTNTq`=3htqv zy7rp#Vg@Br>xtCrQ7i3DjC2#ZcDF4bunw#4h7s8$eHO#dZ6`Xxr{s0Ec>H=WV+ZdsMxA~Nr1)BYi`_vqHHPiHRP z_?Nf@Ug@dl(B&+B6i9T!>A}GBFo{um*@%v;L$QE#8KXpCB%|Iby%#;tknjU2&y>nq z-*1_+;e=RI1y^I|-77-J#JP4(7$5+V6p>L^VIstCs#TZWQpKOpTYy|?rTbxX2<%ix48lM^f>sTyfGiPaflLV3IoYAMU6-W?`0fXrlo zTQ~L=q@1DmiBOBZiOK4jNj42o-7jKbB(y$j4JyZ6=-uQm*>UY0AizL6j7pK)w_O}%kXBd4wbL&aP|KNDXXR<_OI1&JsF*z+WeOk9)sXQL>G zQzVHg?SKTHpmcE_7?gkfrT8Hu5C~#t z6$+JX`j}>o+=s2MF8U7_GhjWG6NZ=bS46s(Tj{}x`9tXJrGP}0juk-N^)Fh2K3LUy zuAD6_SRe%5xbNG<5T&Qt87n~F(`b{hS%v==n}x8l)FmBHv52AH9)E^PA)~ki2Yo+? zK*mz^!F%O}V_~gC+y#QygOXLA^k3r5=bhGm1v2$W3J4H|OD>RL6si4558)Z&1BjcN ze2iu1G0VsOPA@BTckfU?!44I~8ZC&=HwH3-A)Nx{bX*FrwUbhHnebUu4T*H|Wmd?! z46+h3gA=c)Z3%Rf!G+fP6Udvg?b)_6m!oQ9o18R5r;zcG%tjX2>QJw zpO3Q4$Y^0}W=}p-{-mJ{GYqNk=T~)+X2H{oliQ!VPum6W1JH^X^U@66LQWyj)31c| zS(3{jZrnXM76HY#q1;$DyL}Q>#lC^yr328JRkpNmm1=AaFA5HZv|BQ|8sII+N5uq! zJ~_kGS8!2`DW}B9Frtu!#G7#nN9Y=qCW*gA&?9w)?_F8D`q;>Kv4Yo>3TtA>jt z;eXf!2w)TJzOjAZKniIF*_jQ){+qz_B31>uAaerPL@m7<;nwDQ*ECe=s`NZjB&6!4CayhdH6;vdF*%3u+W zSsNvTN}lB@)5;fB6I_jKj}A$}F~S;Qw3ByL7z$IwWW!KhDb-pK47lYC=#+wMV|$|9 zH5k`OlW*H{VIj5XL%7QHHM9bGi@BR>NeE-Dz44UJ0hb!YL!g2$xW!;K1I*DT47b_g zjv(fZYiPevU~TDjZ~xK8L>Yu;1O4L?VbSNl?e^}hM#M4vn7}XZ>(jq;I~fs1p-Xsp zCh^<_l5Yu>{UwaAlVQhl^YxCH&1_|oY{v9Ds9h0QN+l#yw*=NQ4)Ib;q$?*z^YEqd z!Cuyj#iI&+IGJY1(ruFSPcwYNKzs7$KmQAW3$EX}Be1iHj@Ed6zBM0h3xs=DrsirR zdy)hwMqPB=shz=xdJ^x8IC8L(kl#mbFEqIqV}yZV2cmK_)R>>Ui5 zX=tv>j0!fwTtu!G>BB`Pl#~uRHgLJmFTjQ zF^7uc<^Xv=+)NWhLo^(T8scC}ITL-IrC8r{6Rra^FmIiGI9@`Y#JAE%)g9=|5fYW1 zpdFJm2?ZK!nTZg6T+XpPeKw8qPL?3`x}?LoB|;Y!ipKU7^w(+U72I6^ZW{*maq@Z* zAgzOw5s?>ju#orYfDN(DEmr&ETXV>PZ%vX?o8ee)NjlPylAbo%08fTDK!@6uY5!`2 z(%dwllD=$%W@77iaeUPQ-Ny>!AVEz0mvBAQ7L@dcN6Q6D}t^{1OcGK+AyMg`jgsyzXNrC@90U-2z!!NEvRsjM+k?>xq=O^aE@InS2PZq$vQ{ z4T*t7_TR(qigLh-4Q*VwJK+%y-AV=N7XtVeQG!q8)Nca}0WYD`=QbExy z@bT(oehrk*F&KXDN^+Hae-(7K^*G{C@9qL<=sj@nE4 z#;xVvDm_2UjJ2hu@IF9~xtD2#A9Fwd*R7%2!af@+kv`HcK%3-1WWl@)j^bU=_Edpl z(C-6MM=ur097h=R;jAS{VfSz8z;q9ulW=dF(O-sRxZ;bR|D>I#-)SBN8M32}k*ka$ zR)8;%KB=U#84p#ObqyKVms!1LmU_EWXk-Cnqz{A8rdUZ>EO^KL5gY@B!~&k(GB)lr zX1n5;vmIdSkC9rK+c2LGBa<2eN^+n=oi|AZ;s4|6z2m9w|Nn80gL95`?2&QEIA-=p zIAoS_$R0&XvPY84W0aASMD_`lSvE;lNJw^OW>RESGUNAr(bebu{oQWYA6IqGdA*+V zvF`W#!!<%RcxUA1d=6gT&kaiIO!T!@U-~F@0w!Q|(2Eu+qOrsT4tKlPX3;WpR%+Y9uQKJkU@WtsM zrv`LYh>s8VTP8pmsMAc;_GBujU;jRKIrx$7iOOCL`J0=7?QTi-TI@BI#rc5Cit^#H zdM4ML#fDust%`jq#4H@Zj~_+T1%}W3*Mm-kXCfd&G;!TQJ_l5kAn?UFwo~; zfz=8ugEXR3rr)sCG0*xk19t!wP-9Wy@0XTp!k0oxCly1RxSlkiZ$%# zJMQ1Y#07aB@SY8S@Z9cI8rb*UXd}WFWvfC5vt^%M?rOULjw0%!rUa_3_rDUGETWs? zk)2b2zNH_FVOWa(c@Kyz$HEHkU%%g%eF0vfTCh|cHMm89yrhPDfPtU6rTYagk}i8S z(Lu||#1004LUFJMoIxc5eE3y3q;+umr1mVbR*dS&ov*+$$pVaOgkJ#m*Ui~uXjWc} zamYcU35))L>koo!SJ_R_a`;CW9@k+aw-4InejNDb?m)c2o!*YlsTSm?%Y*gWUQ`3}wo>3vfT-l~$@l>HAM)V}8}=H& z%QTI!^2}HZ-~AA}Kkz)mz3%JNfIoh7&%dxe=m!zen|k0H+)_6d5J0n>TWy zsa17xu;9Tq2;1G~*A=FM2BsU`|ArJ}lLi!iFUwm# z>V+@#(P5&#fOj`Gr!Mic*fpGZKGl9U3u*pUnh;YW{=pypUAf5CRk=C#g5aat@dlIR z>#&Nc?lYn#Oo3ms0oGQ(6qbVAOZcu3?3ay+wiLu;F4=8@B-2n9UCzTR;SYb5ihYE? z9Fu`c7v=%qEBVonOs|Abd;ey{`dYK}(jOIkG!gZm+`GCv-2{#l@hBH!YvK^JFYSSJ_%*%(~F+hb;21p76p_x zJ?p*-rXK`>%eCfmv{(ahtt{grgtBh%{XV%<10}nf9E*bnc*Gb>3r9fWrTCc6ua?HW z&nGBC7rf`%M}Mz0?0UGu>VXv*mD1^QJqU&GjZoCK{4J>W^G;LBw)zk)g*SIDj-Lzo zYcCK;U4N5cm?cMP%RX@5qq6N=vY`5pbOx12auv0g${)Zul>s^X2jeu25zZ5-m}yW2 zYNhR{ygDFQ0T^9TZzr^;i_%9yde&{S0TAYGJK*aS(uA*#q8uUF-tFw{V#mTq;4@`G z$J8Z!Tno^eEOc=W0tE6mAnnP%HBm+^lbcmI zEknmr$MJjAFZ_+P#^F^n2dCKRL) z_z_!HXjRUjkUldXm38Wbt0xSwJ2&l)ju!^@gyGTaK`KAHa=7jXW=odHhYTCXJ_BjF z?cV~H?h2ql_|zfM=w=xxBK5;n_vE#5FWha4&^1+>PFb%(n(MZD<$TKxi)Mrg&q@&D zG*z1)lJdT{H1&F{xJZoXhEn+9BdETCvR$lFvaOFEqoq}A);;BD<+s4_I5P|91trZ3 zh8>IR%9FgYHa&uS`0~Xt`_YlnKuCT)ai!&6TuY@IjBj>!T9glU^}>G12DQXC}_PXUII~*D<~=M?-tL!yl#6_`bV^9 zSsP1PTXcMwmZmE2XZGN9$Gb}U)8@T7`4y41AeZX44EyL27k;1YlIdCr3*R%F<$hYf zH|B}iVHNTi33wUJY`M++3-v2U&NfN_UpU)j!C+s~D_pAj4--8G$^*LTUM(9?If}vp zZhkAXB}@?baWYFN^w#EW@mW;Zv`E4UuH08)gHeZoJTlChLxB&(uMLZ=RYYe<8I~4f z+W3UIj5R9-=k4gEPqR(PqnIa-hZi$x33H%IUeR~G8za)M7PbTNBK%qS7~oDhU&R+b z39B&2t>ay|s#-OJkCJuy!LZn?_}xux(d|cz$Mth^HR?$(Kweci)9vA4NMhlqQ7UGH zh%{k#;(8-(4-mxVZ-IVKrr+I6OmJ?h^yM>gbA4pswy&_8c}Cm$HW>ISw1Mp2>_MsW z?$&PRw@Jw;MsA>hCc*Ex@$UfH_PR~#8y)Oy8ps?JwkdH%3r!c(C>H6X*6&<1g^OrS zgK~6Ps&~K32}Ib~tS8Reo!Q#cHP|QldepE*+~+aBRrBjLdt#_3t=iaEWQBk8*k7Rn z>Xn*P9g_BzPlQNjArRiV6ViPUo2NT?b23GaLw_3>IJbxvdjN*48P_j`V(3?OJgm!W zv7*l+Zvm9vWI>5Z4&to3XK&Ghh!1VRLl@XFf@p8{9qAW;nQmA6c|`%NyiQZxW% zDSJ#)O82v~jAX*lG;k2^%HOYAg<-gXkb%|PT;)o=X_Z+fG`bJT&VXpu#cQ`S<~dcy zMgYpmCla3d?XHgD6|GD z*vG_*6VbD;(3=#lQbMP?VpPtZW%xb-?#PTMrq7==BoCzU3+?r2J7*oifOZ4_bQEZf!9JatIxc&t2{JfDtyx)0?4&YdRW!QtO{Y)m5 zD^wLdX@6vJ^xjZ30b!`u`rF}I?&hySnq8{#8_PIU6$cx4xLnlhaTf_eF7gCcz2hdx zyo<2^QD3JQ0uk>sb?-G0G>&i4Sem1I>DmZ+H(43CEZ95}jH^d;MF=Lk2s4tgC<2Z{PM zjaTuPS&}a|;3@y`UKaoUiuzQNw-_50>heyQ0Lht8ir}&S-9Rko{*cay{nrJmSgIAO z*`3xY*_HQ$fB^tNioLTOJ57Jayx!2fhv>b|Mchv^kc~jPV*PH>2gqN;H^R%W#BC_w zeY&h9OkT!s|eHGo*m?6 zk`)`Q;qJ!Ll^Y@SWS>FR14(}hu(!-p!*Y_}i56{xq+$ADso#tlH4HKrQxwjh`2@dV z#f)j~qoVwnJRIPj0vqC=m9H;5mlkUXLj-x@uDmQqtZczsVH#bVxE$6ew!pD+Z#?mG zo!_3a%J zkmf5ggju4KHc$N;AdiJM|3OZmrJF-X_3e?yYjPr%Yy|r%IY!H%5xQ{@gvk@^$xkt2 zB`fOai*;}B?wtpGf}zVzlNA-w7L@kbrwj_M?1`o!tV4;HleD@0vHR|S1-(CE$Zm>LyBE`lgt@KMn1 z&#*Qv!AE(wcYkdFg^#jb!Vd{XeWFZrXW$LhQt1weeR^H3G^hifOfvJ*o*@gFM4oCN z?s1QN*rNImjuf5zn2sRbx#9qr7hNGZ)cp5{a{|^6LWl=rKFe2_ zw1x_!H8tr1MP8aB#_~Rh%Ww68OYGr9XU&BkcV88Ad`}6LsMv3*sT11HZ|@16?zFj5 zf-a#fg4qh~O;=eyQW6#0i5nvQupwSE(Qhr+Yh?QhbstA>FD_Urwsska)|9hc-IvOG zY3S@Y6U8cjA$a4Va?-|YfSigfb1psE#C6xcsC86b_vP8Oae&J>wNw|RY}xg4*HEdQ z6Kmv#pAPys)UPykhL{l|KyO->@!PNDDA4-}rF)xKlV~}Pd1`C!;?CGh z-!Ew#e*o3SrTrey1+Q$$zP3H{GAfj|xv6@aJQkILdZtfOaV@mvo5JnXQw)AX7k%E% zb5CvEWSKbj$m>A|y!tR5+a-Un}?3X3-FTUIuRMy$Ja2QqtjG+lfURTXi7ac-V zr?uQH;~*2l?>6Q9`T^g?7}_ymvirwg1W4 z;dNPmyotSEQ>&sduQ^p2sjYsb^J_R8KL*o`R0ceg9gv|+wsJ~!F3Z@3Ukm~gX>PqX zjcDO(Wno_C%=<4js<+c4qTrh}MX=&e=vi^%MAJ?f8_j#HUaMS~jmd`WoJ#Mbzy7BX z@B~1D+610H=K?qfWO=}61K<+@)QSwd;+8hW=5fRvX?Jy*|s1|Wm>|uD> zXb5SQ%z6EBy>mC9nj;ds|40AsyOr>S;IL)n*5|`CmMH1d)kn(~CedbL(h=|E87~XA zwNhYpIxvz!TVClGQ```PXX`Xssir{|uu_<%Orpsp?BH2Qto22n(mFbxV)~qL8_Hx7 zOQwX4upJ}S?MuFpfZj5q??>A*<3|#5y5}fEJf4Uq9&BIiZ;F?Fa485gr*&M<#GA&R zk2Okr=d!cdq47(HJMp$$biZ!!nt_x)pB&_>5?p$a-u!@^X8_xyNM+h3UAGP0de9Ds zkdGfwvWq})D1ADz)w+ZE_T#}2cq*A&2EqCDonr#1W@Ku@TVyj|inU8Yy)EEH-#p92 zT!m@PwLkT+lBHm<76P8z*0p-bsENNEWjDGj=@@vhe@C0pdv!iKajF0BwDO^KKx$u6 z1fn=5XoWrO!hBCxx%HV47m{VBt9&%|cugIRLnJ?*gTOz>v1N|>EP^?6EdDu|CP*pw5;$zKKPHDpG%@NhxE7>b z<{poVeT1O-k1|cfcfj0|eob2!01H@LA*}wb_xxOM(`pY;2`ddH6?ibW;mS6lwTp z!V6vOv=1{qArY|$5}QsSZwcu~N&-}gG;sOkpnwDtCUaZ@0Nkl=s1O9HqX`;ixj_h6 zAbu(u(RSxSoJ3uReuiS-_cRE+Qw;tZK%`Wk8l=n5c)LgAIN+rSw((c@N^WL8K7B`R z{lt^k>yv?O&%$(b8ccSGu1BydjyS+@;aCcBk>$fn!9wtw<-%C(#kO7r_tV+Izf-M% z#$GpsKvX^0^MzRckHE{lFD`56aB2!86|b!a$fT!03FGO7#*pegkWsq?Rg(rPt{4q8 zh8~pesccR-Lg{atTQ@HRewXtFPFTMM-Opg)!u^cnT!zx7TN_NE?EuI_vH&Vgg0uvVd_(}QUrj%TV z3ifXt0iF|81AUL7iFpB1mj%K)SCU)8d1;G@n;ufgtQ1oew1)Ql&SXs@NBTT zGHD}P_>&JWPskJ6ZrBL7W$J8DIf}f~AjrJI0VA4(9-B?#fig@oXujX;p9uGo!U(EM z084hc8v>I;a-(s;W+*E~9B&^|--gh2Ay8@R7!`pS2Wmk$TW>B#!XScQ?%iey>m~Pu zvF|2r&>8Jz0_Vp$cDz_U7FP%NItMhRbw?2EG)!z#t&ij3h)U0?PoTaaaK%7>?HNG) zC0n})+c-UrV(ILRPWaiPW(e`EAuw{K6ty9^#nbI(0O3@0)Z}s1?`!AbL9m9)VWIUI z?o&5;g|#%5{`6HH-3%L$&iu}6|8Fl5gG>bHpa`%?wNH1R^$aQypqR%=u0t*s0Qmdb zWk7{X+>Jh*&8HX-TyeW@kg{EV=J@vR@DOi`sODq8!VCn{>82WL>_VOGWjmZ^j}Wsc zuZ7J?i6c*Z1RCJ-Zc$$hP78@`P}lNSFgt`D(VV{l)%@5tA^u?n@3obQQOOw{-3nd# zd+HGsRoDX%AwL2&QYGrN#Y5PB_tgGxg}{A);r?NC)dp@NFl|G@|Jg7>JF`Fo|M_TK za(ZA~Y4zgYk`RL@2tcnp6+~X+v|(Ems5_R8o8`$j4bQX!ude&o>eN&Q1PKR_c1g(H zdkJy`Zt{_U{r*UYCs=m*2gNZrdkShc`_Rx!TQ>kMcXb_O$_y z-o38^byj{?Wt^kar19oIt8jr2YL?5(C6F0zg2C z-uG0x;*fcG$$rnl)$E;oP;Rg5TwC~;I%J6GPDJJz?i6|e?Xf|6QW!4-$Yl^1VgfPr zAUnDTO1%pJxG4ornXUQ|l#6!i&(m{)$fy-PBEbRby{7rI@JEC9zW>?v^@T1>Phq^v zmA#Y3m}?wLIfI2jD&?@s_JE-`{oI33H)w3YIVCnlUuE=I+4%xOmT+c@1ebxQ@dnM< zD0SZhk8f9h>D6I@er?uD{f-EfuT&GAY4y(V=_-Sr4Fx5qIb{%x6a|g z>V@{*;&@SW49*?B7V+-BG2sT~Uu%{OAM-o7u$;nQ0t<4Dct=KACM10`&nBjZ{LERnO|!fNYTPyKS2A@FeM08c~DSvyud6N4*3V;Y(P z6UF6~jK_rHK#|1@>#u1$hwme&XFqn9QQt%YMJ-A5@pc;gnIXi|2}AIo!3nc6wiR@O z$6Fr{D<^qES`IR}$1ZaJ&ARXFVBX8d*r95dNzJ6Io1KAQOufYR*vpaZ3283d;( zwUgS^TfEG`vfxgm*X)pq+zI_B1Px3#4#_}sjKH+7ua;?JVRAZ{LPa3O@AZDem<#en z2-cW{%D8YIwA4FXTvgMVVBnpA|8#GIUHjF`tOSg*1ii*gdC4?kmdI=mITO96@V$4z ziDa84Y>DQ&{G&#HCj+kxXBr@0Ux~WO2Q$zkf=gni7MY~mr1_U>C3UrpM_0xvd?o#8 z40H6LMmsloE?yV@Ew(9v_jJpC=PX9^F%9$rlHkJ$RfG^*XqqvwHpn|+vxc~935o=d z$vSpnT86iUC@_8wuwx`p#Pua}w+M=@GG_$zjRZc@mqK|63o~FsnGEd*^Nk^QV20es z3}NgHh$+MvWZvf-l77eunB~AsI)%72Zu}>zrittTR94Mh@Iz+2n7D{Sdy8D~|8j;+ z{>Hj>MGaNWK)EgP`T4`y1+qZU=H!^yc46b|MZohEog-i6OF@gP*91iBnk4ns9gpeP z|7zziGPi@?4FjWr=v5STsT{EgSpXQL<(QFhp$31iM{j>|l|O!j?7RGEhnZ)h`I$)O zzmf#R=Kyz#|5#&`$x3MF$F!+CezJ%MaB-q6>}nnM8fVb;kpkHy28YjMM+*q<8WWDe z?((oqD>=e2`^!%+P0i^=~T2=BRs*93^hvD0UCb3l9w~8f5;sz%Jwd6ZzEcm*h*h6$jh1bt9`5qT_5$`Om$-BS!P|M^DXgy(}3U^x7eH!&M@ zh`?3QdnJ=lUAKAzE@HliGq2?HPl?CfGCleH%+b7=@M6wJn3firfGIzNPH0mXz!rh? zM6l+YzG9qEr7k|nzM%z8Fh>gH>TZ5=xy08i3!jx1g=lrRrehB8NN0Xn2uqY~35^K7 z+T;5xaqWkgv3PylC?m)Aii;axFF*8^ge>D!=CpZAqC+f>ksdR|*9mJS*V1>~>Z0XD zn}f0kFY0jqapwRpmMHA`ib5O~uV+6}>TpXR#Fw1tP2ABBYc^%~ItC=%+UksnN%`d5 z?;;f6P-F;A!+&^dqahH@;V;AT&i`Vj%yUYbC3Y%kLG4TOw5Nd}*)#K+7a6)0nS&I> zzshP1yhL1djJ>G^>d(6o8#3{w<1m>)WE`2VA)h9Qzp zV>mGP>*k;1X^ri`WrYVE*GFUkT=c-YX74(pJ|A>*dHnZ#6~K1nkJ{wk1(!6x7Wf`` zl-_+a73hs|nD6iEiy39qA&98}t~di6a_DeI;nD?CB(wj_sgF;# zZH|p5tYc^%f3ms~;`p?2Ep2;!uHbqgLA*vcS9nPH&`#nV;ZE=1CXT@-{^0wX#EWnW zI&QZ~Cff*0-s#Sx!*`Qc5=a477yY-phdf?YJ6{6c2lX)RU(4Mt2mrL9PNFVYhZljK%5mi{1w2; z-hRT&FLh{c2pmHAcQ?o$-pR$!jSQ(k8_$^o3NPN54CBB|SRW@2-YKE)2l)YlH9tDD z$Xj?-fVUqNJ*3 zaJ9J&Q*fA}+Apo-9Pu#``cx&ios8l6l=!c(DXqgl}zX*`XV*1*&y z(!=wMV#H%wQDhcS-eSWb3>VWXw-)v54wv$ESuE2bD;#8s9#ThlC^9pZrTN>qLqRX@ zGL$-&&KQaX|4+t{Fr-fQ$&^M9*!nOkyrnQJ`P3Lmkk2{iFdhJMf_4};K=$5{tUTOp zZRnCy%8GGLF^GzEusjf{1$4h3ywfRitx2=7fowi;7#z zXQ(aynwkG2WTOcSeCRLbrC^(-)5woB+5fdLX8_i4?cp_6ujll4$`ZeuhZh{$Yl4#i z*x636e{z5=ixYv*+Ohyz>lJY{6IP0OzR(l^{BMQvpvw|hAb6E&x!uR7e_^kQLSPZ+ zQwz1BHANs>t++cb^S0(fcRmwfm{B~`ZCt!HTO+DKuA_C7kZOdkovFZQSzw|Ek)&4~ zh&L$xeBKNi&^x1HA#M?8j6QvS+KW-+1B#X4u)@GOSiEvL$;^D{D}J~EOKwbTfW6cr z4|oO~xmC3Puu^`FndKpM`yFg$sZnyFJ~;)k01U zc|eXr$@yo8k2#DZk}0bEjRucHM~^q*gF7j_{FHMdPxvi}b?@~YHe|WJDbAH-!29`F z$v4PKVg$gD^5Et}U|&Sz9s`0uUF~^wMyeZ;(g6;rn9n+~FOA`K-=3=|*D)7s1(&63AN2A;3$mT>(vFu&sbdNp1e1;?P~_ z`LcY_^|$`qMIQ`~<}|J|;?~QD9MK5VhfRAn8y(i)U;N6;{GGDokMFQ|TU9mTjFbhW zdhppQ0|3)<+1=0ghZZzJ3v;2H3?;In&LZ(0{Ok?*9x-w#R-Cv+@rg#y!54Vg)x&@c zJH86?22xSxrk9U1jsW;l%Zwk8>GJSj8WunGG=2ffllp%0kGBRlM-Ot|gj-TfT4L?? z`Ox{LJ;9<~z62P-T9>f6`PjNMj;YtY{>(E>EfBAJU+X<5@~MHtO%+yu0b@;cZU{W_ zME&QaZ#;l35ebOerfB$?JDmXg;%+I`%d?4P{~m|?$&gR(O18KgOE$rRNX81^A?bt( zqfm}dOuo`5z<5=vt1K%(Xnwpt5ue?R8$X^#;_D} zF#odPcg`DZk~H#=yhw)S4tri#jgk!Zk0k&Urz&Uc!~m$NV^9lSc(D6p{<0Yqf(OU;dM=70Ro(D5VcrWf$AXrS53KH6FGzuhsXOm zaL>iUgb_X@gEOXx%4*5SQ;pi^Nhk$jLcC!B@E7fm_LC;7ENL@ zVykFx=sFFE^oA;f@}KNN@vG(K7lN0nuB8m71Od^uMhhsvxd18(-F3ncqy*|26~CI~ zmvct>Ldf=$Ln=97&R>SQfDPp8(2X~?vDk=J>wx}X@h?FOnQp*ddk=9#L<8BSGxa%CwdSaJH#H3M|GkwVS2Wq>+7Wp zE-%)0lj(-cFGk`EO9|mMAiygVr(>7aG$${+bggs@e&a<)71p?5>N})jRSlG=2VVJm z{yg}eNmob1Z*=@fn+dv&%o=D9n>{}~0jHAo@bl^bAfpx$PwQ0lNs zWugf{4^wmRp6rCA7bN(GA%r%RIF@l87V`y71rKHA#H=ejHvB*iN4Dts&cMNkDS(^< z5DMxFkl5;S0K`;V{eYM>~S1WyyGVKdD8{9BA2bzb79E#N8%`ce)*ta?QW6iZf4d^Y<{U|z2e(wY3ImcuQ z&Igc1ATa>Sfai_-HzD#sIsi{i#!xj!XlGzJWMSM|yd5$a!VQUwVKbL{T+FnSn)`DzrIyn_ft$R)YT}T5yaN&LW7cu0uv$M^B zF$7^p47!rV=VEIEff%9)M}DwN7la-GS@k>NpSuF^walLfHO?b&BrvqD9BI^<{twHe z6_aYqJC1T0{DO%we{G|m?6&HUj^pt`^bo?l$45PN{>n7SWc%90{Ms#wd94|xp{I)E*JU~r zxLkI7+ytXWdGlE-Y{)`i5^I{WQgq@%(0bG;koAbWEV^X1RPOVD9fl4gfxBek6o<=M zDO*?&v8_a&$pId6m7BjXs=n_-nXi?aBd@W`4|adw9N^yZ*UoUQ8;=R}6+FRNzvi3H z&x!Tpma11uH>>go%%zqCp66p-1#B=geoUX}PH@)+ovL8YiE9G}J#=A$Qw-FXIvZCT zc2Az$_o)j68;vptsF1o}ISH7ASpbh#0br`^uTRq=KU?7Rl1Xn~uua7Ay24lMB_;0d z-%ZRt9YYRP8v0n`JSIaJuFd_>S$%gQyi*i{DnY&H78Dw{<6T{4Jx{-&r{Oqr*9RJ$ zG_L7jrqD4M7VhQjR}7RLn$;@Vahj~W2`;d-ysj}^GR3Dc+-920Wi7?tLRZTK=ASO< zUJ74of2=IiuZ5nDZ1hD^mb(<2@4Qvpp9!K^EP*`X^d z-vL5V!iB(sXCifg5KtgT?UA4s)L-VP1L{7YRZ!i@jBWe|a?1YZq$qc69IE77HfTXtwhHkQx`7tRYLIBF zQ>g+o^JCYK&kg4jo)GqLiF{do28ju7ScP!$b5y#1>_IX|efpTN8%;s;SeqR>DC|PL zXwqgIkxQIhleckv$d0eX#A2GnSsh#pK|}p!BNYQJlfz>clR>v*%|sUMhpJ3`#27zI z#a>jf(t}xz4VG7GmpafwY}ik>yk%Slv0s)YpP!Ek z=TyG#R_sOm4!8C)j@Hb+tGeU-#bO5B zAnZ#ADE1aLiLd$j@G*wz0dRTE2eEA7@9j6J(ekKHChiOo&mhU2+@p@^4#`9ru-D`DVyg(0=gzjM4}KFdbl&*0fFM}& z5*z7Srt0=tqW2|C>t0O0@>c-h`(h=449+wn9eN?(ag`WE0~KmL(g}i#pZT{v`t#}m zz|_O0Ss=ysVx<-lpBUy1gePBpPlvjfF|t7%AP7cqOv|tqoCWnj}Cvk=YHY7we3o2O<72b33Ro1%mTX8~F&(l-vWhwD;d5%0+OOtV$D2!a33bo?ciS$~~lv07jASqt^bG;hgTk0zZRpL=~Y{D~fFLZ26G7scgPe z@y#-fI;H@gGYpgn^|%Tyj72%GQBn0APB7*%ygnMAtp=By^1RX{q@h9i!0Aaij-#07 zR7d`e1k)Kzy&OR=kw@jOl>oY0-aby_Mo&-jNndTBL z#A;}ek(j_?$+)p%nmuAv8*S)|mu+O#f?M|`omM7ZE`T(&uTE5g4UqbL58SxlXKyj| z04LxIL;zw9H(Xzt>xbmefojUDllK7XiJevOMGufggfI>gdzs}YUM-gkB=gyD64qGA zRdn>@a+B@^g7;vfv?e`#4>LhD3&WZ9c;bTnef($j$2+Zz2&1M49D2O87c+(kKjd!T zK-(F=|27=?EIVW@Xy2$_#0seeH`4)6P{geS16$=** zc2WYM<`ohtz5yoz02Bh}`@R!Mc?3Fxl7EJpw8&gv9+@7?7JE6L_!QjGP5`*KFdaen zyXLIwK4{z}2|S04t``lEt0#~mo?I4su)hFAv#&Ws$9ZGTb;h!^LKQ}G5+>+1Wp!6Z zdZnRBMdwQs+EGXs?huB;Q@W;8%W!TRpCKa6xI_kH#R?)#>p*!`^ zVuM1#y-^6U3!F;#UkB^JF7@l4ss&E0L5;_xZNLc*&TGBu5OL0HlV29Q3e4=1=50Xo zPJ__TJ-e}!36J&wbX(aKv`id-4@o<>m8a}MY?*0LtFr)%EXZBvKacL{ofa zSJ904b%$9z>Tuf%VtUcM%?eaqaHDWXae9PNeY)e51_rS=!D|}@C%QJqhAYP(-)l7c zu}&~%O+Bt{x0q8k$hso8l+z68Ki#y-%l1&6-xq3l3-JhOzd(_|VGmOv4Inqy9^W;t zx(0%#w3W$M%7kekGktxSqQttYvuF;|3II^;{B1zc{SHT|Y~~OmDJJQj0bP!-liv^0j@?vgna`^+*dRXut4W1;B74D{dx`^pEhXpB@(mieBsO<_`;h%#jgDccZ<;*nVh#ZXkRqQT8z>9$u7S*&edf-r2R6G}M?YJ25<)^~ z+n{<*E{QQJtW-F?_ZdbooZ_eG7}K~ICB_fJk%5yeL&B>3|7ig*&+K`xJ|7Bn=))A* z`1eazM^jv76Bs+P-!aG0)w=k(Gk&#fE77g+C)_GGYmeJ-aZ>C*z`-1zMPIFwU$Ye8 z#J=Z{8=LA>E(|@>R((QKi|j&k$fUev*RKf?PX_kuzn@NlT=?7FWw}t(3jnbFMR0K= zbv+a?)Sy;;hL?J)$pm7M0O(L(VUjS~P|SSBU?YY?DDzt4ONiP54wc(omER*bi!#+h z8~Wh>5Caryj9qThTQ!zft@WT-5OBJbe4%JcZEuEd0n8w6xf}3mw1?>daW=&3!ZG;T_@!~2wSi(Qa47Xat%#s840BL_{OA#;!cHKRTG^S}MhJTkPX0t= z^%~ta9jF1|1oyf;hu?Q+N)H%RA9ESL@MPhkSqUrI;OziLo}%O2H>A{cPGB^5UnSY8 z$n%~hn+hwFxgg!Xzvw^uB5h~N^Yc3F@rMgbI@@CSM7r>uL{7d=5p~fOpKG=49Imww zx1pwx+sarS$^2Ej@~~QvC$N>bf+ZzFs?)jEH@?J_J8y*^tRAEq=W3^dz1;zk=KTuq z_hN+ZLG-rxJ)TOp(SBQ?)!dUf3rJ6|f;PaIjZC+Kcj;S zRB;qOmihCU$5=!xmK!p}c4A(Nv2ufsQ3~D`#}f2@R+j##FBiY5ah-6m#u9+P?0$*CmuK|MRJ#f{aZK zqHfcADOoMEasP}JUl=^17)2>=z1M1F%y>o*belTy!WarJ1e**3A4K zyJmKqT^A3bg}3vW`pB%qq~AxsyYvS?vkz*&Q;wadH|#tnbi>&-GDp7S!|7oyGOMYr z(6@V1-3dQ0p>eXh?Z6v9GzT4u1dZ@;aIk=WCV3>k2zF?bGC6eRp2)eorNlkA+i3Dc zp#&CJvV*|6ajwnXx$LM=wLKMo+d7ZM#`-S;=dX2_K2ZmaO*o%G#;?`jQ!e6{lOfy=H6Sn80{3hI zg!V;0Pp#Y%punnCrDs{iQ$59s1i7M^qaIFq`>8s#he+Uid6PVI(OXE9iPnw- zdTtBD%{|}H!Pohv#6Z=kgZ#La55LEcC)HR62HSBC=n#cVX!iWMo-%F5?|zx_9GNO= z?>nHamv_9iJ(iZrdX^Q`Sv>akWPZ+k*gso8ZBeW*u5vD4D|I@h-&2Vs=|lw3OzKi% zW1>_-L8^(9A_o8@$LJ=5z1^oy$;IS$4{tYSLX4&af9tC^)N!vF5W=nivQJrkK5av( zhK9%_FoYgmgqpi+AE>yPjd|Uw@Yo9_Qzt%;=lfAyUF6A8=g^LkKXgS&;IZg6#%rN> zy1oh7dp~&YBWN>IKjZtlB4K1&$f}c(nd90CG5N(hOSNAagZ1!P;2CW)yIXAq4pe9` zMlRMcl|1A4IJ0W9al7%oNy9s?VXKezzu$i>GZ?rYnOnRac(C+bPGO}X&@D@BBFT@C zaSTd6NLT~xB-zDRCGJQdE zY8PmRzLbGkfdb_6)6Ly#YLK9)Z`cU%5@dWAx<{5{*Dx2T0Szt;Lakl=8R;ov(h4*% zo#g?+bnUx-*6q;EH;oagk@u$!k8|+f@N(t=E+;rtprP@e<^<2&HyWe8ScBg!q4RA+ zjIUXHcvLsHQ86_8FV(b#0`^0KL!GsS!WwL)SX7PD?#`@>KAG&3x+{9lD{(5MEW5nX z{q+0MQs4KfIa69&fzfw^1>V*hyfus8R+6=^JqT{x{*>WA@F;D46TM>d@=MzE6cFQk z>c9GFq4zYMTv2g(ECm1hQP@{anz|(lYX&4X_x;~Bjh|I`q%KC7hdo4zy5Pz0KL@$lOkdUsJESABVthJ}IJ^g2MGUSGAiL{X); zk4l<&C_-6;9pFGLgQj6PDJe^Jcm-e*SYR_q+pT)b z-8dx8jE_SfZ;;x%2QT<*#Jfu;e%9*kA7rJw4F8#V7W^mku5!B>yTZGV5hjiCKI0D~ z_sS)(qtc8)oEzu|6FYm8S`UZ#%4 zJXO_;3ANU))QJy^LvOKrIsMvm<(Cs*tS;XP-joRpuITu6>=f2q63zN~AhKVmL^kEk zNJ`Pe+?Ep{zmcx}bQc83QXqX?XG1vgEHpL@<82sDs6NFn^&NDZGF(iE&=J_!gmgfG z(k?G8I+R<|T?I+yVs$XN#eSBLE}9~SlZE5RHbPf*qLKan8^&&`KOt%86OC311HrSA z^9pt0;z6q~LS?!HxyE(GMD#{nz1A`6r@kt`C0u{cwkE zjT7boGmevtYT*}10gi~z+gr(hjR**DTNxKmU97$TkkDUjoEu+Er@IZmMa`b*fBd9F zXJChx0U96O4u<+taK|LxP_rhE=9qjj$BV~3D0=%qjVPQ6&C!Jgj8caPq1f{r_VUD= zrz390*eJFDmsFBQy008|Vl@47=POx`HZ6F4{<~rNzg|}ns)g;{@&3^{HcX}R`CZY< zqZkZj^o(iYxsP7#J?Y8dT?c+FxF3%6@KO|nQns>-DgJlaCZ=08Yp_66mO%s^7ADX% z9628f!tr-J$E}ZiXXslDMwn$L<9IVg?fw&RCMSB=hzmEok_}*el5vVS{kQ&MMiJNU zFqRy~`G?!70D~#R*)S!^b0|E`TZ9!XT9R`Vh$G1;_nC4`hGxczFnj@@8N8)|deEyy z?5X2csjTi>#lAC#-UPHD2`Jc%J8Jcgq5`wKN1=A-7^c3wa4oLy&!oW+%O^6xPeA_} ze$z;0Fv;@Cf}2!~rgKiq*F8m8VHAS9XUMt8_A$XY)G~GX5keV!w?Kw84i2@5s`t?1idayY z=ktax5Cwi&#J{!3%MROP-)fa7H=2Fsm~pNZs0FF0F0y1IHscJX#d;s7`TqY; zBq!B6XBkrA@w~+6j^e@0g1q09%qyAx@5!;a5#d>IDq=Vi3Jp!m%KF?<(ld8Y#ud#j6>z}J1klXXzsmD1&4(!P z0c{8jhbgrH^S_NtiiiYi3%I-J;53zG)6mX7hJ={w8D40)FBB~BeofqCtvPtaVolal z)yuC-qA9EnV2f)1W-OnNRLgK}oXIQJlC?52!g*0C)mO9yW=`oB;4L%?|Mi}hQ&H0M zY`C>pGl?3=%DlQ*a(w=x)X7s3P;7m;RnX3|q}wuUTQa{lGUrW*R@rU$yK^nIAujyhd`tr!RdToB#he(K=ONvsy@fDw|6 zh$F33>zHh2i+8-hE;;LJRT&!6tve)Sc)RGIMbmJczE}S5G?2gq=n*!@@|&uuz9gAS z#HFGPT3TW2n79j?KX#VMC91@F>cUoK1007RvG8)p-r~Oe@4+PSGlCeM=pU}cR{5x% zGG<1)%Ghy`WUH1Kymz0InGf?ASI{dDR~hhE0?FzT^6^YNqMqJUQGBw6`6YZ2y|^>`j?1>^vv_CGe?V&VpvwcdM{#e#cpEFjNI-%I3fK zeq!-&2$=iuoN*>_*xIqO#|sn*Can>(a%)V0vL0rM4|u9q3}!4QS0nwuDG$TZFxsbU zyrHxxYO?+l?|bUwnzb;J(2vx`;x8W4_}bl{#n^}Weq49=-%xQVWU0a9^$5WwvL`$G zZ;u@ax^$c*jSP1SC5@&SD*Ty1efC1^olqNu?^ha;LRU|KGsf84#Z5@N}00aN*Fq68_)N=kT(+ z3d_`?R`9u%9~l~c79XAQ0x< zTfY~e{NJb0VND;P)%?WnE#Y?fO=2n%6j6VYtlj)KI1<7IM5)0HD@z7+j&)(+S^Rfk z?*B8H2=MQ`xMn2u#!f=u8Ox_}WdF@H_*@IT(!e3t+cDh6?HmH{1?@-TJLow|KIi}M z;>X~5NRgD9=ko6{e1K-eG#dw!?n9w8p8xYGXrY{beVfyeEX)Jq;(|v@0>H)K*SK6i z!T)Ux3|^2l$}Z^fR1EXxAV+&C446p@id2ceu@jg#YfO`Tlr@{y4)}pQDD^O=edqW8{WCRm zhHg=2KL_Qt_Zr{@txA8I{Lc&CLifr?#ka>JV?{V~Tci_@7V{+7hlOt*TL0T%Bp@q8 zazJ4QJ6i?q^ow95t+*MM|Cx(&%T{a_0}j#0 zFIKtQ>7#E9-iVyJ@@l@-+$lxuReEO z*LhyYd7Q`bJAU6qd;Y!Qsxcc>H&k%r0n`LEf3^VzB@--xn_0Zd3$Qij--M@Pc3#2l zpGhLoh!1n%5w?N@BZkKdQVHI4b_EW!O;m;nDP1C`uPx3*vmWCW_{BxnKkohYx&)l> zW-_`rsd>W-ZQL1@9%8N2zk4t>P|LO}+`Z+ac^e6+TLj@ym}$1z#tAG6#%@qLs&#?L zPb+lKw2D;POOyWL-hWV)Uyp`_B*e$#B9Zfu{RFLBba`^U2gDQOErC6~=C3&c@Fi7Pdqw3nW`CElM8g`CE^>a=;;CavAJY6*6O^F{%(3g0p?5*b>OrH4&JYr!yN zqCDJ?5C`u1Ab*|=ZH83+0xK94@A`P%t@r%Pq-ZQN`&FKvFqJMeN;(6{j3~YjJvz*3Nte~ z8Hs^o2wx_)v={5n&NE!Ji(f%W`*<-+->15)Fa0B>L5p1Gq6E9l%(irh$ZzLEeBx;hsHw9bt7V~blkq|{k3#_0| zCT5QrQepxWo`y!lov9{?WCkU73C5ofASs8h!W_RRp|+{k9BrZdaT?!G{|Nz-$d0k- z`NPFwLqG>-($(UNmnH?V=6kbeXf-yha2dA}_UlnM^*wr#Ej;T~M))M_xo>?@OZZ=s z6snLd{?Jv9p?k$lAc$7*inGSmS#_GecBS6aXqS{D@xOULU6s8N93f}b655`zO1#?7G4w{74eb{4pHOf1iv zPq{z39d{n5_YT7XjxyHujcwtM!+XPdyfXXvy{>52&LHvEq`eOabnN+}!2%<-)Km8f zer=5-s>zyb7VfE%n>CEzkQL-V9Y%z(l`qiUj}xyucdc7S#`p=GBr?}Bx{Gm0VJ*r==EhfgujLi#^* zETQFB*^NL6Rk^WBFMB9tidlF;J-~#|E9hZN)mW%S{?Md*DxQSr&&iNPhSP)llP{h; z5BZCqRW(N)GTQiQ>EV*#PxC2Q&=1H){)l#L%_+=&3{RbAiii#F;?Hu*uE0R^;_?$K zhL7V*fjL$b-cr2w%Ois6_d9!&@zi5)|1Dgz)#K94k1C2w55|by;eprWmtq?xvkarUgt;&YuBYJR48khknseK>G5U0`NnkG{i2WPA+ zNJLYeR@AE*XsSJ*`K&0O?er*3K1OxZ;r=47Ki(In+f`^^%O}sU@QER(pkT#~VZ}bR zgcWI#Uj)R?Q5*>e)i4ZKxr5|K0hLoe=iI~dxgOfg@7x=0pocpH&2~j+t|2aRgcX?`Xf8h>?L1dnGradTr^k zo2uU17b4>GDPb?S?kvtipg1o?A(e*?Zt6Z?GJIOS`#AYaUnBkr08_6Z%Hf{8ve(5r zNIF=@=ALw=+M{%1TA*|9Oj{fdZ`VC8sDpb78qWUL~7>qlcl61$LPtX~yzBK>*eNVv24o z4G$nRwf7*kRJe~{qeUi)ItfN_Sy6x&A`q~-)8+z&LC-qk)oM(kF%L{ITP;T3hhTqf z=;aK3@?UTwIJLdfcof9fj>OLn6`P+G$z7R({EW*m#Mc73iX)MHp?2T1PmMK;pUl6g zf+vE=hpobTdJ=c1|kn|uCU9 zP2gjPfi>CHzI%#M@DoOCSH6kzD~@zPhb9uD)xI1oS-26NTNEYr=d~Y-PCELq%&k)% z30^-}EMh&ci^pG7|Em^p*q8wT;8M-;;Y#2P7ynJu&+wS0O%I{BJW3VdRV^9sdhqj> zgP!(gkbhZqDA)rx1Z_gwaWUT&NJzgDrI5~~hDmC8d!^&Lb{RnM-_)jGxmIrKx>!IM zl;$@Zw?yZK;s7YG;Tj-ep++;^IBds?x?zda)Kuzi<*E-E(-P-j75 z<@H>SXN8=f%fR)q^NU%Dc&UBHxkWoMBf6$CYe>;ESk*mt74GTGTvv44yZ02aZE4%i z)H~!bV(W6;wSCBzR0gaFE(e(LCk%ho_f1#8!eV3JJGYRB8j5JbY&1l!eykEJL)1#-Nl*gu%M+Rj#y4R^c7Lqt*ud%D+%+yRsD*euReyC z^A4K$-B#;hnW(n{Vw~M%~BS1s~BVrA%RpkZ` zDK6Y&s|6s9pN#}0h(Wj2E<*cQhv^stZ`Ys>lCZQqq_8nsbfg7#2+7|Deup8#x1IbPm6yfAX}yza!TZHzI&Y#U#JGgV)W|a ztuDmQtpL!?3L{NNdH~FxZ-YU`gle`|yLh~Dk(LV*mJyrk@h#1vjov$MM2HhRYDgU1 zjvfQ&hZiyNplkB7wst_shlqoE_Hx@@sa^07IgSJyKjA&O^PLEU2bpMop_|@&oVuD; z5mIe;?+h5;U?k3O_Gc~9F%OS$z0tP=F#VG1>hY?dcl1XfjKAO1$<3)(mhP>`yvZul}42 zJOkgme__-nkb=8%OmZ@eHaafd`nG*fHP@(FwBrUj7&HY0)NND><|H+c; z<{jH!M!<4@+8NzHi{O{z@5J*SUb0c8vRqnoQF7bRtpOTsVkLNjwON$2s94x!y)ju|g7I^yCOkc#2qdxDVXkQ*xz$B%l z%GTfHTj2(Q-ZP^E4ZxjRDs7%@V6!lJ(C{Xfg6F{8p-?|Kv+czx$89^tMU+Nq^V$vB zc0bR=9&ux4ozseYDVr)%YR|>&SrptDBoJCcfp2tkc+spsTQnF)YEvJ zC}(B5pPE?qQ)!MCV+Dq;U<&uxI2y`MgsE#-)n>ohe}2SXgQZN5ulaF+ppXTNc_t=FRQv2aMJF1r4i{9yaCwEzu?xutozm6y1JV(uWzwFkY?-ZNlTiiXdjkw*Z zL$w{VX(U1byXNGPYijV`a&M%icVwe-GWulvz>py8+1?NizDFZZ%(qtFk_p6jg6HeC zs9tHE&J#RU8+T`)buJ%nX)K7gPU(hH8|-nBSwwZ*<_!sWeqsLjXyy1NYb(Kd_FJ~m z5~fL+Ek(5JUm~Vr6~h|F4rZ?9X-xaCf_Jp#j}m~Mzlt2a$073>J1g#5Y4a+rWo_*E z$brqBoyol(BYx_>;sp-ko!Oq`GuI4MEw(#u(VsQTn^-o<&+$KFC5z?zy8fMYuh3%F z_dOoQ<~?Q0u}69KX0#0|*TcMH8efGLv0mz5k+?y7Qf{^T+G7{edZupsQ)WNMi-+eb zx+#fV;$h*U&szksh9E3GL7S-|YJSBo%~5G94&Oe#)MKDAIKWQWN1IR}R115yd!gHV zBsYhXsSX@s7MO(8jchwJE7^^C%K2uwQU0uhLsz-G_DOF}SxCtC<3>A2yhccwkLWhb z=1%sb64!$hP{pjvkS@>_NJ^p>pTs_Q=<|9kh9@3(GZmOeW!tUsUO416t%t#G2cLYh z3yGP$I5u=yc177)Ojxv9q9}zO(K8TrLj-HIR7VRC7l3esGBh zYIc1#KcQQ$vSh>f2i3fbsaO4lu8%dDBgw6cx03GXl{+6ZyUz{3N%gZMH~FzeGfOY2 z?^&H^DUDa8{L;Cm&0H(-K$o{~UBE2l`t)mC`W^<^WJ#@mPJVFYQIf$>QkQJopxRWF-jX55jlUM zHs`Cx4HkLBpC%#idy=xz9{&d>l6TF%YP2y)Wcl3P)s|7VW_r(`b^3EXNg@KBL7kRmDzBC(Y%f@sEWdYTd zYq!>4oxRrSI2Q5O11ZLt@W6_YXGwd8jr8(8<>sZ=&ooF-qDU3$Wc6RD7JZV{e|f;t z94(n7!`mVx6}F?_B6Qbw_|}u1y01_ACmzJ@zYtF6h9*rz2+Pe%`Xg9E1AAF49pT?mTO?CF|QZ@ycIR~)x^x#o7uuA zsnRq;8XHBMLWQL&&xD4_!O2R6;elGV7uC|2)mU!DpIeFt+Q&zSHueo;hhHyw2?{4V zY%fW3mq7JC`$WCG^4s95yI?~}$@?|I3Q-0lwl?lA2(gJNc>YmGLpXq__3IZ?hpwE} znXF*<*vTQz3ws_SeOg8@${g2crpW~*HZ{z!BgTBsom}u2&f{zntcVa{Q#@LWw)iAg zZew;XzJQX{@rFIn5Mp(>x<=uUB3RM&&9O<%7l`}ZMr^6vS3EWYLkh^YVu@1c_R?fX z>`7zQf|ID4lU;ZRl>)eX-HT|O2g9Fjf6ZACc3nu;`OwLkBtt%Fh8r0h=RW)7YfJ3t z(0Jo;PG7~*{v3)!08{{HtRzG6e;7a|yFUHn++OS^S7?DNv$cJNjP9Y;nHN_cltfO6 zuJw>*u}@SU4_k&U>)5|uP7+IhS}`D=+YWT9sUf3J`ZHOjlaWJ9r*p*n^(1OledOrQ zo0Uyg3v=$23V~JV&^Wk+uY5CdF`de?d|d%~$<})c+0#B}k4xuJ{nUm^V$S+;H09-4 zB;Tv0IFm|`h|h@#7~RMk0jo(Jd(Qe&e|)F3#+#uuygB`Y$($BkM4qM;vQjm-_;Cy^ zb4l4u16@}RgDtdJ7M`{W6_$9Tp&>|<#zUfrjYyZb+q>!bt4d!i>qDrx9w?AaTyM!? zn9UL_=1fy^jSkVu-FP~gSzedqv7jU%B>$Q(5#i|l$oS%|{g5PlY+2`2UB8An#O)ct^6Qs{21}~XXv>IBe`dZ&dqsHykMF}kigH)>v2Uz2p3lvf@2@9$ ztjXe&5PnHa4!ROj5Xv}GFUUc|UbERAUqHM$@|+^SOOtOZi|k|UDM1spvT!aLdy6Ww zG=aS%xk_bxNi&VpQ=GO;n9WZUo5?&XjUOZ{macl5`sHd(`V{e|--{bcy={Cl=qhnG&WzU}Up7UnZR<_deY?Qne?+CqZ4} zUj;qjT#DavX7GwQqCIKQ~l3oefx^>LNT3f8nE2gEXUh@m=siaZm zi!RoCY0M{{_nX!M)$>GN1BH@qO_$VI9cq6)s0Z{ftd+ zh;=R){YzD;nB8emIih@#dbg=rGq!Z;l+VIvs(@_3vU7ct?EDA7s9fX6W?oml+IMp< zxdrF22`p>%3wn4o^hIsL`5TUfqtavH)f019Dg=r+F^fcSHX9;k5Q8nsUWSzXSHK;r8o5!RJj`NHXO8_?8~G>edAg z8gj0YJEx}gu6J@#;9ZW*GW4@aaRdkONTApI((?_LNt2;aFZz||`I{Y66z#RVsrjEo z*4+6LI*0l|!%ai@r$-dRk7dn$rkFou(?c~-cAoXYc8xHbM}Nu~BF60WNZwQ)uj6Zt z9Nyzdmpd_CYl%ob`!~MPWv;y@Q03q}DIaN=XblI@vgVWdGa&^si@|k*^o3e196OC` z8RcM7{&Lh;pJUYb^!b}peDzPiw^S&6#(j%tLc=wL)~O1S1GQ`uGxV`o`W`oaLEjDn9L9%&|+Mh^E{R1l}ji;TuF|)chRB|`T?k~pnM$M}&(1mB+%G@RR zaAEP}4I`y|S0e4XdH_Aj*X62nW6KRtDRY!QOjq9AQxmY*anRlVT+SzHev6j`7y7)v zTe{HymT~7IQ1Om&&tPFN&aJNFFE;&>vA~tD%z<@`QgPGp(Mt$Txhgkf(hzrh?NSCkf6Vi_o zww0e6HbVDW>LgzAhA~k2$4kGHBf!hBfmW3e&V}silg=kdh!9!L@Ad`stkX$7(obPW zy)ol^4a-#@B__&q?{G_270*NwSoQRhLw~S-gs8)h=d3GS6cO|5-+KJ@K#5aSiZ?wx zG=(1RO9NOL%&-L|H%y>8-{}!oGXo%VN{ocbR(~AObk|j>@kr=CD6;*P5^lIp>4I4)T$}zwwhNMJZQgEGFY?Dj-{Kjt0fU#kK%3MNzi&icD*=*IIjprCq~BgAC^T!zcs|HZ zRZSbdZx)v%j&|=PGT0uV5^cPwUD}N}jT+~4+VMoRML~}YXIl{IW5>G}Q__LLH_uZt z`-RI6ukq2!E6U%kT9}3gu@bRS1tH_J4BzSTMit3D1QxOQl(a}qd+KUG-QxZC@23Wq zJ#im%z1L$}D&06oEUQA6vmQOu4>V#a8DOMcD1}`R- zBMvhqkjwHrRdEdOe(eMK#O!qL7eYYg=rs2JF#8xnG_icVvBwxu_>hg6WfEpQruK@& zSPrYf8>>i4|05s*S2ipBjJx0HF|7QeVf?+YNKoS}ke%Z7K$;Ypr%xxI{L8?{orS@p zmQ914{lm3=Fj8?(aYT1blt6%<{~i zf%^N-LL31Mk2=P|`2F+#T+kngDFJ=)16c$*K{Fwi!(-}fy7c>go*kw4tl z-=8)>ZhJ-?69esC`1_BO@LTI`Cb-d8)L-|_unxC<BJsL8W{w{m1ni;InA@!fE_rFAp~PS1a13=@$!){049cLT{e1V z!0DH##Q4TqJT&QMem2oN*j2685zauvJX@wJRdo$XB0PdtV^b<6BR$R^1mjh>yj$e)Z96)C} zMxK2SqWDnKCx;|B3>+gsL?6=(oR^c!;LvfqQ?YS~6}TwF-*2z{>uJBaF^;$Z(L1k7 zY8WcO$>`&)K_hy^%Q-@Cg(wYxoHBdwMU*&?ZBF<$@X&jT zj*+;yGHq`DMMSuTSRHSlZ}YGYyGV=#&TPK}Va5QWOLkQ8KLpv9iR=FGSTsDC(V)OC zC5}aKVXsFpqQ{{7<8x|)v|JYo++c*;a0`M*k>yGdKHLESUR=cXbc7y1s2cMJpA>Xx zS3HGihsxO=!l079#Dk%?;DewiT|j=oinN2P;RA<#+Af=4a(JpOUB*JYn)0^b`S*Q` z>^=YI9ro}(y|XoJF>0VaDFLy}9km|V9dK_;tn<*iE*^wzP5If*xQ>T@bd-@jX8GwY zi!ehh6vPj03*d3YfMqYWxXFecM;Ewd%(2E^ksO2Y8P5O*7d!mV075(l#OM1Ev$8~; z_W`~LBno0SAaZ}mS#!G#NQPaP!9s38HEWcZNs<%dZvszaEPya0O^nRpOJ+bmYbhc+ zbp!!mEW^a&MibK9y%WRVF&$pY6#8hcJMqX$_Lf!XUx@)5p16eC!VU8CU{hmzIAoF* zhZT=aL&o#+O`RrINIllBfs!8c$$1@mmks5QTcSvB8)z(-5H}WTLF*I*v>Qn|1n|qs zuun`>t%9d~j%oYOn5XmkdY168(!#0Nw}lcPriQCtf%n8gw_kru8WAGQ#8^B9d5YEN z)Z1s+R6kheU2zPzQI5gMdVLFh^Fd0v+MF%ZT@fR?NnDFOEXf=sq)?dAcKgS-^%2O} zjxXTclRQ^pO&7ap05DoJ9Z18L-{snXXN=@KP-a{j$Tw_@Ez-qbrpWxH$x>=B3b>Y} zYaE2y#l}4;e~u)a=9mu`mjQgm!V^p(W^G@$vOMpyFb2V@kr7PLuTyQ>#f*{%)F)%`^me7ZUR%wiz82iHRSEhVohc$f33 zGGDTPy|P?n`lVH;ja;{-Q)q->2hK+Gs;)%@--^uC9!0kvkw&(3Wh8iFe3C$Q{Io?>q(S4VFSiG z0&mx6I@&UMv~RuH1aZe~@iX6jK>*2JN*v#uF$wr0ziQ>Bp}o@zh?%N#jDZf5 zud0BR>bXhIHWCf2dKL}AgLy@_*5`#F@R`*a*p{C%JlaiSG@M+R&=rCmD#d2Rm=A3c z4I&GfuRb7Xwgp9ViNnKva+Pt1nth!X(cD~O6ged25PAoezF2xuzFzep8-GFM?J*Wh z)c$RZ&{bvA{LEQyv->3MsR!_C{kXI0LYMmH!rNIm0NQGu&MBQFSO<)-^@hMRP!k1G zd{}SSq$owX#+|@@`t0&FNVe=1v&)UGlZqM{u?fZ4cn1>m}>2qH9}QlCw_qLzcjJ zlxjL*D)?G;6&m-{W#{{;HGOZDUV)|0MWI<~pEGpIx%;W3r2?r+JngBQad-Q-&+VS~ zCsQQ|CsWg)ZuWC`W+0c#=RmUmM!Mibv$T@B)-5bI#e0}<`$OJhlMK$!3#~cT7IhJX z^k?s~YQI~zC96HHU)~hlk5&Wrv17_}q}_?qpYm3e&1_D6_WjiN^ju;=o<4(Z00bQv zw+vqKEcybH%$a_3X7#LjVW=PyY!605qb@g?y6cB>8*g`ad#iM!467KLTwc_T$MNHZ z`zRE~N!oFe)@5lFjF4q#aFR;%nxyVShupekT74Cot5>zmi^8uC56BD3^{EE>vId-s zrrKx5oB}2BRs;)O)n}<9dE%3XE)NfB1=u{TD2x~D5T+b0Ez%tgSFQ$!h_gDJCY3B0 zJLaSrYMpG;2A8>8l;7_$-o{};-RVDZQ@4Mw8fB;pX89vuTPITQUNF?Yd2}xf+2XC$ z=gHA#=Mc&`xT<}%$Uzs5rI!a(J+1G$*cqp4Wo|`}w???LcIAuU?^B0#`M{yztYg30 z8|>+VR|Ii_HFgKH(MTKv)vj*$J16sdAzkjE5BeB@)i9KavTk}s7iY;xj~zYktJdP8 zal>E-X@wn-G#luduf4_4lpT;hghFX^uuTlo0aCf^lZxfoQ&N&{y=l`-q@j4PWUleh zheBH#M+2{!UNSMoAN9gDarGss4s%L6@ydYY6JGuy>x8^eH!PGuD}97PUP3eN3v0%A zL#;_Gtg#FGtZ&pdZ>NWT@ju{xbC>mixYI09f^h$~>et8V#d~rV=%rAI5SDdhT1yq{ z0+D=bALAk>3OhQaZ9b{^6cYO82)2`6S%kcfD&I;ClDPzC-F#><8pA_B-w-?^6sCFE zHv8$!O^xP^37|OSmO&9S`6Ap_@iUVKl}t*H+;ge0EX@1CtVcdnHx~QtNy0+~NZ3?m zoV%xV>O34`sk6^JbG@apndW%|w=8zJaj!|zGemJS{O7JfKP6PiLo|v`=tm2W*C^wp zks3F*M^bD;MuE{GLzlcBua19wN}9ukA*&djVpennPViQtr(S>49XjDrcp9etfAOKA z3Qo9=Gv%-3ic1Wczzlbi+lO+?7dU3GCS1?`V9G}-gh25b)n2WPA%+m9YT&prkH&h( zz;R_wSVCR#3cebA0^!B!z_DPE?zt23zsqY9b~*Ps)Qz93Bxn^c9iL+z1_p{%R$B7@ zfByIX^9z8d%YN?M{~loSZ$nXE@ksF(+x@@K2{&K`CU5v8g}y(f#MD9`xC7aYmle8! zpPw*@Pa>K)*uAcg{?G2mBcj-LC}!HRPl+h4v>|a4>wnj%Ut%Io!qJZ(zp(S>wRx65 zZxHZ+Wyb-kD`}awX0ba$baG>^`{uHaEzoH(@WL1-%fH;YLHcJ95~xwXtLpY>zP6Lp zSL!$BdOrOC)&&wbtQeVBxEW9119Hk1jL|P>aedsxG>u~6z-9TJ6q8vJX^xnH@&PyHZW@* z1I6YVV$|jci?`>Rkg`kugRf4D*&QIoegXj-aof(|=$fObpDdP<3mw~pT^L;6W2$bf zKf?wtz6oFUku%c|Kr-G#6*L3h82pISEE0)V26yH4P0#Wp?hKFHZXn_aM-6J)U8uS` z0QodVs^zsx_(wO%c0djL3CuVev%t1*2KHflCeX0}k05Ul&Nv;gd!A80i3ER(Z1&yD zan7qm7Gx7hpw_Tzpct)p13wj8LphVraG02^Cn%Z)Rj-{GxdH@7CFminE5wSKr+Oy| zS;Mt!{7%uI^FK$**_X!Gul@;|AH7*1`c^0nahHVRF**W6j3_!n+ybbMzt@Up3{3ew zHT8$MR{`|^Q^S>nCZJk;sfBd+mpC4Xy{gFZ!TMu377(uevbmd2jo^n4by?4pHYFZd ziN$I_9j5aR&IY5qoZWI&isDB*$tHtqX`3HEof01jIF&D#&a;A080vlV9yOI`$eDkJ z!;+b<+o4?n;DBY;hzG~7=yt@ZY>08u4{3lSMjY3A+GDQqH1mW?TyKS~;xm@U>9Lg=X^G|pHEQ||FnyO}`FvLvwO2yIfxKXR8ymD4GL)FKjI3rN#(!j_ zh{%hI*El3FA%VM9Z~`n>e_>)y7t{Q$Z?fO=xR<72w+%Yovn2z1)wl$yNOI83h)owC z;wz&;c~O15lR}m;dZ7)hjoN6k56mjOnZ$St<$JbG34QmT!;4+M8l0hZvG7pRGr*a; z+0ElwGriaAB+j{LSf6-#V3V1d?ixmGk6e+K*x#GBkZ>g+cUncPJp`8uKht6ga?=7a zVI31O6{g-5SBG=U%j?vwr}Yz=tt7&%zI>R&T{B6*KZz8R_fo1WvGGn`0)oVA#EC1; zP(o^$+{Fr*H`)e%$YCcZ?nUn^&^!e=v8Pq)y^$07jR)QQc%n7%wCKEybr3f^`P;CA z$hXCmpiK>f5clABtH4DSWb+j=)#pz0wUVtSGSERxpM_5|aw~4UJHT^>wt=GpX|m&i zU0!AW(~u(1*ekyGq=v}3SGrg{!Rd~>%PPr@mGDFWid5u{A2JZ)$Ux9zYofJ@l)mF6 zB}oQ3L?E3yK56$Q6d!M-E$*L{oFNKFv(WhAl>1-6foXZdkc}KWmE1Aj;^Ed>p8fOh z{;r`z?2!HRkTBsGz2o$&$>)Iur5S({r!Cj81O@}9+VRO&wdR;L20inH_VOyYJUE@RrpOwl9-o9&=fO&f1g1d{7rui z#vU5j@b?h1gJ>xMgX$?-#r^kIov#4f_o5!gA=>Kt_xF*55=sf=|Cl_EPsv7F W6x6 /bin/bash +``` + +```shell +./gradlew test +``` + +## Project Architecture + +The backend is organized into the following key directories and files: + +- **`src/main/java/gov/cdc/reportvision/`**: + - **`controllers/`**: handle API requests from the frontend. + - **`services/`**: service layer for managing templates, data extraction, and interactions with the OCR backend. + - **`models/`**: Data models representing application entities + - **`repositories/`**: Interfaces for database operations, + - **`config/`**: Configuration files for security, database connections, and CORS policies. + - **`utils/`**: Utility classes for tasks like validation, logging, and file manipulation. +- **`src/test/`**: Includes unit and integration tests for the backend. +- **`Dockerfile`**: Docker configuration file for containerizing the application. + +- **`README.md`**: Documentation for the backend application. + + +## Key Features + +#### Template Management +- **Upload, retrieve, and delete templates**: + - Allows users to upload new templates for document segmentation. + - Retrieve a list of all saved templates. + - Delete templates by their unique ID. + +#### Data Extraction +- **Document Processing**: + - Connects to the OCR backend to process documents using predefined templates. + - Extracts data based on segmented areas defined in the templates. + - Returns structured extracted data. + +#### Validation and Error Handling +- **Data Integrity Checks**: + - Validates user inputs and template configurations. + - Provides error messages for invalid requests or processing failures. + +#### Secure Integration +- **Authentication**: + - Implements JWT based authentication. + - Configurable CORS policies to control frontend and third-party access. + + +## API Endpoints + +The backend middleware exposes the following RESTful API endpoints: + +#### Health Check +- **`GET /api/health`** + - **Description**: Returns the status of the backend server. + - **Response**: A status message indicating the server's health. + +#### Template Management +- **`POST /api/templates`** + - **Description**: Upload a new template for document segmentation. + - **Request Body**: JSON containing template details. + - **Response**: Confirmation of the uploaded template. + +- **`GET /api/templates`** + - **Description**: Retrieve a list of all available templates. + - **Response**: JSON array of template metadata. + +- **`DELETE /api/templates/{id}`** + - **Description**: Delete a specific template by its unique ID. + - **Response**: Confirmation of deletion. + +#### Data Extraction +- **`POST /api/extract`** + - **Description**: Process a document using a selected template and return extracted data from OCR. + - **Request Body**: JSON containing the document and selected template ID. + - **Response**: JSON object with extracted data. + +#### Configuration Management +- **`GET /api/config`** + - **Description**: Retrieve the current configuration settings of the application. + - **Response**: JSON object with configuration details. + + +## Troubleshooting + +### Common Issues + +#### Database Connection Fails +- **Cause**: The backend is unable to connect to the database. +- **Solution**: + - Ensure the database server is running. + - Verify that the `DB_URL`, `DB_USERNAME`, and `DB_PASSWORD` environment variables are correctly configured. + +#### CORS Errors +- **Cause**: Frontend requests are being blocked due to Cross-Origin Resource Sharing (CORS) policies. +- **Solution**: + - Update the `CorsConfig` class in the `config/` directory. + - Add the necessary origins to the allowed list. + +#### OCR Service Not Responding +- **Cause**: The backend is unable to communicate with the OCR service. +- **Solution**: + - Verify that the `OCR_SERVICE_URL` is correctly set diff --git a/user_guide.md b/user_guide.md index e9c312d4..f739b154 100644 --- a/user_guide.md +++ b/user_guide.md @@ -17,16 +17,18 @@ ReportVision is a tool that automates the reading and extracting of labs from PD 2. [Node23.1](https://nodejs.org/en/download) 3. [Tesseract5.5](https://formulae.brew.sh/formula/tesseract) (brew install tesseract) 4. [Java21](https://www.oracle.com/java/technologies/downloads/) +5. [PostgreSQL](https://www.postgresql.org/) +6. [Docker](https://www.docker.com/) (required for DB and middleware set up ### Installation and Development Guides -1. For Frontend -2. For Middleware -3. For OCR +1. For [Frontend](./frontend/README.md) +2. For [Middleware ](./backend/README.md) +3. For OCR [OCR README](./OCR/README.md). ### High Level Architecture -![](/Users/arindamkulshi/Desktop/Screenshot 2025-01-07 at 10.32.09 AM.png) +![](arcdiagram.png) A React-based Single Page Application: This serves as the front-end user interface for the application. From 3c36b6274af3d0fc6fb6ecd263b6ee77f9c6c732 Mon Sep 17 00:00:00 2001 From: Arindam Kulshi Date: Thu, 9 Jan 2025 10:35:16 -0800 Subject: [PATCH 3/6] edited OCR readme --- OCR/README.md | 160 +++++++++++++++++++++++++++++++++++--------------- user_guide.md | 2 +- 2 files changed, 113 insertions(+), 49 deletions(-) diff --git a/OCR/README.md b/OCR/README.md index 301d2c0b..54c829f4 100644 --- a/OCR/README.md +++ b/OCR/README.md @@ -1,11 +1,42 @@ -## OCR +# OCR Layer - ReportVision + +The **OCR Layer** in the ReportVision project processes document images, performs segmentation and optical character recognition (OCR), and computes accuracy metrics by comparing OCR outputs to ground truth data. + +--- + +## Table of Contents +1. [Introduction](#introduction) +2. [Installation](#installation) +3. [Running the Application](#running-the-application) +4. [Testing](#testing) +5. [End-to-End Benchmarking](#end-to-end-benchmarking) +6. [Dockerized Development](#dockerized-development) +7. [Development Tools](#development-tools) +8. [Contributing](#contributing) + +--- + +## Introduction + +The OCR layer uses **Poetry** for dependency management and virtual environment setup. It provides: +- An API for performing OCR operations. +- Support for benchmarking OCR accuracy. +- Configuration for different OCR models and segmentation templates. + + ### Installation +### Prerequisites +- Python 3.9 or later +- [Poetry](https://python-poetry.org/) for dependency management +- Docker (optional for containerized development) + ```shell pipx install poetry ``` +### Running The Application Activate the virtual environment and install dependencies, all subsequent commands assume you are in the virtual env ```shell @@ -13,32 +44,59 @@ poetry shell poetry install ``` +```shell +fastapi dev ocr/api.py +``` + +### Testing + Run unit tests -```shell +```shell poetry run pytest ``` -Run benchmark tests +### End to End Benchmarking -```shell -cd tests -poetry run pytest benchmark_test.py -v -``` -poetry run pytest bench_test.py -v +#### Overview +End-to-end benchmarking evaluates OCR accuracy by: -Run main, hoping to convert this to a cli at some point +End-to-end benchmarking scripts can: -```shell -poetry run main -``` +1. Segment and run OCR on a folder of images using given segmentation template and labels file. +2. Compare OCR outputs to ground truth data based on matching file names. +3. Write metrics (confidence, raw distance, Hamming distance, Levenshtein distance) as well as total metrics to a CSV file. -To build the OCR service into an executable artifact -```shell -poetry run build -``` +To run benchmarking: + +1. Locate file `benchmark_main.py` +2. Ensure all the paths/folders exist by downloading from [Google Drive for all segmentation/label files](https://drive.google.com/drive/folders/1WS2FYn0BTxWv0juh7lblzdMaFlI7zbDd?usp=sharing) +3. Ensure `ground_truth` folder and files exist +4. Ensure `labels.json` is in the correct format (see `tax_form_segmented_labels.json` as an example) +5. When running make sure to pass arguments in this order: + +* `/path/to/image/folder` (path to the original image files which we need to run OCR on) +* `/path/to/segmentation_template.png` (single file) +* `/path/to/labels.json` (single file) +* `/path/to/output/folder` (path to folder where the output would be. This should exist but can be empty) +* `/path/to/ground/truth_folder` (path to folder for metrics that we would compare against) +* `/path/to/csv_out_folder` (path to folder where all metrics would be. This should exist but can be empty) + +By default, segmentation, OCR, and metrics computation are all run together. To disable one or the other, pass the `--no-ocr` or `--no-metrics` flags. You can change the backend model by passing `--model=...` as well. + +Run notes: +* Benchmark takes one second per segment for OCR using the default `trocr` model. Please be patient or set a counter to limit the number of files processed. +* Only one segment can be input at a time + + +### Test Data Sets + +You can run the script `pytest run reportvision-dataset-1/medical_report_import.py` to pull in all relevant data. + + +### Development Tools Adding new dependencies @@ -82,44 +140,18 @@ To run the API in prod mode poetry run api ``` -### Test Data Sets - -You can also run the script `pytest run reportvision-dataset-1/medical_report_import.py` to pull in all relevant data. - - -### Run end-to-end benchmarking - -End-to-end benchmarking scripts can: - -1. Segment and run OCR on a folder of images using given segmentation template and labels file. -2. Compare OCR outputs to ground truth data based on matching file names. -3. Write metrics (confidence, raw distance, Hamming distance, Levenshtein distance) as well as total metrics to a CSV file. - -To run benchmarking: - -1. Locate file `benchmark_main.py` -2. Ensure all the paths/folders exist by downloading from [Google Drive for all segmentation/label files](https://drive.google.com/drive/folders/1WS2FYn0BTxWv0juh7lblzdMaFlI7zbDd?usp=sharing) -3. Ensure `ground_truth` folder and files exist -4. Ensure `labels.json` is in the correct format (see `tax_form_segmented_labels.json` as an example) -5. When running make sure to pass arguments in this order: +To build the OCR service into an executable artifact -* `/path/to/image/folder` (path to the original image files which we need to run OCR on) -* `/path/to/segmentation_template.png` (single file) -* `/path/to/labels.json` (single file) -* `/path/to/output/folder` (path to folder where the output would be. This should exist but can be empty) -* `/path/to/ground/truth_folder` (path to folder for metrics that we would compare against) -* `/path/to/csv_out_folder` (path to folder where all metrics would be. This should exist but can be empty) +```shell +poetry run build +``` -By default, segmentation, OCR, and metrics computation are all run together. To disable one or the other, pass the `--no-ocr` or `--no-metrics` flags. You can change the backend model by passing `--model=...` as well. -Run notes: -* Benchmark takes one second per segment for OCR using the default `trocr` model. Please be patient or set a counter to limit the number of files processed. -* Only one segment can be input at a time ### Dockerized Development -It is also possible to run the entire project in a collection of docker containers. This is useful for development and testing purposes as it doesn't require any additional dependencies to be installed on your local machine. +It is also possible to run the project in a collection of docker containers. This is useful for development and testing purposes as it doesn't require any additional dependencies to be installed. To start the containers, run the following command: @@ -132,6 +164,38 @@ This will start the following containers: - ocr: The OCR service container - frontend: The frontend container -The frontend container will automatically reload when changes are made to the frontend code. To access the frontend, navigate to http://localhost:5173 in your browser. +The frontend container will automatically reload when changes are made to the frontend. To access the frontend, navigate to http://localhost:5173 in your browser. The OCR service container will restart automatically when changes are made to the OCR code. To access the API, navigate to http://localhost:8000/ in your browser. + + +## Project Architecture + +The OCR Layer is organized as follows: + +- **`ocr/`**: + - **`api.py`**: Defines the API for the OCR service. + - **`main.py`**: Entry point script to run the OCR service. + - **`segmenter.py`**: Handles image segmentation based on templates and labels. + - **`ocr_engine.py`**: OCR logic using the specified OCR models. + - **`metrics.py`**: Computes metrics (e.g., confidence, Levenshtein distance) by comparing OCR results with ground truth. + - **`config.py`**: Contains configuration files for paths, environment variables, and model settings. + +- **`tests/`**: Contains unit tests, integration tests, and benchmarking scripts. + - **`benchmark_test.py`**: Tests benchmarking logic for OCR and metrics. + - **`unit_test.py`**: Includes unit tests for individual components of the OCR service. + - **`benchmark_main.py`**: Main script for running end-to-end benchmarking, including segmentation, OCR, and metrics computation. + +- **`data/`**: location of segmentation templates, labels, ground truth, and test datasets (not included in the repository by default). + +- **`reportvision-dataset-1/`**: Example dataset folder for running benchmarks and tests. + - **`medical_report_import.py`**: Script to import and prepare medical reports for testing. + +- **`Dockerfile`**: Defines the container for running the OCR service in a Dockerized environment. + +- **`dev-env.yaml`**: Docker Compose file for setting up a development environment with containers for the OCR service and frontend. + +- **`pyproject.toml`**: Poetry configuration file specifying project dependencies and settings. + +- **`poetry.lock`**: Lock file generated by Poetry to ensure dependency consistency. + diff --git a/user_guide.md b/user_guide.md index f739b154..ab16fcb1 100644 --- a/user_guide.md +++ b/user_guide.md @@ -18,7 +18,7 @@ ReportVision is a tool that automates the reading and extracting of labs from PD 3. [Tesseract5.5](https://formulae.brew.sh/formula/tesseract) (brew install tesseract) 4. [Java21](https://www.oracle.com/java/technologies/downloads/) 5. [PostgreSQL](https://www.postgresql.org/) -6. [Docker](https://www.docker.com/) (required for DB and middleware set up +6. [Docker](https://www.docker.com/) (required for DB and middleware set up) ### Installation and Development Guides From 39ff2240bc03289daf020444d6e771bd070c853c Mon Sep 17 00:00:00 2001 From: Arindam Kulshi Date: Thu, 9 Jan 2025 11:33:00 -0800 Subject: [PATCH 4/6] minor edits --- OCR/README.md | 133 ++++++++++++++++++++++++++++----------------- backend/README.md | 10 ++-- frontend/README.md | 16 +++--- user_guide.md | 38 ++++++++----- 4 files changed, 121 insertions(+), 76 deletions(-) diff --git a/OCR/README.md b/OCR/README.md index 54c829f4..f594baf7 100644 --- a/OCR/README.md +++ b/OCR/README.md @@ -8,11 +8,14 @@ The **OCR Layer** in the ReportVision project processes document images, perform 1. [Introduction](#introduction) 2. [Installation](#installation) 3. [Running the Application](#running-the-application) -4. [Testing](#testing) -5. [End-to-End Benchmarking](#end-to-end-benchmarking) -6. [Dockerized Development](#dockerized-development) -7. [Development Tools](#development-tools) -8. [Contributing](#contributing) +4. [Development Tools](#development-tools) +5. [Testing](#testing) +6. [End-to-End Benchmarking](#end-to-end-benchmarking) +7. [Dockerized Development](#dockerized-development) +8. [Benchmarking](#end-to-end-benchmarking) +9. [Project Architecture](#project-architecture) +10. [API Endpoints](#api-endpoints) + --- @@ -23,8 +26,6 @@ The OCR layer uses **Poetry** for dependency management and virtual environment - Support for benchmarking OCR accuracy. - Configuration for different OCR models and segmentation templates. - - ### Installation ### Prerequisites @@ -56,46 +57,6 @@ Run unit tests poetry run pytest ``` -### End to End Benchmarking - - -#### Overview -End-to-end benchmarking evaluates OCR accuracy by: - -End-to-end benchmarking scripts can: - -1. Segment and run OCR on a folder of images using given segmentation template and labels file. -2. Compare OCR outputs to ground truth data based on matching file names. -3. Write metrics (confidence, raw distance, Hamming distance, Levenshtein distance) as well as total metrics to a CSV file. - - -To run benchmarking: - -1. Locate file `benchmark_main.py` -2. Ensure all the paths/folders exist by downloading from [Google Drive for all segmentation/label files](https://drive.google.com/drive/folders/1WS2FYn0BTxWv0juh7lblzdMaFlI7zbDd?usp=sharing) -3. Ensure `ground_truth` folder and files exist -4. Ensure `labels.json` is in the correct format (see `tax_form_segmented_labels.json` as an example) -5. When running make sure to pass arguments in this order: - -* `/path/to/image/folder` (path to the original image files which we need to run OCR on) -* `/path/to/segmentation_template.png` (single file) -* `/path/to/labels.json` (single file) -* `/path/to/output/folder` (path to folder where the output would be. This should exist but can be empty) -* `/path/to/ground/truth_folder` (path to folder for metrics that we would compare against) -* `/path/to/csv_out_folder` (path to folder where all metrics would be. This should exist but can be empty) - -By default, segmentation, OCR, and metrics computation are all run together. To disable one or the other, pass the `--no-ocr` or `--no-metrics` flags. You can change the backend model by passing `--model=...` as well. - -Run notes: -* Benchmark takes one second per segment for OCR using the default `trocr` model. Please be patient or set a counter to limit the number of files processed. -* Only one segment can be input at a time - - -### Test Data Sets - -You can run the script `pytest run reportvision-dataset-1/medical_report_import.py` to pull in all relevant data. - - ### Development Tools Adding new dependencies @@ -147,8 +108,6 @@ To build the OCR service into an executable artifact poetry run build ``` - - ### Dockerized Development It is also possible to run the project in a collection of docker containers. This is useful for development and testing purposes as it doesn't require any additional dependencies to be installed. @@ -169,6 +128,45 @@ The frontend container will automatically reload when changes are made to the fr The OCR service container will restart automatically when changes are made to the OCR code. To access the API, navigate to http://localhost:8000/ in your browser. +### End to End Benchmarking + +#### Overview +End-to-end benchmarking evaluates OCR accuracy by: + +End-to-end benchmarking scripts can: + +1. Segment and run OCR on a folder of images using given segmentation template and labels file. +2. Compare OCR outputs to ground truth data based on matching file names. +3. Write metrics (confidence, raw distance, Hamming distance, Levenshtein distance) as well as total metrics to a CSV file. + + +To run benchmarking: + +1. Locate file `benchmark_main.py` +2. Ensure all the paths/folders exist by downloading from [Google Drive for all segmentation/label files](https://drive.google.com/drive/folders/1WS2FYn0BTxWv0juh7lblzdMaFlI7zbDd?usp=sharing) +3. Ensure `ground_truth` folder and files exist +4. Ensure `labels.json` is in the correct format (see `tax_form_segmented_labels.json` as an example) +5. When running make sure to pass arguments in this order: + +* `/path/to/image/folder` (path to the original image files which we need to run OCR on) +* `/path/to/segmentation_template.png` (single file) +* `/path/to/labels.json` (single file) +* `/path/to/output/folder` (path to folder where the output would be. This should exist but can be empty) +* `/path/to/ground/truth_folder` (path to folder for metrics that we would compare against) +* `/path/to/csv_out_folder` (path to folder where all metrics would be. This should exist but can be empty) + +By default, segmentation, OCR, and metrics computation are all run together. To disable one or the other, pass the `--no-ocr` or `--no-metrics` flags. You can change the backend model by passing `--model=...` as well. + +Run notes: +* Benchmark takes one second per segment for OCR using the default `trocr` model. Please be patient or set a counter to limit the number of files processed. +* Only one segment can be input at a time + + +### Test Data Sets + +You can run the script `pytest run reportvision-dataset-1/medical_report_import.py` to pull in all relevant data. + + ## Project Architecture The OCR Layer is organized as follows: @@ -199,3 +197,40 @@ The OCR Layer is organized as follows: - **`poetry.lock`**: Lock file generated by Poetry to ensure dependency consistency. +## API Endpoints + +The OCR service exposes the following API endpoints: + +#### Health Check +- **`GET /`** + - **Description**: Returns the status of the OCR service. + - **Response**: Status message indicating the service's health. + +#### Image Alignment +- **`POST /image_alignment/`** + - **Description**: Aligns a source image with a segmentation template. + - **Request Body**: + - `source_image` (Base64-encoded string): The source image to align. + - `segmentation_template` (Base64-encoded string): The segmentation template to align with. + - **Response**: + - Base64-encoded string of the aligned image. + +#### Image File to Text +- **`POST /image_file_to_text/`** + - **Description**: Processes an image file and a segmentation template to extract text based on labeled regions. + - **Request Body**: + - `source_image` (file): The uploaded source image file. + - `segmentation_template` (file): The uploaded segmentation template file. + - `labels` (JSON string): Defines labeled regions in the segmentation template. + - **Response**: + - JSON object containing text extracted from labeled regions. + +#### Image to Text +- **`POST /image_to_text`** + - **Description**: Processes Base64-encoded images and extracts text from labeled regions. + - **Request Body**: + - `source_image` (Base64-encoded string): The source image. + - `segmentation_template` (Base64-encoded string): The segmentation template. + - `labels` (JSON string): Defines labeled regions in the segmentation template. + - **Response**: + - JSON object containing text extracted from labeled regions. diff --git a/backend/README.md b/backend/README.md index 50e9a732..e3f4f68d 100644 --- a/backend/README.md +++ b/backend/README.md @@ -1,6 +1,6 @@ # Backend Middleware - Spring Boot Application -This document provides a guide for the **Backend Middleware** of the ReportVision project. This middleware bridges the **frontend React app** with the **OCR backend** +This document provides a guide for the **Backend Middleware** of the ReportVision project. This middleware bridges the **frontend app** with the **OCR backend** --- @@ -18,7 +18,7 @@ This document provides a guide for the **Backend Middleware** of the ReportVisio The backend of ReportVision is a **Spring Boot** application designed to: - Serve as middleware connecting the frontend with OCR. -- Manage template storage +- Manage storage of template in the DB - Act as a middle layer to pass data for OCR extraction @@ -57,7 +57,7 @@ docker exec -it /bin/bash ## Project Architecture -The backend is organized into the following key directories and files: +The backend is organized into the following directories and files: - **`src/main/java/gov/cdc/reportvision/`**: - **`controllers/`**: handle API requests from the frontend. @@ -65,7 +65,7 @@ The backend is organized into the following key directories and files: - **`models/`**: Data models representing application entities - **`repositories/`**: Interfaces for database operations, - **`config/`**: Configuration files for security, database connections, and CORS policies. - - **`utils/`**: Utility classes for tasks like validation, logging, and file manipulation. + - **`utils/`**: Utility classes for validation, logging, and file manipulation. - **`src/test/`**: Includes unit and integration tests for the backend. - **`Dockerfile`**: Docker configuration file for containerizing the application. @@ -104,7 +104,7 @@ The backend middleware exposes the following RESTful API endpoints: #### Health Check - **`GET /api/health`** - **Description**: Returns the status of the backend server. - - **Response**: A status message indicating the server's health. + - **Response**: Status message indicating the server's health. #### Template Management - **`POST /api/templates`** diff --git a/frontend/README.md b/frontend/README.md index 8dff74e0..0f7e70c3 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -8,10 +8,9 @@ Welcome to the **Frontend React App** for the ReportVision project. This guide p ## Table of Contents 1. [Introduction](#introduction) 2. [Setup and Installation](#setup-and-installation) -3. [Development Workflow](#development-workflow) -4. [Testing and E2E Commands](#testing-and-e2e-commands) -5. [Frontend Architecture](#project-architecture) -8. [Troubleshooting](#troubleshooting) +3. [Testing](#testing) +4. [Frontend Architecture](#project-architecture) +5. [Troubleshooting](#troubleshooting) @@ -33,7 +32,6 @@ Make sure you have the following installed on your machine: ```shell git clone https://github.com/CDCgov/ReportVision.git cd ReportVision/frontend - 2. Install Dependencies: ```shell @@ -52,7 +50,7 @@ npm run dev npm run tests ``` -### Testing and E2E Commands +### Testing Runs the end-to-end tests. @@ -67,7 +65,7 @@ Starts the interactive UI mode. npx playwright test --ui ``` -Runs the tests only on Desktop Chrome. +Runs the tests only on Chrome. ```shell npx playwright test --project=chromium @@ -93,7 +91,7 @@ npx playwright codegen #### Fast Refresh -Currently, two official plugins are available: +Currently, two plugins are available: - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh @@ -110,7 +108,7 @@ Currently, two official plugins are available: ### Description of Key Directories and Files in the frontend: - **`public/`**: Holds public static files like images, logos, and `index.html`. These files are directly served by the development and production servers. -- **`src/`**: Contains the core application code, including React components, pages, styles, and utilities. +- **`src/`**: Contains the application code, including React components, pages, styles, and utilities. - **`components/`**: Houses UI components. - **`pages/`**: Organizes page-level components corresponding to application routes. - **`styles/`**: Includes global and component-specific styles. diff --git a/user_guide.md b/user_guide.md index ab16fcb1..79b537e1 100644 --- a/user_guide.md +++ b/user_guide.md @@ -9,13 +9,15 @@ ReportVision is a tool that automates the reading and extracting of labs from PD 2. Extract Data based on selected annotations 3. Conversion of Extracted Data to PDF's +Please see "how to" instructions in order to understand features of the Application in more detail. + ### Getting Started #### Prerequisites -1. [Python3.8](https://www.python.org/downloads/) -2. [Node23.1](https://nodejs.org/en/download) -3. [Tesseract5.5](https://formulae.brew.sh/formula/tesseract) (brew install tesseract) +1. [Python 3.8](https://www.python.org/downloads/) +2. [Node 23.1](https://nodejs.org/en/download) +3. [Tesseract 5.5](https://formulae.brew.sh/formula/tesseract) (brew install tesseract) 4. [Java21](https://www.oracle.com/java/technologies/downloads/) 5. [PostgreSQL](https://www.postgresql.org/) 6. [Docker](https://www.docker.com/) (required for DB and middleware set up) @@ -30,21 +32,31 @@ ReportVision is a tool that automates the reading and extracting of labs from PD ![](arcdiagram.png) -A React-based Single Page Application: This serves as the front-end user interface for the application. +The **ReportVision** application is composed of the following core components: + +## Components + +### 1. **React-Based Single Page Application (SPA)** +- **Purpose**: Serves as the user interface for the application. + +### 2. **ReportVision Middleware** +- **Purpose**: Acts as middleware to handle communication between the UI, OCR API, and data storage. + +### 3. **OCR API** +- **Purpose**: Performs Optical Character Recognition (OCR) on provided input. + +### 4. **Data Storage (PostgreSQL)** +- **Purpose**: Stores saved templates and extracted data. -ReportVision Middleware: Acts as middleware to handle communication between the UI, OCR API, and data storage. -Responsible for coordinating requests, processing logic, and integrating with other components. -OCR API: Runs the Optical Character Recognition (OCR) process. -Receives data from the backend, performs OCR on the provided input, and returns the extracted information to the backend. +## Infrastructure and Cloud Components -Data Storage (Postgres):A managed database for data persistence. -Stores data processed by the backend and results generated by the OCR API. -Handles both structured and unstructured data related to the application. +### Hosting +- The application is hosted in **Azure** -### Infrastructure aod Cloud Components +### Infrastructure Guide +- For detailed information on how the application is deployed and managed in Azure, refer to our [Infrastructure Guide](./infrastructure/README.md). -The application is hosted in Azure. Please see our infrastructure guide here to learn more From 4c402f0bfba67aa0858d8bfc1b33ac1b22affc54 Mon Sep 17 00:00:00 2001 From: Arindam Kulshi Date: Thu, 9 Jan 2025 12:01:28 -0800 Subject: [PATCH 5/6] formatting edits --- OCR/ocr/services/phdc_converter/builder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OCR/ocr/services/phdc_converter/builder.py b/OCR/ocr/services/phdc_converter/builder.py index 8c8f50b6..c2aeba85 100644 --- a/OCR/ocr/services/phdc_converter/builder.py +++ b/OCR/ocr/services/phdc_converter/builder.py @@ -644,7 +644,7 @@ def _build_patient(self, patient: Patient) -> ET.Element: ) patient_data.append(v) else: - logging.warning(f"Race code {patient.race_code} not found in " "the OMB classification.") + logging.warning(f"Race code {patient.race_code} not found in the OMB classification.") if patient.ethnic_group_code is not None: if patient.ethnic_group_code in ethnicity_code_and_mapping: @@ -658,7 +658,7 @@ def _build_patient(self, patient: Patient) -> ET.Element: ) patient_data.append(v) else: - logging.warning(f"Ethnic group code {patient.ethnic_group_code} not " "found in OMB classification.") + logging.warning(f"Ethnic group code {patient.ethnic_group_code} not found in OMB classification.") return patient_data From edf2e49a07c1d73675d3cda4a02ccbc80ba65d15 Mon Sep 17 00:00:00 2001 From: Arindam Kulshi Date: Thu, 9 Jan 2025 12:06:16 -0800 Subject: [PATCH 6/6] edited azure link --- user_guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide.md b/user_guide.md index 79b537e1..86e88be4 100644 --- a/user_guide.md +++ b/user_guide.md @@ -55,7 +55,7 @@ The **ReportVision** application is composed of the following core components: - The application is hosted in **Azure** ### Infrastructure Guide -- For detailed information on how the application is deployed and managed in Azure, refer to our [Infrastructure Guide](./infrastructure/README.md). +- For detailed information on how the application is deployed and managed in Azure, refer to our [Infrastructure Guide](.github/README.md).