From 4a4fd2a27f2370e7ed4087cbfd462b3b263a2e62 Mon Sep 17 00:00:00 2001 From: Jens Tirsvad Nielsen Date: Tue, 13 Oct 2026 15:34:49 +0800 Subject: [PATCH 1/5] Take the screenshot of the program running Task: MIL-005#1 --- docs/images/weather-run.png | Bin 0 -> 36550 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/images/weather-run.png diff --git a/docs/images/weather-run.png b/docs/images/weather-run.png new file mode 100644 index 0000000000000000000000000000000000000000..9f170e72eaebbd6b55ba73b8e3bcdf7e6c1111b4 GIT binary patch literal 36550 zcma&N2{@Gf`!+6ANFh%}c8SVPvV`$u3Q3|xvP?okcCyc;#8jB1OxY$6l09VXM#xx- z2$MaAEMpst88c?)f6vhK{r-OM`yR*tIgXCU%-r|q{w&vWUgve*Pp@1y+0S>BkAs6_ zznLl6nuCKI%)!B#z_SPVrbPa0C-A>r0oEqxIEuT(=YS8~Zhu?+&B0NM*tg}h8~DuY zYib+7!ExX->))=bM9)PIjs!b1@ZZ-$92ZABi)mj|#>r&PI{7)eBzkXYIQgU+=Q@~2 zuvX&q!qSE`{*06Kkr#@BFWz!Nj9v)(#ynV1{QlK+hVRiKw-;9OwTo{}y7r6QzH

O^zyfxpb2#x6@y7U&n{o(H}AbPbf)Cji=7F-ES|VraFa%giyUAQ;VuzDold>=sB* zxLD~a0SmEU>L$~U0h4`a1dC~Lq<>zfA5kH{*(1|4zIhgwOZ&FWm~i>$S?OFht$h0? z-D4-J`0)EV0%k#hz$zT>s=6e*uGEd zBo!eF+@pqM#BD-N4uHi)AKpI{qf@X)mWWcphIO^?mW9!WF_P(d6AT)Nfn}D!wk(-* zATfgn1T1U`<7(?&Pf_*8qmj(XHFd$nX}S-vWeE9EZ;5J$h_Rv>^z%;A1J9nwpc^%# zL2+svNkTcO3)B&S)rf0R0T+BYAAODHTLGjhz z!{qU>(WGiA6qI2!x=89#>iue)_ER>QbuT~oPbZ*AqGx_8Sa&}JCpyAKn;J{R$> zy3+JS!uu^v!FWHp&d^-WKPpDiWXX81tokqb2nM!=WM(s$MUr)vHbq`k)wF+f*;iu# zJj(IkM-e%JWhM8bbSq~V61sxABgZv0R^Uu81BKU|LPtERUVQfZJB9{3a-)jlnB(L@ zE+M+5%PoDUB9=Exqrc1%60AAesxiOyQ|+F+jDHmj~~2_ zw$d*LC+Be`hHS)M6^6Hwa*8;GM&B2Fr(fBgfAc>~-(A4qo;@go%4w)Ij{d0_YDg_% z7GS!&mx?9eU$66lC*Mj_f9DbXMK{y<4YEbvwPE!H6OoOtI1@#_Us9Olmi7GcXFTH{ zu#c>{7R`45L!bfX0oiOkM(S1$U|IK^HjIF%fsu#D?{W!LE{rotJ__9!4_p)+&&eMd z6H9X|Qeb`&1T#}E@t+P1C5}nu7`6Bfcf$PW%D}X{6ni4Awx?~}3wy_UjPhJ}7To^C zKd9l}Q&DXPYwCjEUd|?;$B~}}qIPx8pmykQ| zkjTJu1DDi2X4pTololM6aTqV?rCZnIbmpbNmSjkR;#A3~0BCZ>N6YWak?-#R6Awe`7F0>ha-aw;S4S+I=Y;4TLc zcMa-){kjmv7I7cndIIkj3;=c%( zFiTnHuyFwv^W`YiSh`H02Feo%8jNcPy$6D@4i&K<`1zv=H9#pjpAXDv9jAx^UjYB* zo|9%d0tbgmtPyL?931vtbDW7K!}a0%OQVYf&$5N9-eeOpoi6d|n@jZ?DTuIySYER0 zH!vJOLvu$Qo~O(%RniB9*!Sj%z}JOII+?)_4(os+5DOf1SzCYQa~m(0&`g1Q?ea3( z^FfF``_gyU_sSw@-+aDXjtC3WEIo!YGs0i(16wB?gzJ=i5@22P(DIE0Tn9R-*yZvj z%cdOe3v2woi05Zfg7l#Il2+5YJG*gE&)-O}DXOhjT=>lGGH_PniByq)+6UvqIC0zSF zYpp~Zy6{7lL=mfZGX2AE<$ebydueAF)IO4O;A9Ctnz5M#`gANAFp{I)Ge^< zaq$({g^PC|70>AnsFY`KNU0Ku!VJ-%q>G;)S46l=->HDa9Va02BjWwIG1)j``eT!aK59b(BHhX z$Ow$earV-_4`Q60q=a}&A6!;k4Vj{;2ZdToopx=NU=8~)IP|38+qB~c6$V?UMJ`jG zB+VRG@Ee`su0x%0Lzg(E=gB^~;_}l3)@nY@fTVv`s?9+;@ ze}yKy%5|lQmLxR&;Io~^A0?$8 zxO6Rj2kqu(U!IYEO3?2j=Fy|j81wjECzCqMxS%}tPaNTaH9KzfE_&w=`vXxttlK!M z(&mj^*2^ne4fK9*73O1hxqS%>_&Uf`M61B^iU^fKn*lem-Mv)h(FQr228O#U^du{S zaiFzT1y62e=hFs4TJh1Z~QVALxPV=HZah zajlhQk=1y&(|hvAJzEtj>9dKc7GUY@k}aJw-7n`PkGa;#FhBE z`o-LA`Leq-x~R&864xO5C)WR9%Qq&iR2zzq0C_KJdW()T+YC*(msROppID z@fX84hY_5K&+rNz5?Oh~lU_cHo|ow)!;2pC>;t{IbSw%U)EQADaZD5=dG~dk^ z79P2EX+CilIYOC2<7mOZ`yk6%J61d9yk^uQ>TqUK{u%YP?VO)BKuab5$w+d&6Y3>>$LlOEc~Ky6cj0PEFnr~<51MOC5k!?JDb%W zR>~x;wE`Oo+Pm!ybyxun{=rj%wvV-lX^O$}&5lJqOSAAr%u!BuDhWX}bIWQW^|Sfi zM8sA3ZypoW^{17@Z2Q!hv!nDKdyS73P8eL;*AgN4?eTTjv)r<*bNX06zvFnnlz70b zZ#WvK1>x*J9(Lv(0AC~v&MiN_trT8)92A~X7DPM2EcYkPNJ_CT@ikoIOl&IoS_NtQ zPKb!qaww2bl(7#DBoChvo&nGd)?+-ye_9}Eb?%$1vlLQ(p?Vef;uK^rU$q|8I-e1i zZZN=(RchTEQE*y24x1IO&-F}A_W^I)elH9K zy*=b6J!|ArXaCBFn`a;Zb#O>(UhGk#yR?P_43xAHvTEP7|KD9KjH{5o@AsAIHndBsUha>cF4Ti=oMXCxT(8%iy7pCf` zAkUyEQ^SXpXpsxHLHYZ^)_6#5L=o}0AS+Tn4ESj%IHyo)fioN`_8NsfH)?^|fik9I z&FE`!FDIizZ{lth6P%SR<$n!K(SLYw3W;vax}yG?XXrmRjJ_Ci=?T3pJPZd;h#O+IKFG^(LaIEoqhMO#um+64-TKu$uV*vr1lqQ zVs_n^e|KWHNIG%!!P6>){NvT(hcpvmogq z8?JT++3MQ$VFIgr1Jlu-Ew0KrL{;Twl$}$A#Gn28#+8`$QGOucN#~8J2-oCygtrEvX^SIbni7);WgPqy!d!9IyDt)V}TmA4KYl4}LFRi`iv^Zl!D>hxHM# zosk=?q-C20CAJ-iBW?GeWG24-JBP0>Xe$f0j;O&pe4T8omb@5GDlatm8ijUr+ueeChcC&n)78vcGeBJYVqn*?uq`b8 znHEL^4~;S|(cF6UbOXSco5DD)`$BM?8#LT2McA89xjvFZi1Ys}0v>q#bxCu~wO==0 z?mO*aOZq{1%_@^QbYi8TbZzEca%Tw&x*0?+C!3RhbYlWxt7Ik=Gg%M?cWj7lpBm&8 z8kanxHaf~22Cx?Y(3#PVYnz^>9SG7h?0KpC_irNpDu50-COEOiY}5d<=J5fSGSmm{ zX6{iwr7agNzdcbw$x%u^W%X9AA)A7yqRUUUx|QGTj=Xp;{DTkAg$1J)1j@6#rxKOT zC6rs@`GvAhz`8zd%<)PoUaW*gE)+V8u*UY)kL3ZcYm=#ddEQ}>4h_SOOXDR>`;4@Y zZX4v2c}CzpIyH-^_cSs=k^4vu<|_VJXIZN&PiKr^%dL{+AbwVTz>(lajxv2CZRd86 zUzq6|(|DSPXIgCd$hlqh5%wteNV4o9cpDVwMf;Re9{%Z?SfO*@(UQImt$(LL69Av` z=}4)$e=b&`X3I(9dV{^nxkH$x=Sg!#woz@_Mf;i_D$iyfch=HhQP>+ z|2;sQroKN_^FT&W1*cFbg3mw(M?-Xfsj2k}65D9v14lHzYmUzCeJis3js}X6D)kPl zhINTZVf8MUW0A#{PyeiB1|MY_0TsU;*Uj;<>u6hIP`~gz;DO=M(cw4vg;!?2Y4=C# zj>Y1Mby0a^Zf1~TKeNtTg+}0TE&iWCD2_-Y8raXbVoMXOwoUb+>yfB7%1b!fv(NS9 zK%{)&uf*E4JJ%ZQ?mArP^Eq`)PK`sPO3uV~TiLfn zb?mqmb|-@wg?UQ*y9~616_0#_Ab!n~AY9wN0eG9~@z8Oh`a%~SRBYdUw4#veo`+*w)35q70$}UmoQ#sv z>(W-91#~-l<$G2d8%HcbgPA^iWwnwh+uri`g+j<5Z#lZX5ev=vQSjpx=@~Axc9-8` zj2{2e%V`{y7C$QJt0uu1SG3Kvotx+5>7GO2so=-nk;Ng{}wh^s&DMfqZwnCUtBQe z2pS-GhDAokz=s|8oB*;IrajuA9Al|#3}C}UkM@m@=MfWKZSmFt5#Rpa#;UZH45JoH zL!Z(5$UwvlxX*%rR8H&C?IO(+8E(oqJL8SOmqW0f@r&m-m;7{$Zy)8R1QjjrLw=o| z&Y>=yR}pMamsm5?awFdhi;yLVRJJNsMDE`{_Mfrj-Lfjt_fm`9jjO)zl{HKgvAj3! z%Bxo=^7<{0$c4^*(^xNSq{XWZP+?XFvh)0hNnE6#Mt#LK>3?#%K2o@WWf-PnORkEb z+Voo4Uc&oli#LFv^;gOnZfT2QgC&b#m_8RLo}WJ;TzzvrC~rTQZ`zYCy0;rWumrj6 zU&I@b#WhgbE*Bb+7hT3DR;pRrlUm2=4Q!xvkz=76TDvO|7(&2i>;FHWa+epPE8 zgJCdG8c?sjM}Db0DrXA)2ZXD`FJjnH{Yn*=0a z8{##xtU{WV2&cUnVcaGq%%j^N@U|2Wcnmxi3|3VX?Yg3S_l@s5wdCoxr)@YnN*`2J z2=TiEJ8z5;+GGE^L1~8#F)Yzz`zMf1_WhgiwpYRN%RiqhvDW}hrOAnO`VUY@uL0k* z?SExM%N=;#DJ7BWQ$<`>#F`XGje{-OfUi@TW!#L_cO;Z=bA`Ttx6VLJtAyL{YEQ_5hSS$|5$Kr2XLiP5R8$95F zq*I`1-ao!b*ZDMu&h3Dh=WqG$G3EIlGc(kKhZ;E3D(b@`vWIeUJtb^x-AKK$AAGT} zeM}JE*10F9z2wlFS>C*YR7D#{JsEPAs+n&f2VS9ImH)8TgoLWpxhK9izJ!!{8<0 zmNn+9E=l3_l!}&<&&#PH_$(EzPBZ9KKS(#V{lV+)?6!gQrsY1Vu^`+pr5flOdTjY) zZI&pe$;?TL-Jf|wd7RqD7obm_+EiuuH|J(h<457y^4U4lOQmVQVl+l=cb^h8yOd9= zxv`nz=$OCkn9V7qt5V~IO3p9;qIaRl=zK!8mLs01BqimUfFT`am-&Uu`cbBt-gH~J zge~-ViW-5pB*^(Em;;X+wR$IQJJFO~beP!k-tND?*cx`p@ocXJ}84_1op z6~Sws{ykt(T*8H5XSg_pDWI4W#L|5AGqlzEX-adQMfzfshi*v!GuKd3;Yv5Yw>DJr zb(Xl;S)kvrzCoqTlFNO!juEujQ0nHm3}+(oxS|*CL+3&NAVN1!q8sQ9KYUh;4f=0 z%BO2|l3sY>S(dfl&Hy5H>zbXeo%CdePwMyN72!(2y~Kuo4-+zKYnkIV(g&Mg9E;|; zLD%||(ywzRzUoDLSEsskXqruB2L#5L{UZ)~``Rt@15leq@a+Hk@Nzm&dNuBg}Rw|nLlihR|e8u)M1nkLtmVGXy{At++YpC?>HF_!?mB<6@lAQe z&l$acXW-ib%)sxCgGQ#p*UM?MHC|+$3FRj;T|%5fCXNTM^PNssL39^sHEbWg?*Xg; z_}Z@?NJ@|88!UN*Rbox+vrDXtqz0hGY6VKHwD0dDL^KM?LyXEoj69bj`Q{kcdr)ew z20(HG)sRku&<+4=))WLnL`f|wQlt!JBzHN4WMDc)%`(3;0=@~+5Hdcv+%uXw=PFEx z9wdDb@|L`HK;^f{BR;P;KP{n7CdwrhX7qhXMGKsJ?FIV*Yd-ypowS)7B9NV&&^4hP z)h2XPm{8s1s=W3XUau2I4;c8*&rwEGW{_5OtZz)SdUJFZ4%7OQ_xKOtJN040YcD5j*e|!2Ijkf>t72D{Lg9CY00{&`Argz-!^UVjR2fEQJ6+z4^ z47cg|ZUa-c;lhBCpRnPcQ&E;ebjc;Y@lApxF!tqq*dOD+G>%M0vh#yh0xHzH#n0(n&1 zU5`j3j;}aldRK!LB|teuspQV`+cBjlRkT}s!-^08!8E_4;KI$(ec1!Am64mIc%!Bi z(`5OU(g;!iS>7VX-SHvA>mWs=o4q!6ihKDtr%^A?LNDS$_THLtmEx$OY0r3{c^x*t zUx%{;I2%jr{y~Y8dM|vfh1+|he;RBA9YHi_gDyRshUKDh>;9gc-WFrzM!wYFO3YV% zL}ya4yZ&z_9HNLcV=94V?e0Tzn4R=NlJ?3g;mfer3!5(wdCIz5Tp>lDu_1q4o;WDt zNwcP3*!A2lI5z5$aMvp{$h_69hP_nE$#`b(bh9B&biB`x%@Bu_RPL2csB`&JBlowZ zX?h>H?C!bLf?A*|)4ce5pxeu{f9=ObClmM0jGI_Xk1gc3ljPyq72=Ac6fd|ns+p2; zh|165Ddvz-x7QSQ#>7kdA5H|LH|{OzdD1%8S-iOKD3rQUu^4bJ6`-fxL*tbRC3DU) z+KimWtWW<1W-7A~ZX|biJ9=#$tTL0EDSx}+)cck0}+P&Hf4g**6Y4^e_(;Hmw zT&8Q#PV#CCI2^uo&-hB_lc$mH)heoPNmLvV>WHelC3B|f0g7=YJ3SEE$53hiL#*P+ zcd%ni`i^RXBQs5_6}MCEL`n4(OkG0yLXt zF#Jw!mY`~*DNFgCHkP==j_*|chnMJ&GM$r@rAHo&b6vhmzdvtnSBcn87rV9zhd=3J z_kK?agUpdI>+18;li5R+$=5bny{jAR4opwgr#px$7{M&CCgBJRrAus!2n8ymNf%0b zQd>FD3ExEXdhuprvuei|%0>RA5ST3#Oh-QE!cI{B=plXQshf5SZ~cJf)E9rzd;Fc# zwg-P0HCo>NBFdxpmk2<@pX=unDV*7mJ)}k<`gx6=;1BB`z(ef4<8ny$0RPB(v`#F5 z{}f_`2C}OjoKsS$u2G?;vI-qLNb)kyX1dd?SOsQT`0($`D?!8L+nvM`hlT;l!5v<) z?zo`emYg>9v1;@44qm^o>U}(Yq8v}I7^$i*`dGTWIfivHqN};|bgH zA%A8K7=G96K}1tDx@v?6K+W!QDo=Za?SsnarW!_fM&Ms(cUnf4m zGhX#iL^lCA!<$tAt0qm&bv74vln9}?W&vazJ5#X9IINC+p4QgsCC}lR=rb10g3k}_ z>HAs67&?qc-5LW``bHf4GZBC*>zzLJBWS2t2&vU~Da2&Bc80Vn@-hUmG*jfZTR|Ib7KP=r$_3ZIhfC&~_={h;$G3kPzGCkFl5 zF&=Qnx~q5qznhM@<|QCWcq)lPc1n&epyYU2H8s%|^B`TDzp6~DwGuP(J9JBU` z55e3wtt+N+>r7DwODprv5m1*XqBff+MTgMjGq2=H$ywa8U5bkZuNBWms{??l(pWUu zzJ;+bsVS)WYcGSY=O;dV=B!NQd3R>cw29 z8~g=~Pq?Kl=+{a0x&3ADWu{YIJvmpd(qik3YQ#MW`MOoRhPk~%*ETPwt+e?(3NkPm zer^^;S}Y}}McfkhAD6d^9i&;N?Uo^fv^jW$qt;y%E=5^3Cx^l?F;K=-Z!o=K~(_lH{ZTrp3`OD|3GYz!d@>;@X8KKt~8Ig2{ z;eJoIycD{RD7hLau>I^}jes?V6j{N;N<6{lbWL_CA5craZ?%yL@O(#(2v3c!F z;?){dA9(!JFh4uzN152%8{VM2=>cM$1rNL_RIXqKSB$sE`{YACI~Fl_H?41M22epHDj;HO)KxwJF7){_J;=Ap zbuX>2-R!-n4qU^0x)vMu$lFUZznqx))5GpW=LggwxZH+M|Cd7Mqo>cb+>zE^&o9!D zi{;d;8}8OVtll>5G>#c21>KAz{vgiQa#%QRnp4V<|Saykl*8bPR5^1L5!tN#oBS4S`bDgXSg z=v#qrSzbj{TjBW@#<$17+j zkEdLusVYM{PC>|((83(HrTyfB(=56VeSrGt(MmJWg3w*Co;s0s>*w7oi@=+W`b zBBZE&a;T6_TlqWI$};DLw~8k-d5HA8tRvEf=E<7NE%sbaU1y9iEa|M46Z^y_*Yetj z6r3&PX9j43u|{`!jQ!KcUJ7L(cCS0?A!KqBZYK-#oIg6eZ{z4~|xiW8dYDoROdJ$O`&Fbrm zV~6M%y-e*+)e6ifds0v-a~~cuG=@xkV~W6;q4uwPFwa-(4UIpFb#{7l&*O*l@mJ0` zkU}M_-rM_J%@;-y9vdbw?7<8htr&)AHdK&??*Q|(TZWZE-!s$u#&v*QO#4x2Tqs2ilb(Vh3jC?i00FQR>mMxfxNsT38+JTmAPdV{!tb99Jb}6y!Oq1 z$v;}_J`h{ojJ#}^;v3xzP&JGl?SI|k0bhQc`<|$ z*gF)?ERXXN?wX`@RWP5Z$y{enxIM)~b7>D=l3?yMcQNbf&d9dKqH_BtQMDhDh>noV zW!5t|-gE-oCji(*W8g+p%Q>8hGgDeId@A=|2;T;Uo{{g@ig?vu$WF`p12B8Io_6|zb4Dqg&7j)3* z%snw%m?y1g)!jCSf$KE>bq(ieaF{*sh$cYO8H(>0jWYeVar}lsR<9_3HT?)+#dfL^ zH}BoJmuUCin7{l$MHIZpM{lL=wN#~OdR`KNaPB7?X*Qm%I}C@LUk=onM2aPiU6Z!1 zjeL5&`@|9Ws|^T0I8IaPciH?qWIX5oo*SOSwr+3{x_tNKiPxxnpL|S!GrQPoto?cb zJZah7YF5O^`w6WM+7qSzw0N`ujJw)l(PU0j-7V`a7AcTmlRbZL_fdb?7pmB>U6dIN zjj%g2WC&>}E=lq*FAP_qw7=dy@LPaU5T_4PdlR4s7Y%B{1C}K{?hIRo#CM+ZK&yso z2@;3zZa{?LmxkUC*&MshOerfTsdCFUH=x*X>Dj6Y$vnHbc`zVYlKfR)^Zc1|~N z+I`gII^TQt*0IEO5G-*LzuaBy)*~|PT zymNg2)wKV&2nK|0yXfWUpGQ0+k{Jujy^HKymd^uUdp6PC2P^@3pq62dQC1%;XZtrA zp!{S1mcvVbU8iBV&hGN3?E^Tfk?2jD-4)>_;wsv6vU|P;HAt6a$`z4D3cShKDo3l~ zxB;<#OU6RUK7pZd9oob&eaq)Z1+@MOX2LBBzt*hbfE33_S%E|i*WDmD={D8O5lS4@ zlw@wfoJK;;fN!Fre#w9J%4$lSS=EtA-t`c5TTGa6+}}-SmfDF` z|KLj!$b6Jqh&9`~!mLUdZpFb`MMa#X9`L+akm`r|hmXQ)BEupsOv_UZaIoq{+y@YA z{M(V-vil+^5MJ1o|276RT=%=aQ_MzqXMUNF?K*q<@zDN@eE6n@9^y$Z)(p0#v z?QomT&h3-gH)#@?8RL4t54m&+mBA{To=|TFUp- zAYr7}eF-=qZAliJcsSBJ1pNzsEzZ(q_fe~S=EY$ty=^NBC!fR`woyupl17IXck+uSr{7-WVxbPt@zPz01RFqTq= zwKfg{W#Wib%vKFeH%0~&t2Tv2L1hK=-YKj@H59uzc$3DRn#80Cygqe_^LVQxa$jHb z?Glef?4NsWBxjTH1)0ioC6Ku>UC?;)(1}?qsXX8ZO(`N$pREXN!oKPZATa$5YVKzRefB%>n^kh5wra403={S#Kn8G&TUGfd#>PYcud&JA;kg$4Bu{0&1VO zMkGt7;-%v(eb6VvV{}Vv!%yGP1tRtNI~tE>uU5_Sy7Xl8iy3YVN+10#`=bFB=(u6= zJq8y@!GJ);V{@a5V{VB7I|{GDA7gaHEidnHeqZqg|CWO-4K2Jq?voHi>lvwG>?3)=@Ri%Mv|Zj9-)?%6l!|Q)5a%6b*MK#0tA!6GpE@$fy)xc+wA1C5`qe_cYBqWvco7&tCTwuPn>FYwzSoyI)AjD8 zy+#&5l`a@;cmdsuX36^d-RFig+V9^rR)go4(Wi+5%}znErHYSYb4lZ z-%*#*NMTh%i9#+^ta{`g;U1^w?o(k+ie34ofG%~n8Zj)3E#p?n&Vb~5i(q=BZ9<4K=VBfp; zbCIs?AJrz>^uknf{0k9MrEeG<{bvk7V01iEG`hj9H6v#U`>y+NV$v0Yq*NYn(U@WM z9)B9qKx5$Di4vK#HIzM(Zx?;cQNhcaLJ z>rtj@eYWJAKWnX}CmFQ@C%<;ub_fsk99Lfot=Er{8_TNI{?RE*H9Rv|__(HJkwfep9XNx%NRVqTiIM_5nx75`!+cZs7} zPUlkGA}>+$W|S$&$|pw8F6znfnV>xngOVTqk&1keohW=p98?07Th?b8j2UN#)c6Ji z4M6i8X{Xp)2?42G{ky|A;*7(v4+c@XZ5@hxWc{Z{qC*56_X9QLjZ4=^&Y;Y1X+rOC zxarzOR#UBex}7)pE)4bZC`|O%ELtjE5>-+nXg^E57+<2)c@!seU+kQqrvYoOjWsG# z@MzxRpqSS0T`T)Bquv3ub0^**5d_U5pm){^*{Jm%?&(jH$j+fQ^63>4FMF}~{iZ;a z>9mFIRiwe}3K;pHp7rRQ%g>`sGds&OZ*gw$+486@y(h%@o!tcS06>Q&4B3+*Y^Nxg zNPhEO*rT3Fu`jG0zESCZIGxLhR(L+JL_*1!ZqBsy3f4*qyNxC zNSR1Lwj=e5RQHH-GIn^;I+D+9yX^leg2$Mh+lZ&OARHZ8pL( z{RXK~I=U-c3Vc_s-v>&AbeM1^^OMW=B}jw$T)_8H$@PG z9mGW(qO-5mF+76pjRT}}(g)?e^#ns+qg+4=PyO#GtL6}sEYuT~43Yu)l2fZ%d!J0) z|4?#lAR&-`AB~Bwh2^U};21mvVdD|uYCBLhAdX_~?nBbI17PcqE)?`ym83)Zp75$L<$TaVCxos+-&h zc<|yqR=e!c0nfUt!`p#gQ&Estcs$5*W?2EYqvhC)1k&kKV)Qk~8}FK*TfKey(CJg% z73he3(4et9vso`cqS`+C6m&HGa?I5zIQqf84KpggP~@2xkX*Gt&8h{HAnCI6B9rS; z=YGU+WV_d5>*tmbZbrA$qMW={3!@It& zq^VWkKX0F}rpJ}7Kk@aokBVk9e>juQpe{&@_&3!FUM)xJ38%_ zO)3X#;5|u*+=P-5;@?F z*-|GCBhmkp?*A7*9gu=A5OKDY!9HivCH!gz%HTJJZ5-VNeBhJ~UE7GT5uc<-=+&Uw zV1d%*&j4xP0Ek26HUgG@m(Xu6eq4lJ;=_IvxUe0#ejNm2#vH46yxh$#sPd*q*p@SK z@rl|*&*<_JbI~YUD)s@)OTg`6G>2D6v4p`AZo7PcXwG_j<%SyZzyLJXNG{{ETAG1| zqv0h(Qgok1v_ti3FK;z!VgVj?;P$%h>9D4=j)u{Etc2h)$^uNoEWmVUuWZAzeQNNT zL6q<>pkAJ%JSP_DG^F(ro#HD<;;dEOcGzZiyVc~lWibFC7*E|J>r{5v@>yl_KCp|a zi~?Fqc)*ay?2`3WVOLw#>XB0rKvyV5CKaOY+qeUHf)DHhn?u8f((wKBM)G5J4 z{6E7R?s4JjH| zqkdl&=q)duxbmXV*Qs{^=&D=@pHjcB*N~rpcpL{}tu?J3s2wuC*LNxar6z{)Qrx-8 z)4^K3M&W4>tklcfZnU-jW7CuNdi7|7wCRuQlClGoEF!=x zAm-%Sa3)rb>NJX=GMy}<1E#2YD?FJ|btY{g=Olrm!mmyM%K}FA-3A{yg)GQ)$>{ID zD8JunR^S1dV2oI&9)_6a74z;)CM zHHNQ=!rNXLCqNcgvvy4EJWwLZ$I(T10WWM$4T^cJ4x{J?yN6bi7&S&Mw<`Ng!m71C zqvdD9rJKd;7d`-;YCy$q=u+hxXX0MB-8E+K4*@`G)$OS&_e%<;oxA?XDBjUNU_`wp zUmg|BrvwELVPtNm>^a|)$d&j36ZIec`UrEUVEKzH5wPZiZng_=54n7`&9Lj{19)Zy zps)@*x6o4VJAw|c`|Ng-->*v$u9Icn!WY%r3y9e35!>zpKrbkCk1Qw%#L%hF^Kh~F zz0t*|@ms08C>IbK@wI94x3U^&(k!XP1fXUIq!yr)pH51}@DmnG7y)zUAEpfMtXoE80HB0%EvsdjXd0= zF29k}`Sv~Glfy9;&y(AY;_Lt8rmkoQ0*@q$>+ib7b*O=HCgQ3aJOTL+>pjQ_Pz}xNo*mGVFA9oapKZc4N0g}! zP5N@x)sgH7N(_v`dzG+oxWp@CI+nLo39|QSDZNZ-R0>arS#l;`qZe{~4)a#Mxa*wz z=GZH?cPk;?+WDPrfWGd(VZ2rVkn_lA%NG?x{og{ydE^R###!CA=%HYIT=TY>7b+ih zNSZ=_y1J1qEks7J_;g^DOIjYkqltG-4EU8#dXKdsWY$w(_@`0Nw=kS8R(x;9q z4jkVb)-+eADVXT?>TA(>uBzq5lTRdarcLfhK=Sa;bE}k*v7L-__kr|5g^;|5za)0l z4r0*%rFIZJ+^)$#KYs+UTkOoE_piRc5BzC$3woM*vt2@JN9KUt3D4Kq;n{wP*yZ!l z9GOZ9y2CR85S~EK;dbwVP!zlecx%N9sSIGh_6_f|k!CGvRM(9)-pGfVLh?&F(=~PV zd3p^RSd)`%ozd{zwsYEU^PT@~y*L37bF%4$Abg9$54IOsEs%oA35Fi~?{bz^7WiuR z;_dG|3Q}hm)+?g_BA5Rw^Yf5Yp3gc}j@uwWZ%QeDH?ijhi}FeR3vE-gWQSwkk7D<5 z4@Ht$fTptR^AKYR&&I4Pp{$yH7X8gnzS=5np&{T=I8eE|$Ce6Un*h|)>h+vHg2sB# z6cV<1yBQ#YdY_Bv<8{`rl{xf#T{yJVe$UZn|Ip-?(}KQ{a@}g*GaExTSA+|0G~PC- zJd(%pnHf>&x%Ro~T@m7c(l`JHX#(C1Cf~R;i_o%t?i8&xSCqceafa3o8;>`)n}*vBi3ei0uR)8vp^C1toqa;U9qz4dL&-+(B&7 z!hu+M;jxf%D>IcN^f}z9`@3J=_TRCOya|7ECEg?9vpxbUHpU;|qakP|T*LK8w@~rl zx`jIm8LkuPe)7yuE=7JndGckUgs|l1e5{F6XoUCjj^$nD2ajB&YVhh#;C4$!6=Ul5 z!WnZvd!ujcm4pu7qxrqd?lzk3jJVt+g`hh;`)la*eq*Hp7v?>*4x4&;pubJMT-c^w zkl_msbN;WliIl@0@}II6N7uqa`OWIK6`kvmb>*q<8@sxq4g7GC<-vARQY=-@zri-& zheoP2u0xOaO2B{A;9qEQooZ zZIK+lfw}zPGLUBtUd*NgRn?C{!Hj*c6^`X1gSImAugMk`&b?b29k*Qv;-x4I%#b)J_x$pwI=Z8^df&U_zmVh1K z;}uZ2@kM)=ZHe!(K7i>dg6y4v<8hdVGh10M{o@g^RkRrDWmY_D%XlW+@v*rr$+G@8 z-E-j!+Pj}wfgZy2!#qXmD;q+$%uB`#xMdUiL7wxpu*WrL^QZ#a2ViuO)V0mUd-(DT z%s$h87-g7FWn5jx+vyBZ^lA{5vju>#w4rhcwhp`l3Mc?<7-td8SN8OQ7oPZ8&6YjR zs8mSd=4OS4py(;}8G)o%gs&A|#784Fj7-Q{Qq>R_^!diw3L5NuF&&EO-dG0~MEW>1 zgJ1$L6~=8WOAZG)H21Ws%B{2Y-=N6mTjqHTJIn=739l{tYQHAYvE@o#bP+Z85u$HR z3q8-gWw$s!kE!?~aKw&VFo$Ya0^v*yTBrZ?z8b7>oq4=u+D66rm~qS5d@{_ug@U;4mCZ}1 zUY@&};uNV!-?$c=gD%k;gNpx*n|~ql;rrZ=S9xeg$)5B0MTk_JJuSEMe4{H%*VKMv zQ9$Z@C&{jc`AQHzQX5X&YF&M>JQS{(?}_m#fYAijHNK>w*M8v1`QIC^pjM8SL2B*d z5iWGAGWlx*Qe9TeeAgeubZC|CJjFdv;EG*5#G@=*fw!9YR_O?#Zk<}YLOENFOZ3L2 z_|v?^bocxqkBtz{N}8|wRF8BYYQVrpN9Km+lS>2=`6WN}P` z4#!p1xIU@hhEW~jF4a_;hNJijabr_2`_&MN05M4Ri!4C-qdhQCp=i8%L+B+$yA2sN zNuKGrKQtq?h^`%v0m-&AuF@3|s-u{25scPs=~3n3dl7O3t)t{r>SL+O&UlkNK;cod zl@*6dqHaDH$3;)U>&Ka` zE{}hGpu*a@R=)znH6LBj4_aBaA!gv#alxMTeNF8WYAF?w&)!}+j<&4-?lFI(yz5}5 z#O7!8S9h34HE9oJeiteK8jV&%QvE~}i&1g&G9Ewe0EimVK}_Ybk6&?EHTfH`1R6~o zKhsy|jhH_}YyWd;Rx7lVL9QK7^_LwcEo{+Z==`J_v8Sc7pUiL+`dn zrKLIn8S?iK>mOWUC$L7Oj465 z@`8qoT&zs&jIRMM5|7x*PSGw@wk_$YVc0|#Bxt*>XI_WC)9Il+In%HyP_*@U=#0Z! z-RU9$aAAWm{^~re6=ulH{73ENP!kX-`92B*ZsLZ%lrNQgvbOavF$txdH~R(ni{L6m6Qn~p{8XP6d9ex zcC~4B&X~^hIE>Q$^3(_WG_jeB$K{CwE7P=@NgD$Rjs>CK<-r9Q<+SiX_nPc%dtpRr z(^OyI#$B8tKkZN7sqHb)npXaIzDz*SRP&M0#Bkp1XI5sAa7mUzOQy^Mnl($DFjX;J z^7?oVtywv-OZ*QNw!f;|Pq_11v4lx~4_O!cLELL+CiK|F1JJDH@Ny-x(v4Y8qYvsE zCp1Vx#6=9J#C}otb{_X`2Sx^YchEa7_1j7h4LT0%%q6PV(HLJ8?!^`0n8#hjVbJQ- z5K$pK`~53Z5{HYMzX*Y}XK`XZ7gyQ6eN$b{%2&QZmD0+s#*_D>$CeJp#F?Y+89+Fz z#Yym8uq`E>pV=I3_3M`aaUcK<$aja zu9cabRUPxPpNiz1tXv5_stJ2mf8_>=nIM0<1nYi7CXIE zumFfA*I96{W^wg9Sz*7)9Wq0Kz8cg@{wX+~2gTihQ*#yr;{3+12XltoSC*MqmWj_6 z^U|1RZE)<a zl*gPd%fjl81;731TG1)i+vVX>XYb6SvJWCpJ)X=Fx}8Ho?F^cGc(S#esDPO!_CUsN z*i_(p=;;A=L&(ddI3UNPRoCAr+K5#i`MNAlmuTu-^m2Nn{JUZQ6>;1mI0K$3rTI3^ zJ9zX}H8+XIkRVV=t9E}PBxp^#xo}n{4{JpGV zI78{w4X???Vl5&W{+WdPw(>ayex(B1zoM3aJ>9gBKAtRw9+<~L4nS@yPw!zW`uC9!!Q&6IOe#6k zcu}12wkyO-MVd|XgQ6Ti#}InVqN;M!JtAHdZ7S*8xM-6O!+VFXT7Ov)iJXn3?ii?D z`Sa+{+eYle2z1~uP;e)xc{ zR55>tn3Rni`L=}e15PDrAn|fiv-A5I`o58V*)&>EV=Sb6o24*g&ic z{z&=No}NS^keHaf(v1tK`}8`{k0Q?cFz{w4<6df?ta@8H27qA~pH!T{{aDGq%gL+I z(z;9mpwEwAk>vUG3U=mrGcNYrns!ydYI(j;MkdrMQ#}z?0sDw&p@1)_8JK?V-C!0> zz1ndt!ITR7m^6=zH%I2p!^AjBb`T2|{wkAQooLQExje6~g;uR*2&ExwcILoVt&*wq zEd4^b+cCJ7)d_PXS;;=8Gb@vX>%pUu>1Hg<2q$*#Pq{&KE^dVWjD^`98K&G0JmFdn zjKhD?c(wTYAgx@v{eZIYqBw5hUFF2NOFglIi6r3#63 zEWQ|-qc}xl4n+2ZXjuUay%wAPIhV7)g1shgP0iv|azZ4msjylOa%CXm5Y8XEdTL^7 zuGs~|+_2u?q|$Uob@f-iJ0QPLbFO{~Db1E6;>2o8U?HXX&-Xu+BhU-?g-q zrrb3VnM*Ja^t-sYL7KufzYzCct%9<(z4*m!k74Y$#qr87vV5m(Bccg|@j2*+i8JNkl6&n|>pbbEcy`ASE|yC7 zu4RA0Aur`cjil1W!%DV%U|$ zxUza0&JO7#DdoWmJ#3EE;PZ{Ssq4yD;ST$<9U+&dkQLBh0=J2S8LzsPDI_{VAPDQX zEW_Rdkoh~XFH6-7flN-|J;oLXE3aBw?*S+)ILg^(TXS!j$y$jEE<;)nj*FsDx7fV? zoW;=glIdORDyx8{vz3x0pbpF#(doXBwR4{iHm5tfkFx#7oEn0SleA7HGAaXCoz`y0 zcJ5!cica$A;nc7eSr_OLvq-3&zGgn+B$ZQZJ&zu}&$ze42B8Jyrd)~{7vfBCsliBt zdQMeyH4fUkL{!mILJL*whfI;?=RJ{`oxd-0&W*5Ehe|RyREF1fJv`83LG*Vh1@6d! zzH_@&-U<1xuU1$I(~(}tR7P254pvfEJZ@qW)wN71Fw(*1i-~UBIgf1_YTfd72cxT) z1beVnr~VRz7iY=*9-NDx;SW!)eRxW~(LRawMMnDl1(>oC2zVut0KdtQZ0vn-W2)d$ z-{+sK^VpMk5M8{j-5i0xSCw5dbmRf0^s+7pQ>V?%h!QkA?r*{_I%t0Q`i?Mn6 z^VMl)=X9RxJW&aW)LLbq$$8Q>au3YxL0RtV+3vZzeNqvrp8K8a^{&et|XSM|)i?oCI5oWlEaUY8>?@68bz%;Z}%VgudF z?E!hdGiwC)BSb3*m50S$LZp*`2cb0zJ`}~F4?T4i7dlm*BWzB zam2+fZP?fPn!a2Mgp!>hzM`8j=)ikI{hV#q)11QR>AzTxYp$eO-o8BE4VACsB(mAF zqpVM;1eJ3Y2OyN(`T4XII!>!BkBUQRl4%i3^Z`yV!F*%M$dZ0lBI6mi_RjisO zCpg}2A4A{PX93XSE$b5-LJHsklU}`A^>c&cS($Cfa=23SMGq$IE)hR&3C@{nS@Lt* zBem>v`#t(|;R#fcW|O-wf4VZ=03tjar?FYuV^2W->YoJ8t|-EOWRVOD#n676WRl7S z<8k(5B%tFIO7ZKmuB|aib5Q5Af%+UStFuYdVmz5dGZvb zX?8)<6P8t4e{%V7G)@MaM7>S|_gbExC^z6p*W+>BjDl5qy#;Chi8*ut>F|1)SqV$M z&v_|(uTz}GX{GNw9^ejqMoT2JG8T6a3lamI4ky8Atr0g4c?oboVx{QDB(bkp$8>n} zZC2W|juH0B?Z|~Gkt(eIG#_~!#n2{&=)w!-&wd=!%b4UI{qCW-ocyo znH?WE=TGluo&>mel!dqlzGqc^LivzC2jPC|@a(8EyqsfY! z3XM`=U$}%{m(;NU&<)!l;{t_brbko0q=Pt^jm*!mVhr2CP z$QhU0*WIs-VUP^t9CRAD+>gJ(CH#Q8>VXr&i!OhNx;*WX=OUFrr4PUb>JqskH*j{w zo3;C`W4@g0VdLoZvedk5Tu$x!y?`j5UiblaWgObYw?`g1>~O@j5x-MQC~Lsr+LsTd%vSyg@xIy)QYYyN#Zwu-!9| z)Va3c+z-vM`E`Y)hs64N^Udz~pD>0jfNNnUW+3sEoB`q^<-p;#>>cU_hrRV!Zk$yH z7r79?eT1#aY2j6X-n^KS9XC6_`CT>arS<;Evy=*v9NXd74yMZWsuV6d^p<-?wu>i+ zrM75wzc|j%mkD`fAWl8c{TK0a-0cuI5sYD#)77kQmZ%Im7jxe3x z?l~b_96<035W>CvIX!oe&gmAw8Z|s{y57l#- zEM~wtcjR4g>q@=5An-TrO@t9RHaH$>_&~F3Xrv7usjDD;1W|$?VP3}*q!9#hX(g84 zJ0!yD9sSy~ZF?`HdZw?FbTvP+3rdCZeUnl{pXEL{rflTar^3EAK00`Mli0?1gRSu|donU~9(`Nt z`DvO<>9PooG*cXnLG5eA+Fp0%EZIE3bv%JzeKyER3XM!07ZE`+$TQrUi?6j7?ZBw~<9 zal71WocnTt@%6akAzQ%78c@y@nAx4fidZb|ym|vXcit73AWk%6;S&kSFH4#EOH<1n zJjRhbndV{*;p0R8)UBbHaI!wC(T?SC#7y{7|xD z_ECD{`piIP3E_5vf-3`K4jCpgiYusOF5c$7kF1d1HXblr<)a=7bsfBb_Sfuh`^r;$ zX-ek3a*Ctn(oftaUF%yH#_$Z!K|mb+HX!u@?y zz*hi#q_vZwmkR4rOtY`v0QGAqYW2rz0$o_3(?sS;dH@40|JoCFvYgYZrVpv};6!PY zJ($EdETwVvd>0gM66{Q{QRk2)gK?E>J=UdNM?M=1Cnk-fnjO`0h6k- z0%0=?7KF?)!FMj>5`u-e``=r$bYr}nl}n`UHF;rc!>ZZ$sh6<)OcxG%2)Aw-C4)Ch z$qNdEwqR02TjtkD&2#g+BkR5-g|>K)6FQCb2cS&O1pDZE^<)<)YKM7sqEY-#S5~*2 zio@nV=ArT){#;x~=ka|X{!gX94{0mn{&Q;*C;hyJRBMo*H!I_J(7(Q*`&EK5@_7gL zhF;`;OZ%m%g%ZO9zF)%`-*L7WvAp9iG`Sq1!rjyNT*g<3_%^Lb-mf&R6 zl1qoCbC~ek9d$)bIP{ynB-nmPJtk$rcy*2P*493rgc}@}$xkkX5hDPT*L`18<`3CK z+k?IgVeNwtUhNdlFMYgRoI&jN^=G*eki87IU=A4Xdl?W)j`3He3fe#(GKD%j1D2Kl zVE91eV-w8wW>-xncptuoHHXwgSykZ#Y3d;uN;x+U?Gz?p{Gz8Oa7Ti_>dQEfkb9g@ zQ~9*#8y~&mu{V``9nO1N=dd#uyCx?mNY@UR*Uu@CgHVSq<)&15c&1Q&PG9uYIXFp5 zl8Tdo&)&6oC>+~4MeCeou-Zr>hK1}L8myje>d6~mEhyM7>E=^+pkWd+FI+de7I?uw zs_wLYN4S(OP;h!wuGexNrynf!QUQin!W)L4vtT*D%v>5CKg!yNHmvOYi@kZF57)S@ zI^&c>S^LRekh!j@+v5iHvrhPA%CTnljw=payft8&BM^Wax?{NGzWa~C-}}mwn!g)v zj=QFUm{s)FciuxnyF#fL0`EP0e$sAykUs8h}MJ1D>ZO^Ru65Jg(Aall^;47(1-wL0q19r5&oO4Q1V+F;hyECh@w zsL~hIxC`@zCU`{5h+FqJM;ui)=!Rr4y)KQ1S){yvTN!hiiHTKjZ8-9elBxT?#@ZrIYO?gS}e}qA0Fl zSU@Oy)nnY}Wj~@!h=rJ-tHX)$lUFu=XhitG`TaQwcH&-H$0HJ?_t)5NN!~JklP}=t zsne^FKs@zWXf`Lx9#_wKf3Om;O@54ME{EA7|2*Dyl7ef>h;Cal6qy^UP?}UXWOfck zxD%&k(<L)`!Rei@)dRk{st z$L$tEWi)N;bauC5gjldu6L$i0G0ZJzYrmFMD0R zEAsnQT9DALxh|eR+YE$S!eE}ObJ%<5#Na4R_5}GAFrS*7w$FIn7;q``=v_GZ@_lw$ zIekI1bgK%jE6cv{>*F)VaS{J+hWV}oq+IeIjkzz*I02}ReNp4C!AiOsBg&6plnTq` z+@0YXV8&gp0apLZ03Tx|;C<9p<*^VwvC;Z;#1JrOgTSEKEVH@8>jzNQ)$XETUwgQA zW$I7JHxZ&;)0s)-;^2*E2JMiBBS@y2bmx&43bdIK*neFl)@&7Vre}o7C^ufKP+so* zTDfHNeI`uC9Faes$Fg)qQ%m1tjhn)XgE@)}95D;}?p;EwDF;6X?S+<)i{Jyu8A+OP z*Bpte4z*X&uS&1_2DHrR@+npDY;NGP)eJrQz)j8|4OY&6#XBP&mepwM83`{t^)nt8 z;v9O3R-|T@9-}&Dr`gbwK~?-QAdWtQM%aGzNO%=Cy`)cFOn7r@bX$UFUm;-7J6v%e zuVpG|xs+qh3Aa*UeU{L|QM!ZsbrQ@C$?z#ehA)cIv&>Od9)*wqTxWdp)r-|NnJtUo z9P_L<=!kMe7ajpS`~H8ovwTh5bvs00Y&N~}ow{l829dEj@IZ-CW&A|O`FYGcY%oXZ zo5*m;YDrka4tCIrEGUUZjowSfj_BVEW zMJE7{ajE)1yC2t>tBcF;!R=)#_uW4&+$v{tLMbiQPQcM0P8~N`ZK_)BgMl)HqcU>y zRaaA3F0L2BGqG#|GW14jKbI8y-b_`vSo+DW2GQ-l37eGfE<>h2IlcX1(>@zEsG_cI zvcDlr|3x)t|IB+y7{aH))3|Dq$B)lsO=S>VdQ3dfL}WEs4{?U`C-#?l;t881>u$vH zzI}#($IrE$UXS!u6Z&FLsgm2Ko4xa6(Uqy<9*$+{@7VZ2OAIH4X97e|f;7)W=c`kV zk{NfiAcK=)2a<-4m?FQ<<33Pi@;?q74<=-narRex!TcssGPB(Rm0W6k(lRkS`Z0~V z!P;5!Z6(s&6_UvgAWzU>B#2u`exIJ%wj@(RZ;^D*HP5xRisGA5l#9%-n0q+l4EaWq zx5SP*`#MBTFV_NtLX7A7_N%KOwyboPM}^v~Z&b z0K`|{69QtW#G=Pbz@VQ#o?f!{Elji0!+5ph8(Wcnh@h-ym`&MFx1u9GIW_L-F-Mp3 zUv~X`#4Z>>79z(glt3u%o;XBKZ`68XKm9~StGbjQ(15}zWi#xH@ReEqym^^yP^gFi*?RUOI*y0%{`~TC>5UB?)qmPHS|W<^}gE2J4bGe5Zbiq4NDhcc)fB zFKJEoICwnRCR{cYyCh<*y4zc%JHSh-ny&3M4_&S4i6`uf>k7I)Y^L6T5u(sVxfY$T z&O}vs5l32_Mh+ZizYTxj`33ws_JC-QiZ9c4cpS5wf$**7D4{FXb*K4`Y>bBi86Zn3 z;p%(zb4F)|$7v%*h}%8Jmm|>i9<@JIN27^t?X7`U_pDr&ORIry#dpi@Kq5smMRSQF zja_tq=mL*M7f=a}d|5^HbHW~s1Z<$@Man*rxo_+UMtkXjcL9daA>?U>CEh$j9N>7$ zF+Dg13P5M#)pU;~rPJrhKiVt;c#C3t9VjhGKXdp-2&5B&p3O=p`vP@#+?hSX#KA1= zhq%e-5il3S+iw(S`aixhRyj!xP~^TNYY9ZZiAix~w9?RWB&v}R~x)MSxJvmWFQ z3(>KaagW_in{pIWxO<0h%2E(;e}+fh;}8EEZZVq2W_3eBl!(DL}z$bcvU)#XV3 zfyfmP5trX5bW*>u3*Mgxd2=@l9F>U?MPq?Qm|@FNU|#6m%tPi8Dc&Jm6``Fxa5-OJ znToIv=b&`Pm}%sX`-d$8u6I`zgcPE;lD!tjH`NIrTM5udIhWlWmjE>@Bhqn(Q;EAi z%a4FhoX=>=CTb~awZdCy&WuV|PR*>XxJ&q6_sr`+6&CCqFFu*7x#Su4{r>yVJm!_P<&(TcL7NNejTu87TfS4g z&v1ZRCxyP2oX|vZ3i7Rm*38d?V&IpPdB2p`_|WD6)c^jC&?SJf>z8>{sykTtQtZqM zMFKFX1dr)3ha7+50Xtj+fiAD&vBoe&@b9g{Mk@g^3mJFG3U^zd6EUU7c%S1Y$vK|~ zULnVJ^T!XdBhX)VxV#FR>^}9WT=`zom!Rrn#7Z~Y)K%O3!{>Q|1bi2O0tGxO%5JXZ z{gHrkVcYp!<-LwShsb+fbI_GKTimEUfiFWqcvRri6Ok^_8FXmaQC;mcT}en_WOI!P zpaCnaF>=>h+2$>J>-I^zo||3V;k)Gd`sFU2gwg0HL8_0FTWX_N-WC_`kJ+7oR@%iJ zO1bkd8nAwKI!(+YJ~YC*Pw!IV4J~=E8g57fx^4mTHUdQbP+$;=uTu}1su4awW%z%C zY^~BB#ShzvsTe7oEKY2jM=SpEWIuMz<-wS{T0KB|=S(4cZ1 zpWgElEOzSyLB7udoN2+wE|O#OUwp{>k(gM~XNfY@%q2f^@6*u8Jqk0$ZLb5uBa7<; zT4U;t=x>oO)*5}5>YWr*sK6Uqwz4|tM?$Z9A&>O;5T8pJm1f3O6lk@lnIUkgyEJ-= z=9)*ROD~u*6@XC4Rp0hTF-gYMALEX-@VP#(*>g>-X$tQ??|TeGTP9@VRic#T>D~h%EfxbR}!W5&~%iRX)H+-RAP&(Bs` z*hl2Y!q0LO*9J{Ht+htVUu;O}4vSN%4|*3$Yc8(~KlBVoYysy4Yk>tAE$g}uLAM(h z){2b!jyBI<>@f)!Vh!xG;F!-rm9^5fOj)MgTwR7--fsDBCBG|jrM2S4RGV@NLszgCZkUk&63Zqkn|M2X`og1F9 z(itY67^X=jz>Z}P>EWgr3YDz=RNhf@pmT;l9-~12f>rx?`1I%%IaFMP;^tWo*mEG% zK&886hn0#t4Ug&Fys}ilxZ(UGgY=Gk>$%q)vIpH=GtG|kJ+D_7TTMaXVe`Cm*IRH3 zAh`rEJ2lqSNYA!c+g^D(TqAB2eqAKE)N`yPrT{oL%zw5vS&YipK75%915HVIl|!D} zpb~DlS?d2&^r6|C4Pe{TEGLVU4ZEdrAH$Tj@BpuUxVJf7{?dz+dpD{uB*edQQ;Hf- z6qI+pnPyYkEkm!LxcZeqUVPbe_}aWJkl*NA?`HCAZdrg?(d8`TDV3-2GAdyq==f#D zdRgf!(bTYDIrwU^#FBJ+@JlN@aiDF{u}ddDExJog>9pQ6_yJ~x)YX=lIBR73js)s0 z&h%6aSTb(V4z&8AYs)`7;gU;v37e(dilmz^diMnLo>z2qWBg?$l+)}lHB!Foo1}jG zcd>_u$A6j8f558$Hs`iyghySj^A@zKf&}3D!!Z}oK=7VR!(y@LB``WuWi(w!qhb&d-UFm9L6eiLZ8*r&)MU6!*>mvRT)XNAud{y!53%|dOy^cwwBfR0LX58=y^6C50zIOdB$uWy>DC_A5zOwN(kUzOCOLTZGOUnMyvP6>GvV=?W0xe7E zl6xb=raD3DUS910j3hsN?QsPu~W*Fy+D^@u8 zb=f(4U0LNJ73k%S>DgVGgKvE!O;h9h>e~(%mD2f*rLtFT`i$u$YWaLisd1VKX^_)f zmVouPh2Od}{%ax&TF*lxeZnsYZrT=V5K_43Qbv-E^9(%v{Vm%qa^{E~2N$9@rs*(Q zOH&n#MhT*bmS0taIumEfXD=7TU9_!4*rt;4H|(}KjRmJ0I!1%TezLFSb*QN;a{6Q+ zvT|;u0)z$u!%O$+_B_kNN4A!T+>_Kibvabewc6a`3UVgOM`(B6+WrCUjIV4h;p*X9 z8iM=^uL;q8R3oY0lR>wunL&XpS1P7c*Y12P5z7D`?rq5-&JeiF|=5AFa4}gza7*04Sm$E zhNpM&-4!oK5RrJ3aP}s-Z#(sgi@so|P`m$4-!+6D%W35+8ty=aO|t#Q6-kGC6_i*g0-}Jy zdsOV?ZXkB({v@hMi~oc13xfWm!<~~pIgOxEDPPF2w2CZ3P$pL6CXM$G4@n-l~$1vKqt_!4LH z6g75>%=P%L2B|o{AEBA&2-I66k>QtBo}9zHs3=wq?%>{~Oi@Rc;BtiQyk9IKopZ{6Gt_}t;AM`J`hD?RRxu^#t?VfWb8 z?THkHKf6Wk1n?z%ilS^j3P zW^V?!6$T>7g7>xbS%API*)Lo?nn#l|?a%Wc za6@5!(OxUw&SO34bE9}wD*_Q7!n>jab3!;uWJV5=bL2`OQT2nSjpVnokNqI83nZng zJXn}7W&fZFu7bnCv!T8>>}=}r(``2S=$PlBO@NPm^z@ERw&D|Fo7bg`Z}t6>GL~FH z3k7;;S&5bb@aVrNuivczLgd9$tr}+i-p~&<2_HoDtrA8C1u*#u=(Wl(RUwO3=tE<8 z)FhK?zIw*~>*RdYUda}LI#1><_xoH~r?y{bmDy$~{Wp>wZ>@BX{S>Qeftz0m>TXAe z#;%0}P&4Sx!lFb8`c3%x=wJwedk6<1tCNi4axLz}zXyeDH$FPls;&3iwkAKf8U7ca zHF|=>iQY((4u{QU)AQD*#Dg<_&xn^qF{i+-W$4iy3Yz0TP3j42gZ2kHeqpnEq(yv z?pQ`wO^Q>_-x9}iJw|u_UE=u3G}GQixqFsBMSv?paC@E$SO!v0fokj1=>u9bMP4IO zWczg2J(5A&R1FAW$fOQBEHUaoca}T|HrL)6GR1_sh$NK>t5yg%872A zX9=E7AH;Frz(cN|$>$cT6Q!v3r}l6A%Bh_>0(#FVWV!+~sE(sDilTho@IE`Ft1^?; zI}5c$z?fQ1ot9Z7AzS+r9TQ^f z3j4zLn=}ko&)IXL4hYo1l{c9l9gThl{GLjwuP-<2^VS#+76`7=eDYJgAhb1ADk zn|4mRv*vS9TzF4L;2u8ExzFxJ^bHZ3DJwwi_dMI|InHZiFt+ER2xReg#qSz8h>GjegfH>zC8 zL7^9hawu-=oy;1vEail(uD0G^AGK*=>b#&{2usn$dz$^F*t0<`%Y?f+eCTW3yrC7m zA~NOk4f^{_CfUt|h}PU8gvRKs!nLa+_#k3*^I^>{x#Gbaxj6Zcx1OsfqP=(vuU@Sv z0m|2)sRGKEnt14i-H*oL34Bf$Dk1x#>wT!a{?oUeD#y!t`JRRQPXMff_|XY`otYYG z5;p@!Q~TN-FYQ+NbO$s#0?kQ}vH}^;;#^dfs0s$u(d;@srYj95OLTQ~W?pW0Yd4{K zmBi*uDl&#;Er$z-dtrT%JB#;BP5yW^K%>MZ0I#XY65q&6+Je5Nue8J3 zxvJTGeA)#duMebPY}~21{Q`D_ zowcep-qgtbv3!PTWMh<_=b50*qq9+tCR_ayXo_W+q`i@{&&h<+1zdm6xr8b1_wqgZ z#K!m<#qABspJ`%8x)qI@MEsO@`H_X7Y3i>-Y6R}$;3NXFm}3erx3xc2i+Fp0sK5h^ zgt6Dy60o;V-C!}cEW>s~N-`+B(Vk~8F!&&*52P(HQ+h3D{9akXk+$Q{(G=WNNg6Tb zE^(3}ql|DEYM!+2d{k4VW`u^6k3Mn~;SQPxgjlMck2%TiJrmaIgpfKa(~lUtZES_^ zoDoPS;$GY#H5+0Fu3QQFlu>F1t+b`ZU|_a%)vmyHU1$wu>_}B1T*(w(R0hvn%)1LS zDC8{H{5}lJQPeYqC#oYRwfwifv!2|?#0VKSH796c{a8Q0t%$LN+bagwt1aK%K^4+v zc}LAFqfF^*2=(#Ln^?wWDUzhg;G21$PaR4@T7+h_C;C#m$&ATcI`SO#D|(YhdQY6R z-1kIjrq|c`$DoUc@(sH?=n=~pvo{W>nF_`56I)vj;{5Q%Z3;9PGW=3CJ7=<*tJBR) z@VDkp+S>x4jP@v`<-E3^X|*}#SQn!dKifwYxeO2dxFSx)M(f#M|M;pZwyh)aF2492 z@bi=gQR&R?C0FKUfZ#>Tciy6%pUfC?MVY%aySf+?RRJ1xvt z+ozLzu)Nou59E2Sym*so(uyujW`_@Hb{J1D*BoW#0M!dqpy9j9Fec!Z1>>vp)$S-% zcJ_OIQPj&1m@;ZBU}Sdq77gB|5SIX;qQ|R4Ov-}g&#_|m-eL{cR0sAX^1ecs1yjkl zGAV4ITO}UG)1MzmI!QY)Bz7ImT$05%mcOeDYBEasdZpt{&Q`%66`jxW(tidsm&Pr- zu_>mU8LCpf0u0?q%L)7uOYBn9f0+>AFL`VEQ#J>9GS$d@mh0)p5RD~@(Qi=rSF35B zN2>DpmZYiLP3X3SHiy?;%7Z?;QT^DnX=EA4lISVMk2C3rqnOQ`5d;+hIshDt$s1-1 z2!1~2M008F#g*0dV?VF-mSK+cA%kzWPm2Z+#9XYR8Te`9&?1?h*4}y$nY-rB*}E~G z?So^LAuo?B=epdk9~!v49xipf<7o%Ma=hppP6l`WTgSqnFhmaGS`OSWv#pY(VC~UGnCMtIMupibm_vEXN1FzqXuzVWlb_VO;7ON z%|jD{hVV?M=u?!9=!XU;&HL6Z(&`wVh1E2eCa^%ibht zjg#}zNyVjo{_hB=jX((7%XNJoHvJmEKs5d0|2S88t}RM7LBpjzbaA{##-@SH5$N~K)Z8i1uxUl(!+F8`Lt$E* z4u2fj0W&7RE9vanhzcLpC|kXZNZL-WE-i}PvhS(4J}>_IeyJ@qa{DGMq42()#36+= zm3FZWX~t}ey_f2&kqyi4*l>%i-|u)+4U}fNB z^39L$`N|YSGwS0XVm+wbHY&s4T-5bvqm-b zBj1`Ydfr^$q;F0L&mdiuQ#E~_ooXyb9J;VsqBpd~02$j~+_8JVTwn7R2j1$~Pl1Cg z(V8x`{&Jamk|n$4MR)?a$(SN7H|4Q`z2VO#!ns5~KWNmYD z#JPez)H3J~4DVfR2^VkPDM2UPc|E#PgyNasA{ldTDk%Kv=o}k~42@g{B~Z}5E7w-? z*>)9f9J~nXJ3HV!fWJ!kEy>-0-hSxPR{dD9$mmWfvBB>I0DmeKA8hh~YPLI-vBpP+ z^&r!whjmn$l=SM$p^@a|p>-w#4cOY2=W#$gS)g9YH}^6hG1i}u7S@pONrOGuUQ z`x6SbQfHQR0reQmbdGA()N2$wlZI^C(6uX;=a{@NzbPdgMXxH7rUKyQAYNtrtv&lw zR@`^F_=m^z64yOvzS-6p{#|08%Vb#%-6L5zbW4=(uKNcX74;^L8u&4J&8Z&VQxB0G z(tI7i z`pxtGtJ&?p8aVyWBStvVYysMwhw$V{?eZP4GzZ;Wd;L8GH(_}h-s5-NU?39oW+I1b z34MJ|tLiKE6K{W|cV`{#_lSzE(@wYp&j4BB3V}mv3|#6g@4K;&1#)O24v~)GJEXj`L3fji zCHIJi3A>VwyABpvF`;M3yK_v@Gm4kiH!oPuBSzlcBH!h#NB>yMOg075uJa)DBBdUB zbucoT|4;Vrgap9D(Hj3q*L@3TY2G}+;O*UqCaplWa-2UN@dq?sNDP0A)exm|GJQ{E zRdd1vTceKMrxB0zZ{A869@(PrGXQjX>2vX01!M1g+w#^U!I{{-$SW}0djZ2W^8L=l zz!8>Tld~jmP~X)$Ad#Xdrqv;y#wcG^Y&Q?pB-XHAaxR?ZvZJ_v9{!i?#ed(9>zDrr z`}A+TbndVI0g(Dn`})_71h^cPzwDpVf3bgm-@k(+6e+ME=kLTY=ibBM!WF$j9s582 E7X=SbSpWb4 literal 0 HcmV?d00001 -- 2.54.0 From 7d254138603a6385147c60ef3077e8c1c22852dc Mon Sep 17 00:00:00 2001 From: Jens Tirsvad Nielsen Date: Tue, 13 Oct 2026 15:34:50 +0800 Subject: [PATCH 2/5] Show the screenshot in the README and finish the Run section Task: MIL-005#2 --- README.md | 345 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 194 insertions(+), 151 deletions(-) diff --git a/README.md b/README.md index 5a7dbdd..50d8123 100644 --- a/README.md +++ b/README.md @@ -1,151 +1,194 @@ -# Weather Data (Day 25) - -Reading CSV weather data in Python, first with the built-in `csv` library and then -with `pandas`. It is the day-25 assignment of Udemy's *100 Days of Code: The -Complete Python Pro Bootcamp*. The lectures read the weather of one week from -`weather_data.csv`, collect the temperatures as whole numbers, and then show how -`pandas` does the same in one line: a table is a `DataFrame` and a column of it is -a `Series`. They convert both to a dictionary and a list, compute the average and -the maximum temperature, select rows by a condition (the row of Monday, the row of -the hottest day, Monday's temperature in Fahrenheit), create a `DataFrame` from a -dictionary, and export it to a CSV file. - -The `csv` part has no runtime dependencies; `pandas` is an optional extra. The code -keeps the names of the lectures (`data`, `temperatures`, `temp_list`, `data_dict`, -`average_temp`, `monday`, `to_dict`, `to_list`, `mean`, `max`, `to_csv`) so that you -can compare it with your own solution. - -> **Status:** in progress. The project foundation (configuration, constants, -> documentation settings and continuous integration) is in place; the readers and -> the analysis follow in the next milestones, see `docs/project-plan.md`. - -## Requirements - -- Python 3.13 or newer. -- `git`, to clone the repository. -- Optional: [Doxygen](https://www.doxygen.nl/) to build the source documentation. -- No runtime dependencies. `pandas` (the optional extra `pandas`) is needed for the - second half of the lectures and is installed by the `dev` extra, together with - the development tools (`pytest`, `ruff`, `mypy`). - -## Set up - -Clone the repository, then create a local virtual environment `.venv` in its -root, upgrade `pip` and install the project with its development tools. The -`.venv` folder is ignored by git. - -```bash -git clone https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/025-weather.git -cd 025-weather -``` - -### Windows powershell - -```powershell -python -m venv .venv -.\.venv\Scripts\Activate.ps1 -python -m pip install --upgrade pip -python -m pip install -e ".[dev]" -``` - -If PowerShell refuses to run the activation script, allow scripts for this -window only with `Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned` -and activate again. - -### Linux debian - -Debian 13 or newer ships Python 3.13. On older releases install Python 3.13 -separately. - -```bash -sudo apt install python3 python3-venv -python3 -m venv .venv -source .venv/bin/activate -python -m pip install --upgrade pip -python -m pip install -e ".[dev]" -``` - -### MacOS - -```bash -brew install python@3.13 -python3.13 -m venv .venv -source .venv/bin/activate -python -m pip install --upgrade pip -python -m pip install -e ".[dev]" -``` - -To install only what the `csv` part needs, leave out the extras: -`python -m pip install -e .`. To add `pandas` without the development tools use -`python -m pip install -e ".[pandas]"`. - -## Run - -With the virtual environment active, the program that prints the results of the -lectures is added in a later milestone (`python -m weather`); until then the -package can be imported: - -```bash -python -c "from weather import constants; print(constants.DATA_FILE)" -``` - -## Run the tests - -The tests write only into temporary folders. - -```bash -pytest -``` - -The same checks that continuous integration runs: - -```bash -python -m pytest -python -m ruff check src tests -python -m ruff format --check src tests -python -m mypy -``` - -## Continuous integration - -`.gitea/workflows/ci.yml` runs on Gitea Actions on every push and on every pull -request. It sets up Python 3.13, upgrades `pip`, installs `.[dev]`, then runs -`pytest`, `ruff` (lint and format check) and `mypy`. It does not build the source -documentation: run `doxygen Doxyfile` yourself, as the next section shows. - -The workflow lives in `.gitea/workflows` and not in `.github/workflows`, so -GitHub does not run it and pushing to a GitHub mirror needs no workflow -permission. - -## Build the source documentation - -The source is documented with Doxygen comments and the `Doxyfile` in the root. -Install Doxygen (`winget install DimitriVanHeesch.Doxygen` on Windows, -`sudo apt install doxygen` on Debian, `brew install doxygen` on macOS), then: - -```bash -doxygen Doxyfile -``` - -The HTML is written to `build/doxygen/index.html`. A warning fails the build. - -## Project layout - -```text -. -├── .gitea/workflows/ci.yml continuous integration (Gitea Actions) -├── docs/ business case, plan, milestones, reviews -├── src/weather/ the package -│ ├── __init__.py -│ └── constants.py every constant of the lectures -├── tests/ pytest tests (conftest.py runs every test in a -│ temporary folder) -├── Doxyfile source documentation settings -├── LICENSE -├── pyproject.toml project configuration -└── README.md -``` - -## License - -GNU Affero General Public License v3.0 only. See [LICENSE](LICENSE). +# Weather Data (Day 25) + +Reading CSV weather data in Python, first with the built-in `csv` library and then +with `pandas`. It is the day-25 assignment of Udemy's *100 Days of Code: The +Complete Python Pro Bootcamp*. The lectures read the weather of one week from +`weather_data.csv`, collect the temperatures as whole numbers, and then show how +`pandas` does the same in one line: a table is a `DataFrame` and a column of it is +a `Series`. They convert both to a dictionary and a list, compute the average and +the maximum temperature, select rows by a condition (the row of Monday, the row of +the hottest day, Monday's temperature in Fahrenheit), create a `DataFrame` from a +dictionary, and export it to a CSV file. + +The `csv` part has no runtime dependencies; `pandas` is an optional extra. The code +keeps the names of the lectures (`data`, `temperatures`, `temp_list`, `data_dict`, +`average_temp`, `monday`, `to_dict`, `to_list`, `mean`, `max`, `to_csv`) so that you +can compare it with your own solution. + +![The program running in a terminal: the rows and temperatures read with the csv library, then the data frame, the dictionary, the list, the average 17.428571428571427, the maximum 24, the row of Monday, the hottest row Sunday, Monday in Fahrenheit 53.6 and the scores frame](docs/images/weather-run.png) + +*`python -m weather` in a terminal: the csv part first, then the pandas part.* + +> **Status:** finished. The `csv` reader, the `pandas` analysis, the creation and +> export of a `DataFrame` and the program that prints the results are done. A short +> description in English, Danish and Bahasa Melayu is on the wiki of the repository +> (the pages are in `wiki/`). + +## Requirements + +- Python 3.13 or newer. +- `git`, to clone the repository. +- Optional: [Doxygen](https://www.doxygen.nl/) to build the source documentation. +- No runtime dependencies. `pandas` (the optional extra `pandas`) is needed for the + second half of the lectures and is installed by the `dev` extra, together with + the development tools (`pytest`, `ruff`, `mypy`). + +## Set up + +Clone the repository, then create a local virtual environment `.venv` in its +root, upgrade `pip` and install the project with its development tools. The +`.venv` folder is ignored by git. + +```bash +git clone https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/025-weather.git +cd 025-weather +``` + +### Windows powershell + +```powershell +python -m venv .venv +.\.venv\Scripts\Activate.ps1 +python -m pip install --upgrade pip +python -m pip install -e ".[dev]" +``` + +If PowerShell refuses to run the activation script, allow scripts for this +window only with `Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned` +and activate again. + +### Linux debian + +Debian 13 or newer ships Python 3.13. On older releases install Python 3.13 +separately. + +```bash +sudo apt install python3 python3-venv +python3 -m venv .venv +source .venv/bin/activate +python -m pip install --upgrade pip +python -m pip install -e ".[dev]" +``` + +### MacOS + +```bash +brew install python@3.13 +python3.13 -m venv .venv +source .venv/bin/activate +python -m pip install --upgrade pip +python -m pip install -e ".[dev]" +``` + +To install only what the `csv` part needs, leave out the extras: +`python -m pip install -e .`. To add `pandas` without the development tools use +`python -m pip install -e ".[pandas]"`. + +## Run + +With the virtual environment active, print the results of the lectures: + +```bash +python -m weather +``` + +The program prints, in the order of the lectures: + +1. the rows of `weather_data.csv` and the temperatures as whole numbers, read with + the `csv` library (no `pandas` needed); +2. the same file read with `pandas` as a `DataFrame`, converted to a dictionary and + a list; +3. the average temperature, computed by hand and with `mean`, and the maximum; +4. the row of Monday, the row of the hottest day, and Monday in Fahrenheit; +5. a `DataFrame` created from a dictionary of students and scores. + +Nothing is written to disk unless you ask for it. To also export the scores table +to a CSV file, give a path: + +```bash +python -m weather --export new_data.csv +``` + +If `pandas` is not installed the `csv` part is still printed, a message names the +extra to install, and the exit code is 1. + +The functions can also be used from your own code: + +```python +from weather.csv_reader import read_temperatures +from weather.analysis import read_data, average_temp, max_temp + +temperatures = read_temperatures() # [12, 14, 15, 14, 21, 22, 24] +data = read_data() # a pandas DataFrame +print(average_temp(data), max_temp(data)) +``` + +The default data file is the `weather_data.csv` inside the package, so the +functions work from any folder; pass a `path` to read another file. + +## Run the tests + +The tests write only into temporary folders. + +```bash +pytest +``` + +The same checks that continuous integration runs: + +```bash +python -m pytest +python -m ruff check src tests +python -m ruff format --check src tests +python -m mypy +``` + +## Continuous integration + +`.gitea/workflows/ci.yml` runs on Gitea Actions on every push and on every pull +request. It sets up Python 3.13, upgrades `pip`, installs `.[dev]`, then runs +`pytest`, `ruff` (lint and format check) and `mypy`. It does not build the source +documentation: run `doxygen Doxyfile` yourself, as the next section shows. + +The workflow lives in `.gitea/workflows` and not in `.github/workflows`, so +GitHub does not run it and pushing to a GitHub mirror needs no workflow +permission. + +## Build the source documentation + +The source is documented with Doxygen comments and the `Doxyfile` in the root. +Install Doxygen (`winget install DimitriVanHeesch.Doxygen` on Windows, +`sudo apt install doxygen` on Debian, `brew install doxygen` on macOS), then: + +```bash +doxygen Doxyfile +``` + +The HTML is written to `build/doxygen/index.html`. A warning fails the build. + +## Project layout + +```text +. +├── .gitea/workflows/ci.yml continuous integration (Gitea Actions) +├── docs/ business case, plan, milestones, reviews, screenshot +├── src/weather/ the package +│ ├── __init__.py +│ ├── __main__.py prints the results: python -m weather +│ ├── analysis.py read_data, average_temp, max_temp, row selection (pandas) +│ ├── constants.py every constant of the lectures +│ ├── csv_reader.py read_rows and read_temperatures (csv library) +│ ├── export.py create_scores_frame and export_frame (pandas) +│ └── weather_data.csv the weather data of the lecture +├── tests/ pytest tests (conftest.py runs every test in a +│ temporary folder) +├── wiki/ wiki pages in English, Danish and Bahasa Melayu +├── Doxyfile source documentation settings +├── LICENSE +├── pyproject.toml project configuration +└── README.md +``` + +## License + +GNU Affero General Public License v3.0 only. See [LICENSE](LICENSE). -- 2.54.0 From 0b49f0ce5f779afcece555ea5b93c035dda72661 Mon Sep 17 00:00:00 2001 From: Jens Tirsvad Nielsen Date: Tue, 13 Oct 2026 15:34:51 +0800 Subject: [PATCH 3/5] Write the wiki in English, Danish and Bahasa Melayu Task: MIL-005#3 --- wiki/.publish | 0 wiki/Home.da.md | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ wiki/Home.md | 47 ++++++++++++++++++++++++++++++++++++++++++++++ wiki/Home.ms.md | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ wiki/_Sidebar.md | 13 +++++++++++++ 5 files changed, 158 insertions(+) create mode 100644 wiki/.publish create mode 100644 wiki/Home.da.md create mode 100644 wiki/Home.md create mode 100644 wiki/Home.ms.md create mode 100644 wiki/_Sidebar.md diff --git a/wiki/.publish b/wiki/.publish new file mode 100644 index 0000000..e69de29 diff --git a/wiki/Home.da.md b/wiki/Home.da.md new file mode 100644 index 0000000..fa8e1c8 --- /dev/null +++ b/wiki/Home.da.md @@ -0,0 +1,49 @@ +> Denne side er genereret fra README.md og wiki/. Ændringer her bliver overskrevet, næste gang wikien udgives. + +> Den engelske side er den gældende; denne oversættelse følger den. + +[English](Home) | Dansk | [Bahasa Melayu](Home.ms) + +# Vejrdata (dag 25) + +At læse CSV-vejrdata i Python. Det er opgaven fra dag 25 i Udemys *100 Days of Code: The Complete Python Pro Bootcamp*. Lektionerne læser en uges vejr fra `weather_data.csv` med Pythons indbyggede `csv`-bibliotek og samler temperaturerne som hele tal, og viser derefter, hvordan biblioteket `pandas` gør det samme på én linje. En tabel er en `DataFrame`, og en kolonne i den er en `Series`; begge kan omdannes til en ordbog og en liste, gennemsnits- og maksimumtemperaturen kan beregnes, rækker kan udvælges ud fra en betingelse, og en `DataFrame` kan oprettes ud fra en ordbog og eksporteres til en CSV-fil. + +![Programmet kører i en terminal: rækkerne og temperaturerne læst med csv-biblioteket, derefter datarammen, ordbogen, listen, gennemsnittet 17,43, maksimum 24, mandagens række, den varmeste række søndag, mandag i Fahrenheit 53,6 og tabellen med point](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/025-weather/raw/branch/main/docs/images/weather-run.png) + +*`python -m weather` i en terminal: først csv-delen, derefter pandas-delen.* + +## Hvad programmet skriver + +| Del | Resultat | +| --- | --- | +| `csv`-biblioteket | Filens rækker og temperaturerne `[12, 14, 15, 14, 21, 22, 24]` | +| `pandas` | `DataFrame`, ordbogen og listen med temperaturer | +| Statistik | Gennemsnit 17,43 (i hånden og med `mean`) og maksimum 24 | +| Rækkeudvælgelse | Mandagens række, den varmeste dags række (søndag), mandag i Fahrenheit (53,6) | +| Opret og eksportér | En `DataFrame` med elever og point lavet ud fra en ordbog; skrives kun til en fil med `--export` | + +## Funktioner + +| Funktion | Modul | Lektion | +| --- | --- | --- | +| `read_rows`, `read_temperatures` | `weather.csv_reader` | Læsning med `csv`-biblioteket | +| `read_data` | `weather.analysis` | `pandas.read_csv` | +| `to_data_dict`, `to_temp_list` | `weather.analysis` | `to_dict`, `to_list` | +| `average_temp`, `average_temp_mean`, `max_temp` | `weather.analysis` | Sum og længde, `mean`, `max` | +| `row_for_day`, `hottest_row`, `day_in_fahrenheit` | `weather.analysis` | Udvælgelse af rækker | +| `create_scores_frame`, `export_frame` | `weather.export` | `DataFrame` ud fra en ordbog, `to_csv` | + +## Kør programmet + +Der skal bruges Python 3.13 eller nyere. `csv`-delen har ingen afhængigheder ved kørsel; `pandas` er et valgfrit tilvalg, som `dev`-tilvalget installerer. + +```bash +git clone https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/025-weather.git +cd 025-weather +python -m venv .venv +python -m pip install --upgrade pip +python -m pip install -e ".[dev]" +python -m weather +``` + +Tilføj `--export new_data.csv` for også at skrive tabellen med point til en fil. Repositoriets README har den fulde opsætning til Windows, Linux og macOS. diff --git a/wiki/Home.md b/wiki/Home.md new file mode 100644 index 0000000..afe420f --- /dev/null +++ b/wiki/Home.md @@ -0,0 +1,47 @@ +> This page is generated from README.md and wiki/. Changes made here are overwritten the next time the wiki is published. + +English | [Dansk](Home.da) | [Bahasa Melayu](Home.ms) + +# Weather Data (Day 25) + +Reading CSV weather data in Python. It is the day-25 assignment of Udemy's *100 Days of Code: The Complete Python Pro Bootcamp*. The lectures read the weather of one week from `weather_data.csv` with Python's built-in `csv` library and collect the temperatures as whole numbers, and then show how the `pandas` library does the same in one line. A table is a `DataFrame` and a column of it is a `Series`; both can be converted to a dictionary and a list, the average and the maximum temperature can be computed, rows can be selected by a condition, and a `DataFrame` can be created from a dictionary and exported to a CSV file. + +![The program running in a terminal: the rows and temperatures read with the csv library, then the data frame, the dictionary, the list, the average 17.43, the maximum 24, the row of Monday, the hottest row Sunday, Monday in Fahrenheit 53.6 and the scores frame](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/025-weather/raw/branch/main/docs/images/weather-run.png) + +*`python -m weather` in a terminal: the csv part first, then the pandas part.* + +## What the program prints + +| Part | Result | +| --- | --- | +| `csv` library | The rows of the file and the temperatures `[12, 14, 15, 14, 21, 22, 24]` | +| `pandas` | The `DataFrame`, the dictionary and the list of temperatures | +| Statistics | Average 17.43 (by hand and with `mean`) and maximum 24 | +| Row selection | The row of Monday, the row of the hottest day (Sunday), Monday in Fahrenheit (53.6) | +| Create and export | A `DataFrame` of students and scores made from a dictionary; written to a file only with `--export` | + +## Functions + +| Function | Module | Lecture | +| --- | --- | --- | +| `read_rows`, `read_temperatures` | `weather.csv_reader` | Reading with the `csv` library | +| `read_data` | `weather.analysis` | `pandas.read_csv` | +| `to_data_dict`, `to_temp_list` | `weather.analysis` | `to_dict`, `to_list` | +| `average_temp`, `average_temp_mean`, `max_temp` | `weather.analysis` | Sum and length, `mean`, `max` | +| `row_for_day`, `hottest_row`, `day_in_fahrenheit` | `weather.analysis` | Selecting rows | +| `create_scores_frame`, `export_frame` | `weather.export` | `DataFrame` from a dictionary, `to_csv` | + +## Run + +Python 3.13 or newer is needed. The `csv` part has no runtime dependencies; `pandas` is an optional extra that the `dev` extra installs. + +```bash +git clone https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/025-weather.git +cd 025-weather +python -m venv .venv +python -m pip install --upgrade pip +python -m pip install -e ".[dev]" +python -m weather +``` + +Add `--export new_data.csv` to also write the scores table to a file. The README of the repository has the full set-up for Windows, Linux and macOS. diff --git a/wiki/Home.ms.md b/wiki/Home.ms.md new file mode 100644 index 0000000..a092b4e --- /dev/null +++ b/wiki/Home.ms.md @@ -0,0 +1,49 @@ +> Halaman ini dijana daripada README.md dan wiki/. Perubahan di sini akan ditimpa apabila wiki diterbitkan lagi. + +> Halaman Bahasa Inggeris adalah yang rasmi; terjemahan ini mengikutinya. + +[English](Home) | [Dansk](Home.da) | Bahasa Melayu + +# Data Cuaca (hari 25) + +Membaca data cuaca CSV dalam Python. Ia ialah tugasan hari ke-25 dalam *100 Days of Code: The Complete Python Pro Bootcamp* di Udemy. Pelajaran membaca cuaca satu minggu daripada `weather_data.csv` dengan pustaka `csv` terbina dalam Python dan mengumpul suhu sebagai nombor bulat, kemudian menunjukkan bagaimana pustaka `pandas` melakukan perkara yang sama dalam satu baris. Satu jadual ialah `DataFrame` dan satu lajur daripadanya ialah `Series`; kedua-duanya boleh ditukar kepada kamus dan senarai, suhu purata dan suhu maksimum boleh dikira, baris boleh dipilih mengikut syarat, dan `DataFrame` boleh dicipta daripada kamus serta dieksport ke fail CSV. + +![Program berjalan dalam terminal: baris dan suhu yang dibaca dengan pustaka csv, kemudian data frame, kamus, senarai, purata 17.43, maksimum 24, baris hari Isnin, baris terpanas hari Ahad, Isnin dalam Fahrenheit 53.6 dan jadual skor](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/025-weather/raw/branch/main/docs/images/weather-run.png) + +*`python -m weather` dalam terminal: bahagian csv dahulu, kemudian bahagian pandas.* + +## Apa yang dipaparkan oleh program + +| Bahagian | Hasil | +| --- | --- | +| Pustaka `csv` | Baris fail dan suhu `[12, 14, 15, 14, 21, 22, 24]` | +| `pandas` | `DataFrame`, kamus dan senarai suhu | +| Statistik | Purata 17.43 (secara manual dan dengan `mean`) dan maksimum 24 | +| Pemilihan baris | Baris hari Isnin, baris hari terpanas (Ahad), Isnin dalam Fahrenheit (53.6) | +| Cipta dan eksport | `DataFrame` pelajar dan skor yang dibuat daripada kamus; ditulis ke fail hanya dengan `--export` | + +## Fungsi + +| Fungsi | Modul | Pelajaran | +| --- | --- | --- | +| `read_rows`, `read_temperatures` | `weather.csv_reader` | Membaca dengan pustaka `csv` | +| `read_data` | `weather.analysis` | `pandas.read_csv` | +| `to_data_dict`, `to_temp_list` | `weather.analysis` | `to_dict`, `to_list` | +| `average_temp`, `average_temp_mean`, `max_temp` | `weather.analysis` | Jumlah dan panjang, `mean`, `max` | +| `row_for_day`, `hottest_row`, `day_in_fahrenheit` | `weather.analysis` | Memilih baris | +| `create_scores_frame`, `export_frame` | `weather.export` | `DataFrame` daripada kamus, `to_csv` | + +## Jalankan + +Python 3.13 atau lebih baharu diperlukan. Bahagian `csv` tiada kebergantungan semasa dijalankan; `pandas` ialah tambahan pilihan yang dipasang oleh tambahan `dev`. + +```bash +git clone https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/025-weather.git +cd 025-weather +python -m venv .venv +python -m pip install --upgrade pip +python -m pip install -e ".[dev]" +python -m weather +``` + +Tambah `--export new_data.csv` untuk turut menulis jadual skor ke fail. README repositori mempunyai persediaan penuh untuk Windows, Linux dan macOS. diff --git a/wiki/_Sidebar.md b/wiki/_Sidebar.md new file mode 100644 index 0000000..bda448c --- /dev/null +++ b/wiki/_Sidebar.md @@ -0,0 +1,13 @@ +**Weather Data (Day 25)** + +English + +- [Home](Home) + +Dansk + +- [Hjem](Home.da) + +Bahasa Melayu + +- [Utama](Home.ms) -- 2.54.0 From deaed3899c379d04d6df00d616086f46aefea029 Mon Sep 17 00:00:00 2001 From: Jens Tirsvad Nielsen Date: Tue, 13 Oct 2026 15:37:09 +0800 Subject: [PATCH 4/5] Record the review of the MIL-005 deliverables (RC-013) --- docs/artifact-registry.md | 2 +- docs/sqa/reviews/rc-013-mil-005-code.md | 60 +++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 docs/sqa/reviews/rc-013-mil-005-code.md diff --git a/docs/artifact-registry.md b/docs/artifact-registry.md index e1c53ba..18f17c8 100644 --- a/docs/artifact-registry.md +++ b/docs/artifact-registry.md @@ -16,7 +16,7 @@ document of a type. `Primary File` may contain a glob (e.g. | PP | Project Plan | docs/project-plan.md | 002 | | MIL | Milestone / Gateway | docs/milestones/*.md | 006 | | DICT | Domain Dictionary | docs/dictionary.md | 002 | -| RC | SQA Review Record | docs/sqa/reviews/rc-*.md | 013 | +| RC | SQA Review Record | docs/sqa/reviews/rc-*.md | 014 | ## Languages diff --git a/docs/sqa/reviews/rc-013-mil-005-code.md b/docs/sqa/reviews/rc-013-mil-005-code.md new file mode 100644 index 0000000..a3ef727 --- /dev/null +++ b/docs/sqa/reviews/rc-013-mil-005-code.md @@ -0,0 +1,60 @@ +# RC-013: Code review of MIL-005 (screenshot, README, wiki; no Python file changed) + +## Metadata +| Key | Value | +| --- | --- | +| ID | RC-013 | +| CrossReference | [MIL-005], [QC-PY-001] | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| 2026-10-13 | Proposed | Jens Tirsvad Nielsen | AI as stand-in for S01 | Initial version | pending | + +--- + +## Artifact Under Review + +- Instance reviewed: the source and tests that [MIL-005] delivers (screenshot, README, wiki; no Python file changed) +- Checklist used: [QC-PY-001] +- Scope: full review of the files changed on the branch of the milestone +- Language and domain: n/a +- Language reviewer: none +- Mode: autonomous, not independent (the reviewer is `AI as stand-in for S01`; the assistant also wrote the code; the review of the files was done by the `python-developer` agent of the session) + +## Checklist Results + +| # | Criterion | Status | Evidence/Notes | +| --- | --- | --- | --- | +| 1 | Packages, modules, functions, variables, classes and constants follow PEP 8 casing (`snake_case`, `PascalCase`, `UPPER_SNAKE`) | N-A | MIL-005 changes no Python file (`git diff main --stat -- src tests` is empty); the files are a PNG, `README.md` and `wiki/`. | +| 2 | Names state purpose in the domain's language; no unexplained abbreviations, no single-letter names outside tiny scopes | N-A | No Python file changed. | +| 3 | Code is produced by the project's formatter and passes its linter with no unexplained suppressions | Pass | `ruff check` and `ruff format --check` still exit 0 on the unchanged `src` and `tests`, checked in a fresh clone of the branch. | +| 4 | Every function and method signature is type-annotated, including `-> None` | N-A | No Python file changed. | +| 5 | No bare `except:`, no swallowed exceptions; specific exceptions are raised and the cause is kept (`raise ... from`) | N-A | No Python file changed. | +| 6 | No mutable default arguments and no shadowed builtins | N-A | No Python file changed. | +| 7 | Files, locks and connections are managed with context managers | N-A | No Python file changed. | +| 8 | Public modules, classes and functions have docstrings that say what, not how | Pass | `doxygen Doxyfile` exits 0 with no warnings in the fresh clone. | +| 9 | Logging uses `logging`, not `print`; no secrets or personal data in log output | N-A | No Python file changed. | +| 10 | Classes and operations trace to the Design Class Diagram they implement; deviations are recorded | N-A | No Python file changed. | +| 11 | Tests exist for new behaviour, are named for the behaviour, and do not depend on order or the network | Pass | In a fresh clone of the branch, following the README on Windows PowerShell (`python -m venv .venv`, `python -m pip install --upgrade pip`, `python -m pip install -e ".[dev]"`), `pytest` reports 39 passed and `python -m weather` exits 0 and prints the results of the lectures. | +| 12 | Type checker runs in strict mode without errors; `Any` is justified in a comment | Pass | `python -m mypy` runs with `strict = true`: no issues in 12 source files in the fresh clone. | +| 13 | Dependencies are declared and pinned in the project's dependency file, none unused | N-A | No dependency change. | +| 14 | Each piece of knowledge (a business rule, constant, format, validation or query) is defined in one place; code is merged only where it expresses the same knowledge, not where it merely looks alike | N-A | No Python file changed. | +| 15 | Imports point inward: business-rule modules import no framework, database, UI or delivery-mechanism code, and there are no import cycles | N-A | No Python file changed. | +| 16 | SOLID holds: a class or module has one reason to change; new behaviour is added by extension, not by editing a type test in several places; subclasses honour the contract of their base; protocols are narrow; high-level code depends on protocols or abstract types and receives its implementations | N-A | No Python file changed. | + +## Overall Verdict + +Go — all Mandatory criteria pass or are not applicable. Assessed on 2026-10-13 by the assistant that wrote the code, as stand-in for S01 in autonomous mode. Mode: autonomous, not independent. The pull request is the second look. The deliverables of [MIL-005] that are not code were checked against its Go/No-Go list: the screenshot is a capture of a real console window that ran `python -m weather` (taken once with a one-off script outside the repository, not part of the change; the capture was cropped to the window so that no neighbouring window shows); the README and the three wiki pages show it; the wiki has `Home`, `Home.da`, `Home.ms` and `_Sidebar`; the marker `wiki/.publish` is added (a small addition to the task list: it is what makes the instance's workflow publish the pages). The address of the image on `main` and the publication of the wiki are checked after the merge and reported in the run summary. + +## Action Items + +| Action | Owner | Due | +| --- | --- | --- | +| Challenge this `Go` in the run summary or on the pull request if the stand-in decision is not wanted | S01 | 2026-10-23 | + +--- + +[MIL-005]: ../../milestones/mil-005-screenshot-wiki-and-finish.md +[QC-PY-001]: ../../../framework/qc/code/qc-programming-python.md +[PP-001]: ../../project-plan.md -- 2.54.0 From 1feb9ef29c1c30ce55e07e9b8605b3bf31c5a403 Mon Sep 17 00:00:00 2001 From: Jens Tirsvad Nielsen Date: Tue, 13 Oct 2026 15:37:10 +0800 Subject: [PATCH 5/5] Resolve pending commit links --- docs/sqa/reviews/rc-013-mil-005-code.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/sqa/reviews/rc-013-mil-005-code.md b/docs/sqa/reviews/rc-013-mil-005-code.md index a3ef727..1481178 100644 --- a/docs/sqa/reviews/rc-013-mil-005-code.md +++ b/docs/sqa/reviews/rc-013-mil-005-code.md @@ -9,7 +9,7 @@ ## Version History | Date | Status | Author | Reviewer | Change | Commit | | --- | --- | --- | --- | --- | --- | -| 2026-10-13 | Proposed | Jens Tirsvad Nielsen | AI as stand-in for S01 | Initial version | pending | +| 2026-10-13 | Proposed | Jens Tirsvad Nielsen | AI as stand-in for S01 | Initial version | [deaed38] | --- @@ -58,3 +58,4 @@ Go — all Mandatory criteria pass or are not applicable. Assessed on 2026-10-13 [MIL-005]: ../../milestones/mil-005-screenshot-wiki-and-finish.md [QC-PY-001]: ../../../framework/qc/code/qc-programming-python.md [PP-001]: ../../project-plan.md +[deaed38]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/025-weather/commit/deaed3899c379d04d6df00d616086f46aefea029 -- 2.54.0