feat(calibration): finalize Step 2 Risk & Stops with inline PDF reports and visual validation
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -21,3 +21,9 @@ logs/
|
||||
# Resultados
|
||||
backtest_results/
|
||||
output/
|
||||
|
||||
# Ignorar SOLO la reports de la raíz
|
||||
/reports/
|
||||
|
||||
# Pero permitir reports dentro de src
|
||||
!src/calibration/reports
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,93 +0,0 @@
|
||||
%PDF-1.4
|
||||
%“Œ‹ž ReportLab Generated PDF document (opensource)
|
||||
1 0 obj
|
||||
<<
|
||||
/F1 2 0 R /F2 3 0 R
|
||||
>>
|
||||
endobj
|
||||
2 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
3 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
4 0 obj
|
||||
<<
|
||||
/Contents 9 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 8 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
5 0 obj
|
||||
<<
|
||||
/Contents 10 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 8 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
6 0 obj
|
||||
<<
|
||||
/PageMode /UseNone /Pages 8 0 R /Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
7 0 obj
|
||||
<<
|
||||
/Author (\(anonymous\)) /CreationDate (D:20260212210317+01'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260212210317+01'00') /Producer (ReportLab PDF Library - \(opensource\))
|
||||
/Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False
|
||||
>>
|
||||
endobj
|
||||
8 0 obj
|
||||
<<
|
||||
/Count 2 /Kids [ 4 0 R 5 0 R ] /Type /Pages
|
||||
>>
|
||||
endobj
|
||||
9 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1380
|
||||
>>
|
||||
stream
|
||||
GatU3gJ[&k&:Ml+oF"lkWPV33AD+U)@:uT`QWc#5H(p0/M#n`]37uM1m+6/K<fCO9JO#)pBsh-bh5=I/$3WTCISZ:1-SZThV*Gp:F.h=K&H;i^`hUS:RgI>-j98;=;X-qp`]R!B"VuDJ%lgR?@2ropQE9N4,V@rC"VN.sOkaQK8CA5Xl?/OUq74)c5X.R4'B7?XoEB./@N2,-?5Q-T_CX*]$9C+-aiG-X%.Rsm"3(b`UTY*)"\e81]7Y+Tp/crS""VEY8cA^>TI=+/><ms*p+<p(5S6urRo?t[I1'Z]-(&ral[*)hDr4t#9s6CJeMC%@0egIr6c'=QP*'86+&`L'j2m>1RThS_$WRc6K;a$O$N8$tWmIK7*_b!^Tmml,>6hIi+[UL+O.HcIW9s%B!KeO\jqA?/E1,Y"6NW%!+O`%E]S!]X[U!>H^o?GTear>i?.:"KP\=0uS]$_emoQE,]4(ca0:A@^ofQ/Gh/1MQo(t%<_TmY!:=IIf!<'E!MW\kuV)BXpkE24%EU@-.s)[6-6DK&to6L-@Vr2:TaeeYV%Mj"pA]0GHNhiOM:;0op#>U#+XUHFRb&C2"W*;r&(14GqL!VHL$;'u&.^Mg>-[2%8kPP$(K^@eN%n-_\+o[=sj<#-O.%]$RN,b;daLWe[88;qi>%^Z>J^n^&5.LSKaDBjm!E,PhhlolI`7@FhpXBZWXLkm-cD7)r`qh5b:UB\-ctKtAI>$jJ3@<`nM!:0sSa!P)D-?IZp,dmYm,pY^qmjHpqgGY1bC8BC2qspLq.A#Y[dadp9`mV;p.YXU"fg592$B]^?Ltj:Gjt1ZHaDXQ7(QMtWRP(t)G);mM526%qp.sXXpQr6#M:`1eI6Is6`0O>#"ai,jViX(/i5[:`B=q0)DL*L0@%;qA=V!>AK:I&<#7#e@)k%5f@,Mf(BWj!)Pcu>aU=Nt!e]9Q\dfl-+6X93Zo#+N4)0qWZsGJsVT/O+m7c\'2RT)%Yq5Pp9K1`$-&_0q5ZCC"P+f.i;`R:In@2?8?5l0]NV"m3fJJZ.[_p*uhl'm!<]($-XltC>Wq78tG.V"]H8flUI2b0]HgK%@]MPZ.L..OpjN%mtV^mK#kWc?Cj?K3kbY`^H35`"H3P!P?\J:B;9(P,@hH/^2D5iE6@#:p`l6WUAY3jU6^u6YGaV;-WQsU3Fdg97D.#g9+/i;5Tjrup7Xb5(S*DA*G319IU.a$ML16f@B<F/F3n>8g3T(R_a/!Wcs1L4P5FKgnGPCM'9*EF&)NY/iD@s=A8e`%%OV!lFh@%`uOO,V>]A>\824pE&=3t2O001(#1mEG4.f4:k@0p;halQ/cj)Vq/#F`ri-jK!D]JW-[u5Xs<u+<u5GpF,qUqH]%foKT.N!(6~>endstream
|
||||
endobj
|
||||
10 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 522
|
||||
>>
|
||||
stream
|
||||
GatUpa\K`-&;KY%MRaiEKT,<:9.KVOFooUFA#7>T:1ZuVi.UdfEC.U@3^l>sEgX,os(6_,#[m[pp:j]3*2[[o8q9`/1E\E0+[(IAFS%9P19sIsLj*h\-!c@p;A9d1J4^Np4SD:r0M\0BRUQGU[Y>[]!fg5tDOmee/`9g!eogLU[F.<a+h`MMY:4n]7DESiO9A.C8<<!K\4sK`G5r%k,C+6#4aG4()p0m*E,DV%\1qnRM::a=BqL;*>//Utpf.-]@VdP#r;cA?`%;735q["cg:f*/VI;A@9<Id</kgi_7n!(A0G<H'P(>aYl35<P9N.V]%-(ZWL]p@`1m$cDbOhMe:\p"401-mS&-[uAUU]I79E?AmGBtu_'Z/cP8BBiY[V`b)!l7TJ9/s4GAlauK><:?C13qCTb/;g#_:I/?V4N4;?*A;$.6cC`aq%pP3ONPsT>6.4F>$#6-C/K*/XmW*ht'`-A![jI;P-(8_2Z=Md#HJpY93AiPe#1Z!YO^G]1dV0+u"<JK'?CC~>endstream
|
||||
endobj
|
||||
xref
|
||||
0 11
|
||||
0000000000 65535 f
|
||||
0000000061 00000 n
|
||||
0000000102 00000 n
|
||||
0000000209 00000 n
|
||||
0000000321 00000 n
|
||||
0000000524 00000 n
|
||||
0000000728 00000 n
|
||||
0000000796 00000 n
|
||||
0000001076 00000 n
|
||||
0000001141 00000 n
|
||||
0000002612 00000 n
|
||||
trailer
|
||||
<<
|
||||
/ID
|
||||
[<b164637c0884769535c99f7ca3ce39d9><b164637c0884769535c99f7ca3ce39d9>]
|
||||
% ReportLab generated PDF document -- digest (opensource)
|
||||
|
||||
/Info 7 0 R
|
||||
/Root 6 0 R
|
||||
/Size 11
|
||||
>>
|
||||
startxref
|
||||
3225
|
||||
%%EOF
|
||||
File diff suppressed because one or more lines are too long
@@ -1,93 +0,0 @@
|
||||
%PDF-1.4
|
||||
%“Œ‹ž ReportLab Generated PDF document (opensource)
|
||||
1 0 obj
|
||||
<<
|
||||
/F1 2 0 R /F2 3 0 R
|
||||
>>
|
||||
endobj
|
||||
2 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
3 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
4 0 obj
|
||||
<<
|
||||
/Contents 9 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 8 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
5 0 obj
|
||||
<<
|
||||
/Contents 10 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 8 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
6 0 obj
|
||||
<<
|
||||
/PageMode /UseNone /Pages 8 0 R /Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
7 0 obj
|
||||
<<
|
||||
/Author (\(anonymous\)) /CreationDate (D:20260212210640+01'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260212210640+01'00') /Producer (ReportLab PDF Library - \(opensource\))
|
||||
/Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False
|
||||
>>
|
||||
endobj
|
||||
8 0 obj
|
||||
<<
|
||||
/Count 2 /Kids [ 4 0 R 5 0 R ] /Type /Pages
|
||||
>>
|
||||
endobj
|
||||
9 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1380
|
||||
>>
|
||||
stream
|
||||
GatU3gJ[&k&:Ml+oF"lkWPV33j9olnfU8bqV3[$PSuAB:UO\+F%^n/2p?T16.un+eJO#)pBsh-bh5=I/$3WTCI)cVFZNs=?;P4Gm),Ta?M#MriUXA\Sc3/G;F?D'D.CCK^@h,D#"Vs'MpGf0(?lW`uROfQ=/1oeGGs4p_4LrdKOebX[<i\'`nk8eOJ:<+6-UaT7kptP2B,c6UGCgqG&8$cR<m\D6aU]C(q>pSSVSJS=GOh""21UPZA.LB^gA[sG.q%K63m"#P%htk@Hjf._cb=t^"=V+o?uq4WZ#@JF^BE"=@=1k0n')f1^T"%rZt]rR3!Dd=G>-XD:,6i^]MhS`HSjDs>'"KAG+nInG7YKB;5#)B5nt59e:C3&/Ca-_Dem[;GDP`/cUCWU9[rc[6BCmBLZT6^/TI3:^]OYYL`fVMqn.]nl*c"LHGHN=im#[+5+U:Jf9BrFWLZJAPOCHn;7iP-bUo6R/CZ57$e9+@C@]Ct;o6b.M;r]k/3lA%PN&Fn>cCF(r]-dOVHs'8rr3ruK$_l>d!T/L_^X5S4h38\`5*F2N/UjNfosobb/m`N>gLQ3Ya&XM8f8+2)7>CeDXg^7>mTh,Wpo8(9P&sXZfGP#k'S7XL%'Hbaog%B@YfLX/GY&)"cAXH%qD]</@$nN=H]*k[8IkV=C;m!NfS'B%5OD4@XurEni*Yu4>HL@fc?QuJ9AI>e!]hA?!Eq5E\gEKK1=$!lm#3uR:RQPL6u;He<sWIf9S;_B>6=V_LCoqZCeaaZJTfIZg7o<HIM8LQ`oScAK/tbhDB2hnBa"r=WiWF?:-uXi&0D-a+JmA5*uMW,LXhg8u9gbejrtQ9-i>dh1'ann#@KtI=MX!>YZHo2'f>k1e;X$/6;Q+I]d(B'TZ'Q._pSX^,Wa!PAr3:P]>K0F&g:lNZH=(;8l=V0*K^!2+Hn[P7oXB$Oh(9Bfh!0I%WjrI7#dm\YCL.9'[4ECMM(qe1O^l@oM,b'$F1V_fWAI?S_(f!(=5*0,Y[T>Es9:$I<LemBmPEcq++uo6'Y,CccI?p7;,8@?j1km>=)`V+T&WA@B\CZ?IOogUE;F0ur]=aU>Y<(!P/!.7]-64C(SO8,nF2-i^14DMHl__&89Xmc6jLqGpRI.F%<IH!cC9\"tQ"K-dZuWL(-K]!aut",EMQ,u`;O@;[OD:Fg7\UGV>M\PVbARu;70[1:KkFO7RWj!=%E<0Xk!ALOsOWk=kuhO>u0HIha!X].3p03"&[nR*Su-i^ce40142&gMK[`pP[?jnoS$V!Ig7d6KX9)/>Y1A?+M55'd\L3XlF301:/3mEF(Sf4:k@1%UGieii:X27c:0lKfS8a]Ie%!bkdnJ[DMH5[*m,mSUJUqH](goKSS3!(-~>endstream
|
||||
endobj
|
||||
10 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 522
|
||||
>>
|
||||
stream
|
||||
GatUpa\K`-&;KY%MRaiEKT,<:9.KVOFooUFA#7>T:1ZuVi.UdfEC.U@3^l>sEgX,os(6_,#[m[pp:j]3*2[[o8q9`/1E\E0+[(IAFS%9P19sIsLj*h\-!c@p;A9d1J4^Np4SD:r0M\0BRUQGU[Y>[]!fg5tDOmee/`9g!eogLU[F.<a+h`MMY:4n]7DESiO9A.C8<<!K\4sK`G5r%k,C+6#4aG4()p0m*E,DV%\1qnRM::a=BqL;*>//Utpf.-]@VdP#r;cA?`%;735q["cg:f*/VI;A@9<Id</kgi_7n!(A0G<H'P(>aYl35<P9N.V]%-(ZWL]p@`1m$cDbOhMe:\p"401-mS&-[uAUU]I79E?AmGBtu_'Z/cP8BBiY[V`b)!l7TJ9/s4GAlauK><:?C13qCTb/;g#_:I/?V4N4;?*A;$.6cC`aq%pP3ONPsT>6.4F>$#6-C/K*/XmW*ht'`-A![jI;P-(8_2Z=Md#HJpY93AiPe#1Z!YO^G]1dV0+u"<JK'?CC~>endstream
|
||||
endobj
|
||||
xref
|
||||
0 11
|
||||
0000000000 65535 f
|
||||
0000000061 00000 n
|
||||
0000000102 00000 n
|
||||
0000000209 00000 n
|
||||
0000000321 00000 n
|
||||
0000000524 00000 n
|
||||
0000000728 00000 n
|
||||
0000000796 00000 n
|
||||
0000001076 00000 n
|
||||
0000001141 00000 n
|
||||
0000002612 00000 n
|
||||
trailer
|
||||
<<
|
||||
/ID
|
||||
[<fc42dcfa3952c93b73ce887dcde189a4><fc42dcfa3952c93b73ce887dcde189a4>]
|
||||
% ReportLab generated PDF document -- digest (opensource)
|
||||
|
||||
/Info 7 0 R
|
||||
/Root 6 0 R
|
||||
/Size 11
|
||||
>>
|
||||
startxref
|
||||
3225
|
||||
%%EOF
|
||||
File diff suppressed because one or more lines are too long
@@ -1,74 +0,0 @@
|
||||
%PDF-1.4
|
||||
%“Œ‹ž ReportLab Generated PDF document (opensource)
|
||||
1 0 obj
|
||||
<<
|
||||
/F1 2 0 R /F2 3 0 R
|
||||
>>
|
||||
endobj
|
||||
2 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
3 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
4 0 obj
|
||||
<<
|
||||
/Contents 8 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 7 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
5 0 obj
|
||||
<<
|
||||
/PageMode /UseNone /Pages 7 0 R /Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
6 0 obj
|
||||
<<
|
||||
/Author (\(anonymous\)) /CreationDate (D:20260211220412+01'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260211220412+01'00') /Producer (ReportLab PDF Library - \(opensource\))
|
||||
/Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False
|
||||
>>
|
||||
endobj
|
||||
7 0 obj
|
||||
<<
|
||||
/Count 1 /Kids [ 4 0 R ] /Type /Pages
|
||||
>>
|
||||
endobj
|
||||
8 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 854
|
||||
>>
|
||||
stream
|
||||
GatUr9lo#B&;KZOMS6Aa14pgnP&0=4cmCRIQqi7^;_AOqM,T?R>7BKJG:aa`Ld.aN%p5oVf<9?>#q-@tW;2s]!*:=K>Z7:(_?Ed(E!X4rQZ2RXU5^O5i-CpSqgje?3.W5pT*>0ln,c)_Sr[6r(`9;O>[0(XI[-.bX*B6d1g25e7AQ,`"a;HB$4RM>H8*et#?HoSP4CPZ6rTD<B+VdBGRfpir$^tKY=!k-lieG+ES&XQ'6CYh<\;1QJXA3['o[(\8tfE'6_lAd&LJlY9dgGg]d3N=1<5YBE%0Rte"+uhEAZkl-iIFL*3[=i3Ooab)fa>CV:D('H`m"pMrHH]\l_\KW:b*WOMp`N*8aW=$GuuO/dP/?nV)S6q172Yd\.Rp#nf5"ehE\4e&Q9=8[b-+ft1ODdu4+VkHNc>!cs?*R&"Io7&fHHfQcuMY#N9^J""]ioh'j_>pjsQSZ_[`"CDYDgoAeO`V7Qs&VL@O_X[Tm#,bk8X(5s[M1[+uN/]Y@EaZ.WlH+6iY&%o6f\*Jr[1*AKNKH93\i7I7mr?/aeqL)bhW^C[P,EH9l2+j'_X3&Bqm8`-&:>\#iPQ<4oJZ0SV=BLddD\aGW&^Yo<Ic]r=M_oM_o(.Mf#/.&7/r)"+OA3K*0?!Mg^FCm2mH$r+PB),Ps$ao9q6&)PL^?4Ik^V!K-:_Z0]0LpQs4QI>NXhjD\BLOcUB5XS=PPOJ`IXHjMe`<]Q1^P4g6s6hZIa=:>H#s(5VUoZkSUMkDgAO(P8:::.F/i41Z$V]@[Cr[]qQq[*eR$lW6f+?,t"4^Aua&BGEm;*?]G#4G0Ft6^m&%a/+A^U7X_9/&6m0I=6:LN>miU!!FD`U&~>endstream
|
||||
endobj
|
||||
xref
|
||||
0 9
|
||||
0000000000 65535 f
|
||||
0000000061 00000 n
|
||||
0000000102 00000 n
|
||||
0000000209 00000 n
|
||||
0000000321 00000 n
|
||||
0000000524 00000 n
|
||||
0000000592 00000 n
|
||||
0000000872 00000 n
|
||||
0000000931 00000 n
|
||||
trailer
|
||||
<<
|
||||
/ID
|
||||
[<2ae2cf0ad41711ce62e7e9efe6438c88><2ae2cf0ad41711ce62e7e9efe6438c88>]
|
||||
% ReportLab generated PDF document -- digest (opensource)
|
||||
|
||||
/Info 6 0 R
|
||||
/Root 5 0 R
|
||||
/Size 9
|
||||
>>
|
||||
startxref
|
||||
1875
|
||||
%%EOF
|
||||
@@ -1,74 +0,0 @@
|
||||
%PDF-1.4
|
||||
%“Œ‹ž ReportLab Generated PDF document (opensource)
|
||||
1 0 obj
|
||||
<<
|
||||
/F1 2 0 R /F2 3 0 R
|
||||
>>
|
||||
endobj
|
||||
2 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
3 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
4 0 obj
|
||||
<<
|
||||
/Contents 8 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 7 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
5 0 obj
|
||||
<<
|
||||
/PageMode /UseNone /Pages 7 0 R /Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
6 0 obj
|
||||
<<
|
||||
/Author (\(anonymous\)) /CreationDate (D:20260212201938+01'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260212201938+01'00') /Producer (ReportLab PDF Library - \(opensource\))
|
||||
/Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False
|
||||
>>
|
||||
endobj
|
||||
7 0 obj
|
||||
<<
|
||||
/Count 1 /Kids [ 4 0 R ] /Type /Pages
|
||||
>>
|
||||
endobj
|
||||
8 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 929
|
||||
>>
|
||||
stream
|
||||
GatUr9lldX&;KZQ'mjZ8ac)'%"G5DIVo_We9UhFAPRB4O6Y_P^ni6I>?%hPEiFD-kgPFBpos/nN5_7iZp&@,T\-V$ZM?7?#/:_c!6gt2l#,@NIl1'W@hf=\sXji$^N*2YX$I#hmmW*D@?lW]c'X*jBOXbT`Y(15rMBB0$`T\</j<i-1rE$Ln5YiMIL>]EEI>a<'`s*]?ja"Rq1.sLs+Is<)$/]cNiSe>Z!FphtZRZ5Z$RY_kQ8%`qI\u01=P:EHo0;^^:$F"^$_#T)CO&X*NdhVmm,D%4HSP2_@)Yk(g_66*I0\eJ\X'0p7M)S@@"c'(_C!o,rH%nj*o%"L>1/j<1V6;Fle@&lGc;&5j'%_G<Da.$kuu21eMjldg(WHObI''g/orN6>J<a`a.[cJ7;En*E8Vdg#qF"A/5Bu,Rq\pUklRcO`S"JdR'%p%.o+SU\6T6dZhrEed-g*!ZV.Ua6nYc!Okg%t-\(Cgjll[so6IoQ/!)F==M8j4/%mCJO$tO97;BTrms/G(K_d<Tk5"E`]3r+oA>4.%N>9[EN>N>.=Xl%8kD+&cCM_YUgMhj=)`C;-21t/ji7f#P#jihd&bJUlR3m%g0=U&7n)Mr<6nbb!F#m8/n`TZnIooN4]kr(O0qP)]\r\Y.l5I'm)(7&?&g#&b*_lp]-C-FfohG2gRl$1KI71[0\hOcPN5b0XqEZd/7p3?[dbS<XEWl7]dKMEP@Uk5dK1hL%b.+lcoC)=@J1^D7\!)8Y+%s@cQ-Zbnps=rmcb+Y;l7Lcc%.E!V[dP<M%EpF1eMaTZ+G[pOhUago5JJ:7_8d;TVVpB1V4_rWNNm%@D6H*P7@35<+@GSuH5XVC"F3j3k1,V!EK5caK,9S!$+j;&JU3K0$Qf#DZgDiIB<u*)':+)aWY\sNPU_VnH*8,abo2Q?!Wm[P3W~>endstream
|
||||
endobj
|
||||
xref
|
||||
0 9
|
||||
0000000000 65535 f
|
||||
0000000061 00000 n
|
||||
0000000102 00000 n
|
||||
0000000209 00000 n
|
||||
0000000321 00000 n
|
||||
0000000524 00000 n
|
||||
0000000592 00000 n
|
||||
0000000872 00000 n
|
||||
0000000931 00000 n
|
||||
trailer
|
||||
<<
|
||||
/ID
|
||||
[<fbf718fd6966c08977bc531039e9de4e><fbf718fd6966c08977bc531039e9de4e>]
|
||||
% ReportLab generated PDF document -- digest (opensource)
|
||||
|
||||
/Info 6 0 R
|
||||
/Root 5 0 R
|
||||
/Size 9
|
||||
>>
|
||||
startxref
|
||||
1950
|
||||
%%EOF
|
||||
@@ -1,74 +0,0 @@
|
||||
%PDF-1.4
|
||||
%“Œ‹ž ReportLab Generated PDF document (opensource)
|
||||
1 0 obj
|
||||
<<
|
||||
/F1 2 0 R /F2 3 0 R
|
||||
>>
|
||||
endobj
|
||||
2 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
3 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
4 0 obj
|
||||
<<
|
||||
/Contents 8 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 7 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
5 0 obj
|
||||
<<
|
||||
/PageMode /UseNone /Pages 7 0 R /Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
6 0 obj
|
||||
<<
|
||||
/Author (\(anonymous\)) /CreationDate (D:20260212202036+01'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260212202036+01'00') /Producer (ReportLab PDF Library - \(opensource\))
|
||||
/Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False
|
||||
>>
|
||||
endobj
|
||||
7 0 obj
|
||||
<<
|
||||
/Count 1 /Kids [ 4 0 R ] /Type /Pages
|
||||
>>
|
||||
endobj
|
||||
8 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 930
|
||||
>>
|
||||
stream
|
||||
GatUr9lldX&;KZQ'mjZ8ac)'%"G5DIVo_We9UhFAPRB4O6Y\ddni6I>>m,>$n1f(1fZ:,Dl=KX%"U0Joe\B95i51,Q.ghMl[fX\]%c.`kl:kS3<KC(#\;";Bd7UGNS2#Xp6%M%Na_d%j&PTm^&&NQ#(Q!_KAdb"'S+&0(<LC^D-ds?Am(`J+"F:.+1O,fdkZaSL4Z3Qh%**?.PZb#[#O/mg8[sN:$4$TW!AIR<41ere[:#`4C^nk-kncsh^rl@VR/_``BM-^#05/pmYN<.q>4+[a]8P:Kn&7+*Je\fiF&-oGob1$&ALp]\%)>s-^^.8ZK[$`b]'#$BRl9&Ko1%0^;_taUQ0k$8Wd(KMkMoa2UK(.nk"2Z8Wu@G\(>T;%KOL@;8Xikjq$kq&_VrkUUS8T\=WFq=@"o"D$*2O0hKIVA@+rDqAo[>V4XQYejI-a)%W3*>r9SlPl#f)A^s%u.0TrK\O(<aUb*,'_KsGh#QfU!U`X:eM2H4VniC\#\n7.QK>ERWXpjJ_N(87m*Tma\f`\,p21E#F^@UfKJD'P=&1b:MNZLbsnjKGq!@rK7".W*fdD6t3ThZr%^i'M*!WKs2IP0,V\X)Pa(p\YT+Q6c`\jc7ld@Z3Op/c!;9!pV?X+K"`%<Ruq!"f\Y%WQ[V9JX,^YrUb%a8iAE*&ZBXG=%JN;eFaF<?L#O$oP,ppB/0mA&O"55?'+DBK9db7s,\j873XY083aW68["->g,8n*Ihf2?IW"o.ccota2i[AZqi-A;YWqaMm:NS=iu(%j%8>A:FrODm^fT:'VLTX%YPh_p*;o_`1^+t&Un<6k9:^[[Q)oob.qq'#q('$E?Nh<o&(&M``rY6i%9_7T)MC*hU(mViDC"R8o`XPO!\UuI[*GQ!9:X+a,u,<R[Ad;NiDM-iguO*&83L`Bq$X1U>;-~>endstream
|
||||
endobj
|
||||
xref
|
||||
0 9
|
||||
0000000000 65535 f
|
||||
0000000061 00000 n
|
||||
0000000102 00000 n
|
||||
0000000209 00000 n
|
||||
0000000321 00000 n
|
||||
0000000524 00000 n
|
||||
0000000592 00000 n
|
||||
0000000872 00000 n
|
||||
0000000931 00000 n
|
||||
trailer
|
||||
<<
|
||||
/ID
|
||||
[<7157e2a041ebda766e80d1441656b00d><7157e2a041ebda766e80d1441656b00d>]
|
||||
% ReportLab generated PDF document -- digest (opensource)
|
||||
|
||||
/Info 6 0 R
|
||||
/Root 5 0 R
|
||||
/Size 9
|
||||
>>
|
||||
startxref
|
||||
1951
|
||||
%%EOF
|
||||
@@ -1,74 +0,0 @@
|
||||
%PDF-1.4
|
||||
%“Œ‹ž ReportLab Generated PDF document (opensource)
|
||||
1 0 obj
|
||||
<<
|
||||
/F1 2 0 R /F2 3 0 R
|
||||
>>
|
||||
endobj
|
||||
2 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
3 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
4 0 obj
|
||||
<<
|
||||
/Contents 8 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 7 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
5 0 obj
|
||||
<<
|
||||
/PageMode /UseNone /Pages 7 0 R /Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
6 0 obj
|
||||
<<
|
||||
/Author (\(anonymous\)) /CreationDate (D:20260211215935+01'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260211215935+01'00') /Producer (ReportLab PDF Library - \(opensource\))
|
||||
/Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False
|
||||
>>
|
||||
endobj
|
||||
7 0 obj
|
||||
<<
|
||||
/Count 1 /Kids [ 4 0 R ] /Type /Pages
|
||||
>>
|
||||
endobj
|
||||
8 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 640
|
||||
>>
|
||||
stream
|
||||
Gat=h9lldX&;KZQ'mjZ8l&;km&-?a/g[ro8)CmaRA<::6o+;OVr9q8=FqSErb^a:B5[rV@YBH66_*RHOc2_`.bjQrV#''gqF$,Q!iMW3Y9^$2ea$9ph"@3m_+#nhdd'kF54p)BX4;7h1aoiPs6d5!9d%Ch?m&1t6\kp"\>q(sQ3S`r+W!obp048L9$1.G7T@V?X'gEFEI09$Hj>WH3`(.>P3%b4lIfoU2jgcJccS1]T@-o;B%E-t.G;/ulF,dOf!G*H_K_[F@As>nW+s'3+G<AW*N;Oe7B@Nu$0("<:F:c_Z_P*NlKaTHeU<&=C21<&b+M)ZC_+0s"4Cmbe(Lc/Ojq#WWZ5::uCV<Ct]r=u.,Oi.T&q&?IlOr+8\7fmbqP?d.+i6g`*7?A"G%R.<L(3N4aA)N`*6Y'PSVNbM`3DQs*[fMVQpZ^".oZ,`qdQ@U/a6/7?n.FT=A?Q3DB^25n!2pC1\?`SKsIXUgL`-r@f(+BA\A&!q<2_DasND--cQ'jD'5N3l`<o9*4G`j6<Ur#@B3+_S[QshZg2HB?Jp!9#hmi)XUXpPm*B:V2uBFKVPtfE_A:QPQkn#]."#hXgMNuRJ\"S?pg[;i>2tC>`0^-l_`fq^0O#!"OST(^PQ)F7SGi~>endstream
|
||||
endobj
|
||||
xref
|
||||
0 9
|
||||
0000000000 65535 f
|
||||
0000000061 00000 n
|
||||
0000000102 00000 n
|
||||
0000000209 00000 n
|
||||
0000000321 00000 n
|
||||
0000000524 00000 n
|
||||
0000000592 00000 n
|
||||
0000000872 00000 n
|
||||
0000000931 00000 n
|
||||
trailer
|
||||
<<
|
||||
/ID
|
||||
[<dd9e03bef959be273127c681a80b79d4><dd9e03bef959be273127c681a80b79d4>]
|
||||
% ReportLab generated PDF document -- digest (opensource)
|
||||
|
||||
/Info 6 0 R
|
||||
/Root 5 0 R
|
||||
/Size 9
|
||||
>>
|
||||
startxref
|
||||
1661
|
||||
%%EOF
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -24,7 +24,12 @@ from reportlab.platypus import PageBreak
|
||||
# HELPERS
|
||||
# ============================================================
|
||||
|
||||
def _create_stop_histogram(stop_distances):
|
||||
def _create_stop_histogram(
|
||||
stop_distances,
|
||||
p50=None,
|
||||
p90=None,
|
||||
p99=None,
|
||||
):
|
||||
|
||||
fig, ax = plt.subplots(figsize=(6, 4))
|
||||
|
||||
@@ -34,6 +39,16 @@ def _create_stop_histogram(stop_distances):
|
||||
alpha=0.7,
|
||||
)
|
||||
|
||||
# Percentile lines
|
||||
if p50 is not None:
|
||||
ax.axvline(p50 * 100, linestyle=":", linewidth=1)
|
||||
|
||||
if p90 is not None:
|
||||
ax.axvline(p90 * 100, linestyle=":", linewidth=1)
|
||||
|
||||
if p99 is not None:
|
||||
ax.axvline(p99 * 100, linestyle=":", linewidth=1)
|
||||
|
||||
ax.set_title("Stop Distance Distribution")
|
||||
ax.set_xlabel("Stop Distance (%)")
|
||||
ax.set_ylabel("Frequency")
|
||||
@@ -46,18 +61,30 @@ def _create_stop_histogram(stop_distances):
|
||||
|
||||
return buf
|
||||
|
||||
def _create_position_size_plot(timestamps, position_sizes):
|
||||
# Align and be robust
|
||||
def _create_position_size_plot(
|
||||
timestamps,
|
||||
position_sizes,
|
||||
*,
|
||||
max_position_fraction=None,
|
||||
):
|
||||
ts, ps = _align_xy(timestamps, position_sizes)
|
||||
if not ps:
|
||||
return None
|
||||
|
||||
x = list(range(len(ps))) # robust axis (avoid matplotlib categorical date issues)
|
||||
x = list(range(len(ps)))
|
||||
y = [p * 100 for p in ps]
|
||||
|
||||
fig, ax = plt.subplots(figsize=(6, 4))
|
||||
|
||||
ax.plot(x, y, linewidth=0.8)
|
||||
|
||||
if max_position_fraction is not None:
|
||||
ax.axhline(
|
||||
max_position_fraction * 100,
|
||||
linestyle="--",
|
||||
linewidth=1,
|
||||
)
|
||||
|
||||
ax.set_title("Position Size Over Time")
|
||||
ax.set_ylabel("Position Size (% of equity)")
|
||||
ax.set_xlabel("Samples")
|
||||
@@ -67,9 +94,19 @@ def _create_position_size_plot(timestamps, position_sizes):
|
||||
plt.savefig(buf, format="png", dpi=150)
|
||||
plt.close(fig)
|
||||
buf.seek(0)
|
||||
|
||||
return buf
|
||||
|
||||
def _create_effective_risk_plot(timestamps, effective_risks):
|
||||
|
||||
def _create_effective_risk_plot(
|
||||
timestamps,
|
||||
effective_risks,
|
||||
*,
|
||||
risk_target=None,
|
||||
p50=None,
|
||||
p90=None,
|
||||
p99=None,
|
||||
):
|
||||
ts, er = _align_xy(timestamps, effective_risks)
|
||||
if not er:
|
||||
return None
|
||||
@@ -78,8 +115,18 @@ def _create_effective_risk_plot(timestamps, effective_risks):
|
||||
y = [r * 100 for r in er]
|
||||
|
||||
fig, ax = plt.subplots(figsize=(6, 4))
|
||||
|
||||
# Main curve
|
||||
ax.plot(x, y, linewidth=0.8)
|
||||
|
||||
# Risk target line
|
||||
if risk_target is not None:
|
||||
ax.axhline(
|
||||
risk_target * 100,
|
||||
linestyle="--",
|
||||
linewidth=1,
|
||||
)
|
||||
|
||||
ax.set_title("Effective Risk Over Time")
|
||||
ax.set_ylabel("Effective Risk (%)")
|
||||
ax.set_xlabel("Samples")
|
||||
@@ -89,6 +136,7 @@ def _create_effective_risk_plot(timestamps, effective_risks):
|
||||
plt.savefig(buf, format="png", dpi=150)
|
||||
plt.close(fig)
|
||||
buf.seek(0)
|
||||
|
||||
return buf
|
||||
|
||||
def _align_xy(x, y):
|
||||
@@ -386,7 +434,18 @@ def generate_risk_report_pdf(
|
||||
story.append(Paragraph("6. Stop Distance Distribution", heading_style))
|
||||
story.append(Spacer(1, 8))
|
||||
|
||||
img_buffer = _create_stop_histogram(stop_distances)
|
||||
stop_metrics = (
|
||||
results.get("checks", {})
|
||||
.get("stop_sanity", {})
|
||||
.get("metrics", {})
|
||||
)
|
||||
|
||||
img_buffer = _create_stop_histogram(
|
||||
stop_distances,
|
||||
p50=stop_metrics.get("p50"),
|
||||
p90=stop_metrics.get("p90"),
|
||||
p99=stop_metrics.get("p99"),
|
||||
)
|
||||
|
||||
img = Image(img_buffer, width=400, height=250)
|
||||
story.append(img)
|
||||
@@ -405,7 +464,12 @@ def generate_risk_report_pdf(
|
||||
story.append(Paragraph("7. Position Size Over Time", heading_style))
|
||||
story.append(Spacer(1, 8))
|
||||
|
||||
img_buffer = _create_position_size_plot(timestamps, position_sizes)
|
||||
img_buffer = _create_position_size_plot(
|
||||
timestamps,
|
||||
position_sizes,
|
||||
max_position_fraction=config.get("Max position fraction (%)", 0) / 100,
|
||||
)
|
||||
|
||||
if img_buffer:
|
||||
img = Image(img_buffer, width=400, height=250)
|
||||
story.append(img)
|
||||
@@ -422,7 +486,14 @@ def generate_risk_report_pdf(
|
||||
story.append(Paragraph("8. Effective Risk Over Time", heading_style))
|
||||
story.append(Spacer(1, 8))
|
||||
|
||||
img_buffer = _create_effective_risk_plot(timestamps, effective_risks)
|
||||
risk_target = config.get("Risk per trade (%)", 0) / 100
|
||||
|
||||
img_buffer = _create_effective_risk_plot(
|
||||
timestamps,
|
||||
effective_risks,
|
||||
risk_target=risk_target,
|
||||
)
|
||||
|
||||
if img_buffer:
|
||||
img = Image(img_buffer, width=400, height=250)
|
||||
story.append(img)
|
||||
|
||||
@@ -68,6 +68,18 @@ def create_app() -> FastAPI:
|
||||
name="static",
|
||||
)
|
||||
|
||||
# -------------------------
|
||||
# Reports folder (public access)
|
||||
# -------------------------
|
||||
reports_root = PROJECT_ROOT / "reports"
|
||||
reports_root.mkdir(exist_ok=True)
|
||||
|
||||
app.mount(
|
||||
"/reports",
|
||||
StaticFiles(directory=str(reports_root)),
|
||||
name="reports",
|
||||
)
|
||||
|
||||
# ==================================================
|
||||
# ROUTES — UI ONLY (TEMPORAL)
|
||||
# ==================================================
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
import logging
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi.responses import FileResponse
|
||||
from fastapi.responses import FileResponse, JSONResponse
|
||||
from pathlib import Path
|
||||
import uuid
|
||||
import re
|
||||
|
||||
from src.data.storage import StorageManager
|
||||
from src.calibration.risk_inspector import (
|
||||
@@ -261,15 +262,29 @@ def generate_risk_report(
|
||||
|
||||
results = result
|
||||
|
||||
# ---------------------------------------------
|
||||
# Generate file
|
||||
# ---------------------------------------------
|
||||
# ============================================================
|
||||
# BUILD SAFE OUTPUT PATH (OUTSIDE src/)
|
||||
# ============================================================
|
||||
|
||||
reports_dir = Path("reports")
|
||||
reports_dir.mkdir(exist_ok=True)
|
||||
# Project root (3 niveles arriba desde router)
|
||||
project_root = Path(__file__).resolve().parents[5]
|
||||
|
||||
filename = f"risk_report_{uuid.uuid4().hex}.pdf"
|
||||
output_path = reports_dir / filename
|
||||
# Reports folder outside src
|
||||
reports_dir = project_root / "reports" / "risk"
|
||||
reports_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Sanitize symbol for filesystem
|
||||
safe_symbol = re.sub(r"[^a-zA-Z0-9_-]", "_", payload.symbol)
|
||||
|
||||
# Unique filename
|
||||
file_id = uuid.uuid4().hex
|
||||
|
||||
filename = f"risk_report_{safe_symbol}_{payload.timeframe}_{file_id}.pdf"
|
||||
|
||||
symbol_dir = reports_dir / safe_symbol
|
||||
symbol_dir.mkdir(exist_ok=True)
|
||||
|
||||
output_path = symbol_dir / filename
|
||||
|
||||
generate_risk_report_pdf(
|
||||
output_path=output_path,
|
||||
@@ -278,8 +293,24 @@ def generate_risk_report(
|
||||
results=results,
|
||||
)
|
||||
|
||||
return FileResponse(
|
||||
path=str(output_path),
|
||||
media_type="application/pdf",
|
||||
filename=filename,
|
||||
# ---------------------------------------------
|
||||
# Create public URL
|
||||
# ---------------------------------------------
|
||||
|
||||
public_url = f"/reports/risk/{safe_symbol}/{filename}"
|
||||
|
||||
return JSONResponse(
|
||||
content={
|
||||
"status": "ok",
|
||||
"url": public_url,
|
||||
}
|
||||
)
|
||||
|
||||
# return FileResponse(
|
||||
# path=str(output_path),
|
||||
# media_type="application/pdf",
|
||||
# filename=output_path.name,
|
||||
# headers={
|
||||
# "Content-Disposition": f'inline; filename="{output_path.name}"'
|
||||
# },
|
||||
# )
|
||||
|
||||
@@ -212,7 +212,7 @@ async function validateCalibrationRisk() {
|
||||
async function generateRiskReport() {
|
||||
console.log("[calibration_risk] generateRiskReport() START");
|
||||
|
||||
const payload = buildRiskPayload(); // reutiliza la misma función que validate
|
||||
const payload = buildRiskPayload();
|
||||
|
||||
const res = await fetch("/api/v2/calibration/risk/report", {
|
||||
method: "POST",
|
||||
@@ -220,25 +220,23 @@ async function generateRiskReport() {
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
console.error("Failed to generate report");
|
||||
return;
|
||||
const data = await res.json();
|
||||
|
||||
if (data.url) {
|
||||
// Mostrar visor
|
||||
const viewer = document.getElementById("pdf_viewer_section");
|
||||
const frame = document.getElementById("pdf_frame");
|
||||
|
||||
frame.src = data.url;
|
||||
viewer.classList.remove("d-none");
|
||||
|
||||
// Scroll suave hacia el visor
|
||||
viewer.scrollIntoView({ behavior: "smooth" });
|
||||
} else {
|
||||
alert("Failed to generate report");
|
||||
}
|
||||
|
||||
const blob = await res.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = "risk_report.pdf";
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
|
||||
window.URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
|
||||
// =================================================
|
||||
// WIZARD NAVIGATION
|
||||
// =================================================
|
||||
@@ -804,6 +802,13 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
document
|
||||
.getElementById("generate_report_btn")
|
||||
?.addEventListener("click", generateRiskReport);
|
||||
|
||||
document
|
||||
.getElementById("close_pdf_btn")
|
||||
?.addEventListener("click", () => {
|
||||
document.getElementById("pdf_viewer_section").classList.add("d-none");
|
||||
document.getElementById("pdf_frame").src = "";
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -238,6 +238,27 @@
|
||||
<div id="stop_distance_plot" style="height: 360px;"></div>
|
||||
</div>
|
||||
|
||||
<!-- ========================= -->
|
||||
<!-- PDF REPORT VIEWER -->
|
||||
<!-- ========================= -->
|
||||
<div id="pdf_viewer_section" class="mt-5 d-none">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h4 class="card-title mb-0">Risk Report Preview</h4>
|
||||
<button id="close_pdf_btn" class="btn btn-sm btn-outline-secondary">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<iframe
|
||||
id="pdf_frame"
|
||||
style="width: 100%; height: 800px; border: none;"
|
||||
></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- Plotly -->
|
||||
<script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script>
|
||||
|
||||
Reference in New Issue
Block a user