From 8acfb436f3f1b1faa396fd76e7c9c943924c4ac9 Mon Sep 17 00:00:00 2001 From: Joshua Salako Date: Thu, 1 May 2025 22:57:55 +0100 Subject: [PATCH] Enhance API with data loading functionality and update README. - Added `/load-data` endpoint to load transaction data from either a database or a CSV file. - Updated `SalaryAnalyticsPipeline` and `DataLoader` to support loading from CSV. - Implemented data validation and error handling for loading processes. - Revised README to include new data loading instructions and workflow steps. - Added checks to ensure data is loaded before running analysis endpoints. --- .gitignore | 6 ++ README.md | 32 +++++-- .../__pycache__/api.cpython-311.pyc | Bin 13755 -> 17046 bytes ...consistent_amount_analyzer.cpython-311.pyc | Bin 3644 -> 3697 bytes .../__pycache__/data_loader.cpython-311.pyc | Bin 7220 -> 10181 bytes .../__pycache__/main.cpython-311.pyc | Bin 8617 -> 9871 bytes .../salary_earner_analyzer.cpython-311.pyc | Bin 8261 -> 8276 bytes salary_analytics/api.py | 82 ++++++++++++++++-- .../consistent_amount_analyzer.py | 14 ++- salary_analytics/data_loader.py | 59 ++++++++++++- salary_analytics/main.py | 34 ++++++-- salary_analytics/salary_earner_analyzer.py | 7 +- 12 files changed, 205 insertions(+), 29 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..18dd2cb --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +transaction.csv +output/csv/final_table.csv +output/csv/high_earner_details.csv +output/csv/likely_salary_earner.csv +output/plots/consistent_earners_predictions.png +output/plots/hypothesis_overlap.png diff --git a/README.md b/README.md index e4bbe3a..65fbd4c 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,6 @@ salary_analytics/ └── api.py # FastAPI endpoints ``` - ## Configuration The system can be configured through environment variables or the `config.py` file: @@ -89,12 +88,26 @@ uvicorn salary_analytics.api:app --reload - `GET /`: Welcome message - `GET /health`: Health check -2. **Analysis Endpoints** +2. **Data Loading** + - `POST /load-data`: Load transaction data + - Parameters: + - `source`: Data source ('db' or 'csv') + - `file`: CSV file (required if source is 'csv') + - Example: + ```bash + # Load from database + curl -X POST "http://localhost:8000/load-data?source=db" + + # Load from CSV + curl -X POST "http://localhost:8000/load-data?source=csv" -F "file=@path/to/your/file.csv" + ``` + +3. **Analysis Endpoints** - `POST /analyze/keyword`: Run keyword analysis - `POST /analyze/consistent-amount`: Run consistent amount analysis - `POST /analyze/transaction-type`: Run transaction type analysis -3. **Report Generation** +4. **Report Generation** - `POST /generate/reports`: Generate all reports - `GET /download/{report_type}`: Download specific reports - Available types: @@ -105,12 +118,21 @@ uvicorn salary_analytics.api:app --reload - `inconsistent_plot`: Inconsistent earners plot - `hypothesis_plot`: Hypothesis overlap plot -4. **Model Training** +5. **Model Training** - `POST /train/models`: Train prediction models -5. **Pipeline** +6. **Pipeline** - `POST /run/pipeline`: Run complete pipeline +### Workflow + +1. Start the API server +2. Load data using the `/load-data` endpoint +3. Run any of the analysis endpoints +4. Generate and download reports as needed + +Note: All analysis endpoints require data to be loaded first. If you try to run any analysis without loading data, you'll receive a 400 error with a message to load data first. + ## Docker Deployment 1. Build the Docker image: diff --git a/salary_analytics/__pycache__/api.cpython-311.pyc b/salary_analytics/__pycache__/api.cpython-311.pyc index 8baa4c248879272f50fa658d510a0f2656f8ddb7..939b1f5484e841baf58e16cc5c546cab4e347314 100644 GIT binary patch literal 17046 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 delta 4235 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 JiuiyQ005pIcjN#7 delta 440 zcmew;vqy$+IWI340}$kG<P()@Y00U{Uc>@a zSHwB_DW`(mEtcf`g32P0TQr%9xIq#!K%$rlNF*do*5=BsF9R}v{Agf!z%71(TmG)J z`U<`)(&imL4}`>~s883Qq`yG>qLAJdA-(HDCYOXvZt!?tKtdmxnS{B%FaU|?T%ujt zGaMJVbZB4X(!9c@`GJ*zledTc0|$eo>>T#_JhOOa^3CDvVCmt#!7toX-oaPI2ef*! z7WZ#PqseMKX+W}R2inb4{v=5QyD6J;(VcI>< z7($j_-}ig>-gC~q=iYnH@A=bL{xW9$t;J$OwEX1n;`}G>8&)T?cBAFx6O7D7DyJ%C zy=;gWqoyfS)I4Qo5Q~(G1s?RFD3&`6LLbqmRHm#quDh738+Wdn*qx!L-K3jo7L9Vn zRc5L}uDpuqq=o?UnMjcNH&bcU66Zk$0ElPlalOJi9#&B=Q5d#mdpefQGA@6Izdp>6b@wU(Gufxq}={^NS5SKA|%b?cvK2WgMl}t z*>FVZNY#TZ9GiPaTo(37DOhJ<_O;DHvzwld-qda7rnVVbQ~}@=-D; zcD;F5hr6ho1y=5&&oTOR-(?CFGx10=8dJ~HHXojg#jz3$$7JR5wk?pHnNd`AHW`U5 zOA+deB1M~i6ll!}{du83EA($U>edrQ z1F}2U&OUP0-LA__KJb42Qr0z`a}DQR!$oAZG--wy{IY-(Yo^hM=*74gXvtlc)TlELu^Z&xX^bL-0 z0M=hCT2ZxQ!*bJdOUVh|yx`4h^;-k^&~cz)d^NPP%2NIo_Y={aM~m z{^|J4dHRpygNZ-bKeuOHr)kac19^TR%MXx`o$f}s;UAxNz0RUPH=Om-*F)Ac#BdM! z!>{*p4}0}P%>BQbv4J^BUae`eXn6t|GaXVDa-q6@5gI@&2`eOo*tcnk5jooI79#GzbI^=G$S)_X9A69r4=v3ru`+S2?y}KnN*Ib! zdI`&t8c*UGh4Q4MLprx(-6Bu7z+x?Q_7*ufat@Uk*j=zcCFGX)oY|e|#S-yG8 zBCaQ|1=oUGV*UP{AIk~jd0{*&jBB~BnKpeXHf6#&u`@4rX8F#qDWmwW`LnK(56@C4 z)pDBgermGbjYaALvAF)j{D$b=ohBcv9q6dfNM3QjB)}F++YUiXCQ-M`0IaDd$#wUU z?iRhXh<4!(rWg9VkY>!#-w$r`g}adv$YXb_1F{7}z!fSY@wqul>i7)pk>)y2mjW`4 z(BVG@o(BMDwNu@$?2X3dWJKx1aXJS7+SEUh#kva>#w*&7d{NhaYKmnXMf75PqK$v^ J9a3$j{{ehJq+kF5 delta 606 zcmZ`$O=uHA6rS0-oBuRPE2h-MC3UxrAh9Y%Ye2+;DAg)UFHr~~Gf7wXC*9dqvv}~( ziw6(k-Mv{4LJ=cgyeb~_=4}u>dFsK6f)^2I6Y-=Ud>@bR&HIMOd$ac9RQ5?GlSBfq z-c4@2&JVIhe19;%bWtXX6}J_8F_I9C-AqOABGB|UM(C;F7>$gCE+7)yLL|N| ziH_i(PIj=fCFaNYrb8X;vTUHL0s;PlvqLI9R`Ap?mG~eH&+YFixCYN>HTXQc3!id_ zK*}!~)qPwdi-N}mPY9l@VjgR|R$x0!lxef;xE^hq4xx9$X(MR2DPx_$vihp!wg@F^ zm{C3V4pZHZN?Lv^ga`TZQaF8noS~Y|$Y#`ER>Sm)c%l0J9%akPFn8Hx%yhcy?Uofp z4rrxI6)yD%k9)L5nrzeJGPQetpQn7cDdMSb+7wm_71=v2_J&Sj0p6Almbud22%KAt zD{iMF{^IgxK)pVfM>hN{&A_uc71xIK@}mj3e)tEr!O)IPT&c$3p;l8?MVPk+@3k2v zBT9A+cD19qx?J2tf8`#ARlR~I!POTEXQBaF2#;BAw=1@Q9le~p8rkJYqQiRzeAJ7{ jhA5*mv6t{mUxyp>N6r$A5A30T>(`P0{XboAN+kUTUX!Z2 diff --git a/salary_analytics/__pycache__/main.cpython-311.pyc b/salary_analytics/__pycache__/main.cpython-311.pyc index 75230c812f22a9bec5b6d7b121567b89b21fd666..4f1a3ec44893c33145e870944df2c8c6a9088771 100644 GIT binary patch delta 2379 zcmbtVU2qds6ux&io8L{+{Ip3nX_As)o3@l+tza$k*A}E&DRykDQ(|_3V4Bq3lu}Fq zM`mapM$`*~j534B;8ak{)Ca~VAH?wihNP3Bo%MfwaeS(S4~{c3p1Wy~B0hQN?sw0* zzvtU~&OMWxPITHnv|7ywO8QsV-U;D~?F#N@s1D`!Mu5{uLVZ|7eT;~YqCQr%h`OV= zPbYGqaiShjFB$+1q7l$2ngC5O%M7#3BHN8(yy_vJ>OIp-83p4QIm3P%P#N*XrxL7o zObPVRQ2q8!a$NT+pTjxkGIJ43xx^3`w*~7+FBiicd5vo(H@z-$gR>aeVGP?F$Pe7M z%1K7mE16+AA*rU_$&@rO6wmG@F?}W+W92H?SgxiJ1W@QX;c!^?(4G+aNZ)`@kX!l{ z)~U8Hl6hxi!P!-Gb`jptgMDO|*@wrchpfwR#Td?^eXyof_L)eZJE+TH5K=BL+2=Cz zqOJu7bJQ%3d}7>RqdJnxfD%u|<5q?M$SmD1UP_+Vx@on1_k@?1))_?`82REz57g~JB6}W>;c>JZLvR~& zcr&^PQ!ka*p)n@M$ScU3hKQcaG4GgX`-k<%aWVs)uh4aAkVvaJVC-1pLGHa z(1_N`1ZlA?*Fi`a$yP_?F#VAblTm2oWfWEeEV5Gv9WMB8uK+lOPoq63hhA_(_~8lW zo&dbdZ9o3BB8T5%j=Q?iBy}ztV5%XJNevICl^ClsO)FFuRER8i1{NNGBiaM-r@18s z&YQ>#4z(WK1`m~&_?^MSO)aTRBA!xOK`0OR!LCXzz#m6YUg#~=bE~f=zII>tTo2`2 zpM(0&>QW1+-M~SiGpRsW?+MG&%fm@o62l92tqULYHHCK!r^E47DjZM8Q=^Kcgm))p zC5xNLkfT{&1BXHEeC&v>S$M|Oit;uPb{aj^K3F=S+@aCir9BqkJNnH#sMR;9)g)fk zum}7n(2w)}K3a|5xiSyRrhXk@LFg?Cz2o*DEy6rvOuO-Syz|8N*SF_=?Nb>5s0F^G z$am!Vj_;g-e6@IWIY6lvoKn##<#~w=Iv>Nn=}BiR15d>-?vYT53ngTuV4_e-!A)T! zIaIj?SCKC(AH^Q>S7pCmgl^?qE&5MYO$Ib?rrujF1`srFeuyQHGmnzDJ>JD>W6HCP zJn3>UqK?daYRTuEi`09!LiTWzW8MyMSU33@+GEhJ_jQn;!GZIxI@0Bh#LO4Lw=UiF zFN{P>H;SOFvwS#0Q6xV+#u8qL*!e8wu~2d>X%&|Gtz+yVRs^pXHE9HopaX0#8o@Ce zIV3#I$pM%@eM4A+p9z5^Yk#MKN-HTeQ=s`*eiA@6Z8?yThO)^_T4j~29Mj#G(o`%d zG-@EL(HyZRut7fps;kTu)>%PhHzPFY8R-cBi>TBt<94xe&{B!i6`k%^1US6R8h zJh}@(D;J{b0)XnH>H-8SxYl`e7oub?7&FWxKj(vooDE&ZU1Ta$RY5bjsvC?a({c}p zC&{f)6Xa|zoNRgq^iq!1ETY5rnfj-(WiTTSr=(W7pS%~oxN;MN;rIvRZ(9HF_l|DU L5v698j#B>{Br#R_ delta 1281 zcmZ{kUue@;6vyxV{mUso0b*#3e8i##_=kRxJL?Pk{V=EFz7WhDH;M&GDM_o zNJzzykv2m?s)h||H&mp2*+Thn0#q zRrRJIYx|f6;gqwLcEeKOIrO_el`x0|!}ibJNxRv^{WOjU)a)N=yJEtm<0QQRFFgJ5 zo1@RqnyW0a!3NjaV1=ctEWM_t%{_y^cJUDcGGM3<-`juXSO z%Ct-t#R*cTNezxiPpv|o1=$g08z7N7pDvJ3&pwFnpj#elY$xG z+q6&;@Hd8C4NBXPGL4JU8OXOql?sbiS#))=ZG`Hs49U95W4B{O6CdjbMpW@9RYn_L zw08}+qK|2%@Gf=p7=E%$OkU*r^Ko*EgW)XQg|T?|u-`5p-y#j*@+9T!25~2A?KJ3D z<@6S5zz09X@n%8zJ^p2cD&x2H1k<;*394+SK!A)3ZLyCiDHWX z<`30IRMIf!WSd#M# zDsOQYr52}_#AoKEq*fFu0mU_0iv&P=(m`r?GBVRM;!_ig@=}Y6i}*o8T*>*Rc_r~6 ziOCB@A{kRA{}Peozb;;INxa~qc+nN{q7I%LlPyJ+`0k2I%#gV#s&++Gt%K{q<`~ff ztc(SdWu?6tOD1Om$(YSsr57==6@YB3nH(!uz|Y32|A7Hc2u!{sH<7Vna-O^dWBcTG a`FeX-CPuCg4A{w!VDT?FWQ&A>wg3SB$6r+d delta 371 zcmccOaMXc!IWI340}ve9#-FieBd@VAz_1#KA)uBSsEQ?pdGab@Q&yH5mKx^COd?@noB(C}A{jF$^NGqScJSQLGP_{zaYf6sgXe;%+C>iayP^^^WG;%TT@h95;Ce9GT2zVg z#^!9%1FVeslXaxM8H*=Z0Lj?RN2C`qvE_rTte%`BSHRECs{erjP6$nYEjN*|esZn6 f1Y_IeY4Y{= MODEL_CONFIG['high_earner_threshold']] - high_earners['least_inflow_6m'] = high_earners['least_inflow_6m'] - count_high = len(high_earners) - + high_earners = final_df[final_df['estimated_next_amount'] >= MODEL_CONFIG['high_earner_threshold']].copy() high_earner_details = high_earners[['accountid', 'least_inflow_6m']].reset_index(drop=True) + count_high = len(high_earners) + return high_earner_details, count_high def generate_reports(self):