From 3e414f580e9ac55556ef97bc092168a712493244 Mon Sep 17 00:00:00 2001 From: 41abku1mst <41abku1mst@hft-stuttgart.de> Date: Mon, 25 Aug 2025 22:27:23 +0200 Subject: [PATCH] unnecessary files deleted --- newupdatedFile/Dockerfile | 41 -- newupdatedFile/docker-compose.yml | 58 -- newupdatedFile/docker-entrypoint.sh | 7 - newupdatedFile/dta.zip | Bin 45970 -> 0 bytes newupdatedFile/full_output.log | 119 ---- newupdatedFile/moodle.ini | 1 - newupdatedFile/nginx.conf | 34 -- newupdatedFile/student-dir.txt | 1 - .../teacher-dta-dir-test-runner-jdk21.txt | 1 - newupdatedFile/test-moodle.sh | 524 ------------------ 10 files changed, 786 deletions(-) delete mode 100644 newupdatedFile/Dockerfile delete mode 100644 newupdatedFile/docker-compose.yml delete mode 100644 newupdatedFile/docker-entrypoint.sh delete mode 100644 newupdatedFile/dta.zip delete mode 100644 newupdatedFile/full_output.log delete mode 100644 newupdatedFile/moodle.ini delete mode 100644 newupdatedFile/nginx.conf delete mode 100644 newupdatedFile/student-dir.txt delete mode 100644 newupdatedFile/teacher-dta-dir-test-runner-jdk21.txt delete mode 100644 newupdatedFile/test-moodle.sh diff --git a/newupdatedFile/Dockerfile b/newupdatedFile/Dockerfile deleted file mode 100644 index 769692c..0000000 --- a/newupdatedFile/Dockerfile +++ /dev/null @@ -1,41 +0,0 @@ -FROM debian:bookworm - -# Install Apache, PHP, and other required extensions -RUN apt-get update && apt-get install -y \ - apache2 \ - php \ - php-cli \ - php-gd \ - php-mysql \ - php-intl \ - php-curl \ - php-xml \ - php-xmlrpc \ - php-soap \ - php-zip \ - php-mbstring \ - unzip \ - git \ - && rm -rf /var/lib/apt/lists/* - -# Add custom PHP settings for Moodle -# We copy it to both the apache2 and cli config directories -COPY moodle.ini /etc/php/8.2/apache2/conf.d/99-moodle.ini -COPY moodle.ini /etc/php/8.2/cli/conf.d/99-moodle.ini - -# Download and unpack Moodle -ARG MOODLE_VERSION=403 -RUN rm -rf /var/www/html -RUN git clone --depth 1 --branch MOODLE_${MOODLE_VERSION}_STABLE https://github.com/moodle/moodle.git /var/www/html/ - -# Create the moodledata directory -RUN mkdir /var/www/moodledata && chown -R www-data:www-data /var/www/moodledata - -# Set correct permissions -WORKDIR /var/www/html -RUN chown -R www-data:www-data /var/www/html - -# Copy a startup script -COPY --chown=root:root docker-entrypoint.sh / -ENTRYPOINT ["/docker-entrypoint.sh"] -CMD ["apache2ctl", "-D", "FOREGROUND"] diff --git a/newupdatedFile/docker-compose.yml b/newupdatedFile/docker-compose.yml deleted file mode 100644 index 828f0b3..0000000 --- a/newupdatedFile/docker-compose.yml +++ /dev/null @@ -1,58 +0,0 @@ -version: '3.8' - -services: - db: - image: mariadb:10.6 - container_name: moodle-db-official - command: - - --character-set-server=utf8mb4 - - --collation-server=utf8mb4_unicode_ci - volumes: - - db_data:/var/lib/mysql - environment: - - MARIADB_ROOT_PASSWORD=verysecretpassword - - MARIADB_DATABASE=moodle - - MARIADB_USER=moodleuser - - MARIADB_PASSWORD=moodlepassword - restart: unless-stopped - - web: - build: . # This tells it to use the Dockerfile in this directory - container_name: moodle-web-official - volumes: - # Mount our local plugin for easy installation - - ./dta.zip:/tmp/dta.zip - # Mount the moodledata directory as a named volume - - moodledata:/var/www/moodledata - ports: - - "8080:80" - depends_on: - - db - restart: unless-stopped - - backend: - container_name: backend - image: hftstuttgart/cota-backend:latest - user: "${EUSER}:${EGID}" - ports: # Add this section - - "8081:8080" - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - '/tmp/dta-tests:/tmp/dta-tests' - restart: unless-stopped - # This service can be reached by the 'web' container at http://backend:[port] - - cache: - container_name: cachedta - image: nginx:alpine - ports: - - '82:8080' - volumes: - - './conf:/etc/nginx/conf.d' # Nginx configs go here - depends_on: - - backend - restart: unless-stopped - -volumes: - db_data: - moodledata: diff --git a/newupdatedFile/docker-entrypoint.sh b/newupdatedFile/docker-entrypoint.sh deleted file mode 100644 index 49e734c..0000000 --- a/newupdatedFile/docker-entrypoint.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -set -e - -# Apache gets grumpy about PID files pre-existing -rm -f /var/run/apache2/apache2.pid - -exec "$@" diff --git a/newupdatedFile/dta.zip b/newupdatedFile/dta.zip deleted file mode 100644 index 8de309aa041c025b22d6260e77ee494e9282ea4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45970 zcmbrlbBrkMw)NY#ZLPL#+qP}HSKGF2+qP}nwtF?c_3nGVo1C4Ub5C;Xk0+H%YNV2? z%-Tl^_5h0RCMG{oj-f4UDWzY>jE{ z&F%lwZ=QP3-ag_Nh4WS^hNyu$6!_a~tX&|!*gg<{_@I{NkBcLhD=m zAdIAnFo+ugP(#A?43U}#7`g8b;84Q4-PdRlyQl+Hj8DukBXW$Bh!YV~W%F42<%&n6q6IFV>Rw%*njRF4 zl6mL*+G8)hzjJ-Psbx&aO}8C^bda3HGf@mPk~pI~&lNuo2{P>*6^9RIA++59fN#!< z7m+h#j)TlNKIllJ)k2v0bc%a;bXaf+OL*bQ(iG9C#4Xs#EV+`Z{0e-vvKJ%ESXzR8 zB0Y@nNJ<$SHOd?*WEwpJ)q6T2$Y58x=%A|XAh0{~AdMoQAjak13;)Y5K;!&?)nT{e z*bPE(h+Q(2gN?xAEyBkXF{K2Y1? zS(>l<_#Z%8hxOTqiv;sEHKR>IB(VkPD|dqQd`4ph9F2rW?Zc@@)3l_$0m{2Rf%JGu z=+uyn7=sO*cANToC80qx$I?NmLQNxPVrqil!}%r=2i98;1Wbk zo4rr$^nhyAIc9Bsa{kV3p@&6ksLgAd10Ii{D#a)eJF5=|Ru~L8Uc{kX9C|0)TA!^K+)Q%d~_|<`hIrCA|r2Z4!1yK0)GNDfxi~Zbg31X4H zkaU;CH|zwQKdwos!c>+p;o-Yx-wDVrR${A01#r zX(q*e!sxg$<-NI?084&B!kF2kFbIgYep`Q+v~I&i4VgSZ?>PwFQ@Z%aNQ*w_d!gb$ z2l7?!1;X)z?kQB7w$t+WYS`7jA{!A|7B871#yO7jY#1|VT={?llt-k=NAhv;gthHb zKyo+<53&CO=3aIQi-sAUu z!F31PO~I2EBm$P7fD8j$2izXt&@0}{%0JD^DvI!1=p`yD&j&TWPMg>*(MM_Q|s54~vznA?g&Wtb)B<_I5K zwRFILrZ4*!YxN#CiVK_Opi7`w21Ur#4A{#<9lOQWOzXCD|9RsgKgi`un0MRWBuEI12?@CeW17edULgMwewQaWiI7<}p9s(xnFzAZ1jdB%?{ zOe(qeNF4f{`kay;jO~r};7&gsF_8TTo`n60ZXC7Mp2hEb9+lg7EPpq1;Nff&_CjCQ7oJ{3Erla~UA_%tl13qZfZ1_?9?M5U~vAi7)PMcuP{+oi4S;4kNX zUs~d2nx54!cMMa9$`~C`ry*?@VSuKN_LLMVFl5k40OOuqOtY$U zf=js^nwx&QJAMJ|xl6Jl-RI2}N9>OdbGKxtZ;F?l${MA2_zmG8!V1$3YhNtS&?s0; z)(6_NTDP~c$J9l2dj}=QZ=vWoMy*jJU=qY7=#)y3UY)lMJ4*Fqh)}-BsfPK%C}wiD z-57^NisLv~+RjZ#4>kCDIltN&iuh!QzwhZwf3_Qgw_8}3P31L--X%|xAPuz?g=dkn zL4tKt(~G0+B_jT__&oi5hBzHsc{zW5Y!9L427ET??MvU$+m4OnXH@CBq*{zury6rsMjVpWS-T^#@u+SM}axu&(ASEqt3yOlCKi+sG z4?^NAz5XnGx=cSRt(Y{Ict9R&wBAIv?-I-}ByyZ)ytIXF(%8)wS%clp7dX-l#Ze7Y z7FNQNAydm)hpKJd<7o(4*41D*u-;=z2|8WX_kPSUyD`Z6$|bA$&f~#v1^aFJJdb40 ztEkrq<3wWM38^k_Jt#l_Ezl*kA;_&;diKjylX?zETEvoC-(X8SCKP_R=NUZq4)G-e zh@Lh0q5Ji+6|QZ(_^VqAJasTMQ{gguqkM#F^)}1Fs!vN91!8a{JV{izWI z%OWSg-xxT97kOzRx#KrZBrgNns0zp;ceMGZyf{>>w|a>tiHR&6H0WFF{++~^%2}+Z zlXFN*Lq~P5YFfH7vi0`YY_3;~cFu46VVVz_Rp9k{%9tBT^WDtwK!I}%Dgc+PoI>Qo zgQGcn4j!(`Dv{K(ysKjaMqnu2c$N)ffa@2X zW__94DS$LP7S~--Mi%#; zvs?=ECjcQ7+S=PAI8CQXA{Cwyn?gsL`j<^)=*dy9R!ePV&+#zmTvcy%`SDp@3Ll z^%3^>gwJO~N$t`MEG=R5h4DIA_O;%1Zt5Xwztx9q5lnqO$B_1ZsdhlOVpo^gWpS1hk3e4|6@_+~%cIt1p^QDm@G>devUX_@B^MMu+_n={wTarjxGNCsnZZopMK zSTL+s5;P5FVAxTuHZN$%^mF3dHg?7jS#E4-I7Av{a;q+`4}?vZ!)2_YKSqoMM4>DC zXgy4+_f)-E2MXervdC{xxgd*QYcsP<#h@w;1~H$9m`6)%@uSN99*dCB0`NaWE018^ zRBw!frdWss7yf`E*Pmt~wP~e1nyOAZA4Fa-!p!#*)zSy#`z=45Z?g^!$#gWya-J6@ zRZ|oyS~Eo3o>PMrBmpDa1@g`IG6p%MZD{15z(AcgRS!y*#c2?OqlTSK)$v|ETNtSM zj^FjL?jTR~8K}d@HMGQHN6|W8XY9Ivi_M$1WBawqy>@4auB27)skg5A{j>0-G2dap zLQmfw?g_EFX;nUhBPPSt$-Az_E7+o^c9I*HAM0P*Z;2@}qX_JwYJ;Kfn?7YWQ0?e( zfksW?(T;1YQ>l6o47QMC7Rpt_-KpxDA%%Iv(y6N9H^|@R{0;aI(u5fyEN%WG%?JH| zLK@+}BhA@B@4xJuo{O`EwbTC>+erVKxM_78J1mZWnYcuG|1kcV_MOloiN7XpKMCPq zCeD2QIUsp>r?LBACN8Rbl9oaU4wghw_yC^U7l%*9rW&V!VOXJwaS^ux8pIZ@?+#bz5JffHb>+(*-2R}5=N!lF>;6FrIL%qh@4%P! zYn4jy?M>a+65kzwGp2yyvA$n3ur^+E{(ww)vCg9Ja6%P`Iw*ZZnG7)1vu`l}@^JTG zb@7OF35M1dCA7lo*9bw(E^`pKCKb)^UOO0(LlHENi!P5%3Y09bFOqd?2vu~T11KA` zybqtAq;opfMd=y)!mrzG2&4n0dA||G*pMVL?p)^r*d<4BenGE1Tw>U`8-e*TE_zWr zGiEr-^y7n#Gzx{)9vp`IpMuZAk267W?j)_pQfO!O218FrshejUvRg_|F*|-===NX5 z6_^i)m}V3w@CRb`*HyE}q}I4U(w`35Pp-ty4gEytfr7Rj&_{2??ODDlQs!IKU7c+N zGI2x~>kk0D8)J5wY+aR=d_`v;%mHrjSMJ*NUOw6FYPa6slrRmM!_V5*bh2eX&gidM z=;RJUDz|v#J9haOj7k@za0-35c2`P2X*q|oKq&-yWtDX?y7wnRQ)0Bx<{w1w8&)yF ztt-|h>p+(^=yw<+=7cOUb<+$sE3lB8TIeollIW8EC4AC?%3ks#^q#@mc~7{=ZCmEt5G*e==! z`_)3&$P4E{GMS``a${;J8-|rJ(0VPoo2LMpxN(!zSe~$h zT~--vT6e27IxUEZzBM-5PGCK|*EeTKzTVx0(dZX6(pmaN@eAXx3A7Ie!6uID$t7oQ zWyy9^Y$oMc9I)Nak53<~1Z{8uM?JQ6;3jn?3)p5ldjnU_{20pPYOw^@GR@uh;RmTd z!ob)16K6yjAPR?1NO0nU23y8#=F5uQ4=YoNQ=4`}iMFUZ4yKGA)sAj{J80RHkPW!e z&X2CTceKReL{si$)(Xr6a( zk-jNNH((gnaY~d|G`i!*g+|>Loh)z9Q)IC$283nwtu`L|9OZSLuf8q<-47&6;0k3$ z*oKXn3oMD8+fiKl33*c=X1HQ_mZAY$hd*^+<$FR^Q_4zFo#lH1_~hh#-Kw$Lh)Jcb zvXRgE#D=l43Yy`n*9N;H$@eMQALRSNmR)7C?lE#MOcpbcJ?{BC#$8N;9R(x=T!(}ruQQm$C^T@3kHtfu;ZfsZ`S z=BC0f=O_f_FsX>M(F&#)+G`+9JQEp(dY8?`+l!Y_SXHabwu0;Abyd&A;ij{%WC1dURYaWib_Ssa7PCj%Dy841rr!@S^ z?5&A45S?F#XFG73lHisyPJmA=+eZp5%T->9vi=}7!>v*=6?22Xu`dMjCM7Eiid$8jwQ0dK^*Q`&>gT(8N~g$|=~3nFSa=JX16_6i z8)C7x7l=q_(^(P|pe5Yy@9y)kQPeXG18SylWSI6CsO}_JwyTWC>z;emhDb z+0#UFrv#`oAyC6|bf??)Xzvc9qt!1#?y+it^gyTfOcKrX3E)};*QSF*Q-re9iif7J z0MZ(wTtV=~!1=wuaRo^u5-%Mr?k+4)`A650!ob6R8QZk+n# zOO#)-3uvv7!&G}d1p=VKFwUfC*z-?`!}v2#e=_9Aj;^}x@)FjokG>_@ zq#+%LO6(OHH<7y#)r$-tU(S}6>cyLUdN^{l{|UJm96q}F@L~;&%%q6gcjfNR%-Ye5 zDYf_H>+Vj^h6gl_S~4Wo4|PgqxAHeUEle~hMdlzp3_nyvviC*3 zw?_QP5@dP}dMsZYZ~L0}z1#KLZ{t%xnr8A3@!pW>yk9@uQumDkcgg_OZYYT|^LK}& z=^=;7cJ8E$#SSH0JdYNQ-RZvU<6?)6Lk=6zBKLMk#@kYSaqK60(%Q3T$UQHQeLs>! zi{LglwrWmCvL9ZQ2FQ4x2 z37Hw&#COCh%261zd5845eIBQNF50ovFFh=T)n$KI{RYm)UA!y57s{8A8yQXCk+r!IMe+*uv0yMsa)%|)Xf@R2=F%a{ZpFa_ z=t5iF6p`A3=5~u=!A%DThd~ZkUaj!qa@~90$5@jLFD8yf_YW+erW_hpgKG$OS)mEfUdJ*7tEhwK6A`jB0G1?-BDf^LWJ8jIfQ5sNj19%i}la*jd{ z<;>xt37L7~8B`8k1%uHQ%>KXV6QqHWEldiO_t zuXW5KJld4dJpmw^fFM%tGleN(lOd?4-S7|kDRy2JTKSA(-nQe}v10ZeBZs8Tu<0^UfiJ~&0FGI#1IwO#jL)u1&zTP;7v%!5t`HTr~504mw4qK!y;2tm+9os z)XDg)AkN!!1vY_EH95ZryTsM0RGd=LJ|pTw@yB1dQ?Cq(17pEc0`14Q)|2dAQ^5Z) zax~xT%SW>4EG*laJ2B~;Stc(`tgj){Yh0GUm~}n5vcQ2Hm2I%{$f>QyHJYImNi2&` zNm0-UqsL*#TD|Ne;N0EbmzhFPzf8t_Tg_5y!@4vla?6i^~>R=w2DJ1pWD!<}c zcJlV-U{x+BKirfn2BLl^tk8Y|Ft9!58I(8g@v+q5te{g9c1*5SJc!u?q$3oPIvgns z7VJ^#N9W5L+mW-e8sLOs5C$Y3jQe$-d&sjf0qS5kwlu6Cqi%~#AbAOXJj>Zdf0S4Wzak<8TQrd6zR@`NBkI^m&`C&Phy z`__++smb>+Zv%mkIaRQr>R5OI$SB(ow+UFW(Bz9_mie{zwINY-4kz~nQVuX@QD0Qw zm@F0JA&u8w7dc40BTzD{DMDta36UCuZ|n|Xofx1^H*I0VhR&HpWp4UVzU(?26|}O! zve0@_#6C7aJ~03)1#KUpC zAF5%UPc8335)_^Qe8?TyP1oPs*Nnb8MglkT*(^#Dk@rrXTl&5q+S&Wuh#bY9f#6wX z;|yqNF_Pz-jbz0-60le{-DaA1*Pd2Epc_uhF}K*c$9^Akbpq>Nf^`1UJ&j7`D!M?q zPWwDeydElPY;4)C#6?&N)k!u!dX_Q6{9T?7ELtoo=}lu>hXb}2Dc4+`oL?<9|Ec-mu#DgcC1sdy-_Ks`Bg`?n*``5WWlDmH0U2M zqJLn$p3rNO(orHF-F1rZaHF*6J)k3T8`Sg`>498rfYzq_j~&j(1-L<+`_Nkg>T<#> z085NO4_*LK3kBJFvnnQ`J4+Vy^nQB~D5Xbq5yq?=%t7~0*-RjPyhmI`KivME+;lV0 zuJ_u_XpQnv=`==fYwT^TS$BWT&~`T?)`wCtim|{k=I|&L4;Zn&G+b9U{m@98nAK`T z?DPir6Ko5puzuKh_Y^Q?{+6+A`ktl7^!BNKwOgIIdT?WX-{3J7%Jm#G|I}KGiqU0C z*74PPnnK&Xy0cmmB|6>a*=`chU6(nx_EAAM$+G%cO*DaQlqykUk*m#M+swY>6zkf; zR0&fYhMVxeuByl}r&&BuJIiBIj;Q=eD}r2< z13uB0$|51z8SpOhy$hpfk0eHc1v=@*^FtFnk-Xa)ZZ(zgmD4w$-0j{*FcHuP>R%nB z9QZ&Rxae(W4CZyo;rs#prwLZTFPYAS0su&*{hyj(ivQ3AyIPpI={dO=+E_R_S=ibB z?>pja94n_y)`w1C&;tiaiewV9cHP^nwVJ~&?!+~`$#&9IQ)U!M4Qq16NzgK?KntiG~#!kcoQ;4=}`7 z0kta~Y)bGp>F9Hx&XVZ%)xixi+%bX}_iYb7dM}g`N1qcsYq4YajmMIu*3RtcCKN8d z9*q`8MiwqkzIp;=!3eoa)>4QaN00P?r}-C-!6i^2%#`^0IDA5d;PCKxcssu1jgisG z(#qw!bsi0`gK`j^ilL{HjoRtq>%={_qMw>=pV|m^po|xUxhSN8X8!z-g zDVkdG;^Vo&ZD3<(gTu3q0gvlu(*rODrpfHIX3H@HIL!M<7g}MA<#It{7WoHCHhaSl zg-IXynWc&uUzMuinWxZC@kIfKdW%2;GDUV*63G7fMJIavx;11E%s4KIMa};unsPfy*j`Xpe zzp!%lAuw18zp`$O*jfA?$2{F=508#$HH$?Ia#)qNr}IK0si7iiuZ?vdN>F`pF8GlJ z%0>;}Fh+ks7{0EDEc7A$8h~)B-oBY}+D1Ki{TSv(KdMdSJnoCzln!hakL;L2tLMW* z)1Q~~OysxKbqjdnWiDP%Cm+|3&!+~r-%lqT>trbs2rIh&oHx^u(fx~r0Q|NHAH=n^ zyIrKPyMKg5)`Hghn&+miG~p*ZpmiF zk&ws=_~rtMQWyh_KR4Lb?t|ZHk8KoZRIYi6W5!` zwYK*=1O^ph%3mn5Ey8-WFiQUshQsRYB-tX(5PP+Mz)P5KHQJE(PK@bIN1!`Ns!YavKANZS|P+zT59+;!Yktyh8;U-WaH# zDnB>>lXw#xBaO`X(H_}S2VW5*6Fj_kcL}$myH-OxQyo_)g+!1lqK%-RaE< z&EI+xq=Pm0>DL0sn)27-t;`aWB_@!8a5xHGP=}UwD*&UV8u_^K5R><09H;^kfvjm0 zLc>9^I?KUq`fM>x^4J6=ZTDy?Wjz7S2t@$wI|&XaX3_%9@$--cT%W`A`mtbP_>|XZ8hAgq7l=>q z0r11#-mjLlYy@PfDb5Lat({RGR1Ew^YFqvEK&m??Ct{{Pigf;G2v9Ys!C}T?#Gw9{ zT4pHt%mMNICpa=2Q1nJ+*(;tgw3V3R#)L*E8>9MFjJJH)^`xH`wIGoK1m%$7U1nMk zGCCzLM*|x7>-u6rU&**5SH$ZziL@(dIn#JFGM2WC5I{@67q}%L4!La1X^|r~(E>Ka zZ(NVpn;!cjqIPp@O`ib)YTVyeob(3e7z+0d;2eV;A;*TY@x={}Mvl%#NT@yQi8l7n zeu<%2N15YA#M9r65_hiOJY~kGM zd$%_MG%OfaG=>8Ao7u?&I(XC#2ZtqwxYJx#d%|l3`jNSGZQ8oF)l;gbS-PS&uvR89 z*C{Czbmj*z>jQ5qs_mA}EQ{17%jTxeQs)9TF2j>^r7%^QeeF{A2A3eDPvyksFW%xd z=$9km*o+WCf+^!%5F z^E@I6D(5AI4vg1?&ry4pO^mFxsD(IH!~>4VCsxUS<_6mC2n!DRBe-6L+$`-+AD3Q5 zhQWg=qYt{u(=1r8ex$RDs=3yyR+|Jf%|j^Z)<8R+J@uD#LK+r$(~itpUj8JpTnd7q zb;LKk(AI|nCBxzlPyWf$+IOnrSTqV-z2l--qP{WNN=V0)3Ry{rXE_!A70ud}GpGFHymDA+6kKYp1%HhU4Qs z$!3>SWq03dAUFydTeh0EvISVaTJ=ON-)V!oFogxyB5wYZK`lf5q7fRoVr5)u$FOXr z=XkBqX*0c@Q%NZ1EpoDyd?{(tYOE5;7`Fr-AGv~eyl-!W78!tmph@Mrz?9&0j>_z+7dV+bR4F|65YC#!Ro1JJy6 z%4zo;YiiT_9N-PZ7OXdZg~r%I%WWv~#fU)?4EIH+mk=4`Xbv!l#uHOC4qXWkw^KM@ z8=f#Vcv6&PogrCNSuws;oPCGxkestu-Bp@_GsP!MmbAN(ui(2#QjkAi@J~ORt7wlL zxVzc@oxD{Lzzc6%iTiH0(hRc(66Gz8)ZTKVg4&2Rd;v=7+41wES@Yt~;k)a#U9Q6L_wN zgShNUL}ObsXN(xzB+wfoR`MYNM^0Di?0NQIb;ly_?#1z$(d9s#7?iDh>Up`JnidfHT`*(t^QLjmoN5Jp|ktMFZZp)PnHx6{HxAPZW@?Z)w(#zum!OBx=LqoW==_duBF zF*52-tEi~BN@*MPGvrop(W5P$qAf`j7Ni^u#URbV10^*M+O667is8KZNXhfD&q$O z%W;-lG|lL8`Jiz%g3iiV-H)Rr6g8SY2x*sW&cvahrL@IXf0^EN1|tJ+tQTGs3NyZ3 z1Ecj!yl|_IRDh%{*QY8Y|7PR(QwBjW>$5=8hVqKxrum`6 zGw3DL6*USbp89y#xgW5T#pT8>c^j&>bB+w`JeuvK5X*(+qBZ2Yz3|5)YNg`R<5=wm zDi4+IT{=6M&WEQ~!G+JRUiONTtFp1)D|J`Bi@GH{%X1X z;db#Ao5SM-cYkWtsck}LKiO6lVyAG7D@Ub|O z>Fv+lte&a1t%y7y=~ar*y3ZL9nKosOD)l9P^If@;icF(Jpu+|C=h4bFYw%sN#g7Wkw`Z8rW^IZZLDVV9S zo-#z6V3(0#+N3W}=#}ZwR>PnHv&1R|k0s)5K+tJx64g$3-@z|Sir5sZIPF|k4=z8k zThF+xJ6N7@XENuP47}Cnw@CYNbeMsPqTjX5MxN&^)KM3j_s6zW@ zr!Nhci|@JR(Bw8o+@g-5%?|zIp93HAm39PRv)oxFg}hZ{Z{Z&Ar4u(-gtY{z7r16+FuTG zF|i%B8oFSylba1-v0t*tfZd>RmDQ^`W_LEyvJV{JFCn@KAjkc>+ee>kBI%;^?t-J! zx5t;10sxSi{ohg1KjD>skCY()+o;IK&e+8I|4&Bc z-)B$%CHOZ&qvvR1WM^aZH?U&h{C5KNzuE9Kth#Bt$&TQ?sx}4WVILr@$!sT)xRfb% z8GMP+lLxUulvf--oI(Ve&|rn~{t8F&UZahNrp_~##Bn;64lVZESH)NaL4HM1k zla&6R8_waB8O93MzBDXNqvnW#pwt_dX(=RU-}$_dlpCxVS0YlQR6CFo=O*rmmY=>e zi`P+j;=EffqTQSO2sbz2b0I#S^cUK( zo!5X*qk-RFcdQ^71&P5id%;H~J-kVMAT);q)IvUa30R6`NMma-oVKp5JbvzG;Cdl>!WHRWKoCbQCe%=; zFvrG8M?UrbgZ}M{WkZt5&0`B4v@5VT<{fuKl*?4ok4t}%0bB`nQ4-&~y4`}FA1dzXUWAc(ds~{)Wj(OM^3aC+(pPW$B;! ztT?xXV0Wm8+;ucEWD5iHl}%Y z$6Sy{s*{=cDQY85mzG&oqPPGP)|UMPaA{7-@&VY$D&;~|VL>bAxbotY{-{aQljE6F zHNiB`c@o!uKcY-g)5^S8XIwQWiX;muV{EV&v2*M*Q4=aWz(g3~n@Mu#IJK+ZGlnR9AE_}}|XSS_SVfhcThs)895SrWjG z<5#2GR4CZUrT!L1uc$eeMa=Tb*t2HNt(d(&wHhrvJfN4$mKwQpIuTIsDBPgFevIn`F~Shj#Z`X4p|X;{-{kE_;bf?bdFE2 zH&qtR)F_Ct8eJ6chw|s_$~KGIhsm?F`DG zr0-5t#bl_#!F}RaUQ`N;!ijhuf?!{*2+6kR2p5w%kYJhN1$TOvPGy@GB+;Bi6cQiS z-hhLVP=uQK2$F&qN_-PO*t8GuisheQl-e#vi96ijT-OP#A2R#?UFPr}B1k72& z(M;S8IH5iJ%xci>g@hnUY}zHfx=9S33WUDvl7gQ@x-q zjG>M#Txc|Z+7jiXDJ!k8<{(OvE4qIC&M4{X%B2gNQnDPYM&#yB?6z#Fo6nBF69zfi z9Cp3&%I^`rS)jV*280!{wj>o&@-J%=9dr`$Go|qZvlNFu@MGZ;SER@x2Hy~C2BJya zzT-cWR87O?7%JR8C!o9fr20^SE-C}bfeUB5?pO2<8V23&LMh5u48eUQayoQWCTwoO zRog6>{TC4dXHKvVzq4ZuL0iSsZi_#tR1s2NK7J14HW-PU%WV8=v*G-I|2gLf)hg5= zVpF#4K`WjS_7eDl@eUXH;G#{mVs8ZU80ApHyk%YB`JuUtif(Rz3pjUvEWz=bg>g?lpY17>1Fr?E`XOWzPwKNhF2vSx}BYT)8%R zQGD=&yJdjqR9g#+3EYLk1$Z^zSBTpcKu%-4tr{d4T?0`?pFhs9{al^j4VEP?D zY)-?BO`xbx@^YR=L-H->|)uaot$8! zX`Ia=$|S{l8&oiWE+tr=&7J);|WhTyXxIlQCu=l2)Uq3!D;HKD}IQ;_8Ztu^ZCmS&ew&YX%w$=5gHPif;N z%?Dc_H1H3V|Ku+S=5T{u<@;sCh|8%bw>MBW(M(^7{k#E59;$9t7v`Vx8riS!m3QQS zYM9~HM7B?CI&hi_BEIgxl zU-XvejeRq^Y+scwPgTekJ~D)fhVQcpNKR(7yOPQ zNj}rT;XkX!Oj$9T$MSycLqC6UUxyfq1l2pIw}G0kt|YO~pW!d?oVBU7Va@!GIPF+B zETjde`^cWzyFY~Ere&$3WA7L*X8k_324d7Bj$d+-J@i6VK%U@MeIt_SvWAYTgkU-! z%^BL`KkSy|`N?cG1X$B%)Vv2W!6TQW%O`I%N2P4T(A8GtWlZ}Gxky)?ShkAJmJKJyA3js3%J<#hZ&2^mGC7#3NZsMcmk5a7BwL^W zD}pnI2svBtsT?FrPA9=~MA|fchSb*ou62%NwD?+$)G8EcqBm^l4=;Sz>pwxBM$5VH zsrZ^p3-F?r0ht@Wxn@Fr#z9bJp1?TV)>}-{F}9|azs4{4ST@H(L_tyiI>vYMZXj_E z5p#l>KgXxHC$BUO?`NOazc?Lzp1a8%G2A<)wlLL(iJbLCpeZI0(|43Fs(h2Km}L3q8pNevUPvftC=o|dV!30UStX&=Sr2kTo=}9Pe7KOk)9wj z=~&;8b-1=zN5>`Gn3gd=nNwMeAz~8fMaBYdi z0GU%u-+#Z7(rci6!rP33M6;Y%^-*~uQ;C4pLk`SpeQ6sJi$x3dc}yj(?R0&a(}74u z$N!A{=Th$E7mI$#UhQVA5;|T>1iylmQaaj=mP_bEgkl`~b)s+a-ivRvP2RFXWEk^?^aAnyCq&@ z{U7qtaspa>U<-{lTg-tIJvuiCqdn_IpzY!1=iyVjh;>|aw8?{1I)k+mbvYN6-g`6y zSvjej1H7m|)x1Y%2|h+z)>t@qi-xYIX9JfyUo|rCvM$fEHmR+2=dEv@zQmO|_zbr| z7FcUcZYobgwynowQHi8|Vl77a>(zZzOa0OLW55aKuWTtrmYXylMu?twe{S7_vaE_) zUb_{wR0CV6=*7nmj?JB|;k4y$SX7K(!1o{nzk-hG^rR$3Uaq*gU>i@w0_%1f*_TIs zicqXoXya{=59qj8E-yoEowQTCs!%yOy;rJ zqCJt7nh9LU;tQk71f?ocoTt)wTb!ll_vf!bTCc1(&$64*PpkSBw;5AWFK6`D9-*?u z6gXSTxHxdVE}1k`S4%M~wN+Oij3&6cGZ^^_#6K{cklWnh(eJGqLkBh%~ z)f{niEYw^cmOflJvlf^jKKORR37GZ%yoVdjXP?{zu&==7b9v^;7h6@12fcBOLC-lr zD)PTbjFDb+y<_KIb4~M(cgiY@=PmH96*Jn&i9>eTv4MuaRWoO6Vy1}t?EvfOaM8(5 zVu(5~7Kprk`MhT-M4vNh|0b}q9l|&6j^pON?a8Q(5|QI{5a`&oOs> z0X2p-!aYFOXclhstW{A_re7o5=bV85t~+t+e%_tib;L3PTugcy?SuemkqPvAVVFtwQHycViRT{~_%hV{QGqEPw8?ZQHhO;~v|# zZQHhO-(%ahZM*NkQg5nLdAqBVPIj{Qrtb$X?C?MJ5&*MVrbim; zwpbj9J}b3lFwkm{Mi=_qhvBZttJC0i*s;b$;KKre=Ox58?~r3HgwXdptr3@j)iyZs ziP=L{d&1|W7kW5&>LNeyGfwd?pg?W9;2=y04!tu;5-?-X+awZxwz7Kp&YalLgb!XB zqi+D_Vbr+-O$m zcC%Wd5yT4v8<}8k6Na?v;H_)S$h=xmxg!xkLO5s98n*x?A#e&>@vwc%yE7J%8-r3NvGG^_^VVI@*8SLK2?{`xL$hX{Zp2^ZBRS z2*(V%1c#zuCpy`B2*ysIUOl{i5SBtK@4lvvi~v<{ZuEd#3p+pVGD9!sa5i~2x4Lt+ zasZ{-%ZI}IP!Fb~d}sZBtw;w|sNIV>l?z_=1E5}p7g~DL=E$>w`Xto2bVjGu(TAUy zq_;yTRNq1G3%>~Dd-NRVw`kGIo!FZg&@>%d*!irV4N~t@8uNnqBR;4ZFNl*nM5CEK zr%B!oelDq(CYBpSsOCBzLsu&t@X@7tqz@Z!nDb)xPo(pPgwOPkfKLV358-|hL$}NM zH`v!4>N3_m9wn}Ive2kJr`1xxG`5FzWz7=>+sy^0(sA7) zDyDNoNK)RDzzE1j2Qq)tG4GnP+O`Iy$8BH(XkZIo!@fzw9h0Ti_&tilbSA z#BOLQ!as7w{czhb=Qo}Mjm-)*6b18fg{-&v`RMec>`@3JH2Uzf{T^&TGnXlS`}BVs zM3=`DO&oi@nmXKj>SXZo^gYntjQvx2_jGi6?Qp1kvxoAhERFJhC$cew5OnJ<^}ewV zB>=?&1;dZ|eNIo4+YhE$Pzn-vjIgW#FDC0|aL8R)o*ZQV84>djYa;c^`n(bpnq;9O z?_|U3eVXGHigd@7fv1q_X>KuAHU-%UH8?orqNF*ldwv+7>qPjYqZMH!tAO)nZS?0| z`L|bA=wO|^}T1`0t1h0^9gY+NnH{4wZno`^K9>Pi()hcb!=ZUpRs$_G^M zRtaC_o;$M_+u1%s1x@zY4y{+wjXt=O;OnRS)+Li;++%8 zy_}mji6n<9LtQ;LY@-?%73-9N;)mTe#EsQQE*5((B?C+NpDN;bE#v%e-_dw4sFzdz zN1o5>;1HI3)MpbQ-lTS+&Hi`(lFZdP7kh9%sg-`akkW1FJ+0b#q1WZn*18c#f60AQ z@KKDW%tHXT%F70jWqOfD^5=c(3NaOktqKZoqzj^5$+5Xr5=ZBP%oDoS@SYhtaKe~lwK039redfor z;}xL_48X?2aFyE4 zzgOSdM1?~{5UuVh$~7=1yr>Z8xYg*cFY>+iaM$WbV{9~$>dpFDhicu=N$2P8D?WyP5HVO(TukOvf`_#HQF8rgJ$64D=izV#XxiYSDviW& z!g?@M-opJnKp9j=@8IO(!h})w>Q;tC&&8+E?Ju0~!=g;nju+#=Z6P-kvq-EKFmJ$V zLULr1Q!PQhVwD8pp2c49@wsepNN5vjF*Zl4EAi(*gAdNuH_c3ynOI7HtY*MAK@T~U zYHB|kPde(cIWS1wEe8`P;FbmhgOVGS=HSn2IUq$v>1}m2~g|3>s8jS#l!?e z-9eYG6Y^vBWrQ;Xd!r^@hr%K%WhY<Y#O3(r*YE;{c;Bj^^+-k|(JKb;A?3*Ot zil82F6enEET5(j&&N>;NCHWm66O+^&*1)j2uE<|yw(ZM-R&zA9Vb^xEq+E^bScxxr z8j=8VzD9;nxMY~CnT;cgyueyi2AkyHa8kPLN#i+g?-CrjQrzcoPHMK?F?lN{^`)vg zLAFBR`9N3e;gjMg8hH8vY(4(O!`L-`PiwQNCRLoEzm2aCH*ycd#aF$~wXgUfG`}g? zd_wYZ0^M8UR6ISVMj zBN#vy>f;>b#ihtU=;bKeuO2e#HO8^Jo=ez)^{?mrv%@q)pCoY`6Lq&yWJ=$I2HKUH zkqw(1JvflN=VE=V%CX#%PZbdUHbHuCE0(o{noy(J^wMbyJc_!4+#ee`2C=<4_XF3k zxHj@_q^Q@6m2inWwOt!?Tc=xtofuu6e^w!0a`qR;cmSScMBWedeMew-Gsd>AE-Ux# zg3`atABH2jzFdeYqZ)t?bQj~mj$oj^={wr})}LiEdhC?>)VzW>eWO1~arrc3ZL@8& zfBwU~&leBCmQ=9)x_QaMklYMmM9~ai2!V4ROmD1htOKJ9 zw69w_@5AfY+;P2~VnUa~xOC=3dg{CiErjidS7Qv+6PhNt4|Nz8 z>;`k>1I=`YcMub1L=IHjXNH(`AvKe>fJOba~RBPJYwJXjin`LmA; zmkyetW(JKJ zF^J|?NN`>+fgP?74!kD(a=Oo$5yUzbSnkM?-wpC&6;3{dfl%noq~BS;mmubq-bE8Q zDsgYi{pRUN1}=tnd!V!M;-WBe`@=hndW1LTo?@kJkk}5yKr~$Nc#MOwW;9m^uSYbl zkZ)!u>t&nh!_H;9ll{tN@A4iqX7~KG!_~*@;rJA@#-^s0wzJgQkCv>3oj@&rGrs|j za&|8Pf=wgr6ITY4z_kX*koCw+BQuiXez^BJ@NLV4hCM>Qi zaB#__QQh-!XnaKr2G?1qskMbuF4v|{DXiEIq+-BD9ho9U&O;1-C@?%MDIACm7vjE$ z+=T>#!fp+l0OB>)Z!LEcT&INEw z!Pj~xk{DaJyQ||5d)Mg`UCee)KYIEx;F?IFjQlI?Y=TXJcE1lqBb8@ovjPU1v?taWDL zM*JoXJaTecCZGGa6^2+5DKq1H!j&B0Ud1CMH}n_%cz7N?0%5`HyyO~xQ*?0Hj1KW> zEQ9idC=x83KAO)r#ykWDW2rC+S7Z2k(4-PRmg2XqQ5eZUS}>X#Cx)a(zfnjv!UT)U) z?UkGiRcJs?*B_Ep=|b1%W=}TiM_7y}C4U;pi)%Yux?Bz9qd)=hY&3P(NiMNuP#tt> zT@?=$C$14&6BXS~dzu&+-1_0X=PHtwALfH<)`3x!X!Ky4H6qXb0{8x86BY5uk39ghv{|AAYoE8mHw~sr0!DmE zu2AkZ_~p^d8MI(`@Fpr9lVZ!;$mk;cI?Cju2y2M6U}NE^q8KC2S(`cU78Q;QlJrnI zmskfTT*(av@JCf!eQ`<+W}z5RK?HB5dMDTtF0?LB|5)CVuTBwj{Dix9 zlYNzUE6OF%7#t1}bKY3&G^hh9W7goIR3k-`Qq}O8BAUPUW4as&=)i;9iWdEB*I3qp`a35#;EBVD!!y7Uvy~{Jd%OZ%T&9 zsx&Cyv9f#&Lz6KwJe&`dx02@Z>E)4Q9OgITxCbA}c2>qKM?GYMBEqOejiqmOG?Zur zRvt3dHn46cw({oNbDP`}_L)$jV0(w7frL@DYnd9lClbdGaL)27WcS^xESu{oDG?x@ z*yB{-=3mDHF^bRAVt_gv+p?0L94txDHn15{BN*UYb2ruCqNVUr&Z$_{0X8?p#WHOc zFsj1te2y3@lg+BAw=X*^%RD6{qr+K70(+=!Sa-EuvNPl~Bu)T5smQl}l4Uh&!L+KD zgT33nK0ElH#gJBq7KVpXI6}@BJw>13^HK%P=Lz*T8WbkU`5IVOgoTHRqA}B;N_~)Q zOr-(Qoht_pW$zm@FD}&)oOJ?ZAMeSfvh!biuFh<5H&+gC%SM4YDu%94Sk0_IfIXBw zxJfCa9=^0%dsY{o8#q+dOqx_3 zpK9=bKh*W;@w4E2`91AH!U_hU`SDI%QNTpI5!PJI`l`HZv#$?asbVoe7F6)q49Z}7 z3cXhAvUqkJ2b-EXKyx|GoM3m4eTw$AjsnFNsR(yiEf}5RG;8{k4bEA(dS^^j&Z5f1 z>W)Np-zk>ajyb+J-4}&?^IaF8%lx+4NSn=YMk~Az-m6GjO+WhudIS!q3eNoDzr8~u zKecA(lpQW&rU&trR+rS3-zaD|pC z6IEQ_x0-Mxb_xpKk?nMAsu|xtl4ht2&e#8_D6^oJc&aXGG&3O$A!m>AtW2d$Cqfdw zP)0$V@LBvt!;Ok#gf!d`xYb`A)1up(#5P{w;D91*G4H@eaKEh@J2H$l4cBV`ydpa{V}*i!BB66uTlbzD2f@`{045U=U1kwm6=TjudqZm?GWrJ zlL0#ocx?2?OL$7NKHwtBc|xv{LNK z)mRpH4)3OOG>QqL*gvbBpT1#+dTVPcz&_7B4_f~jDNf`(AC2Z7-JVGJm>MQEE^a$! z$%VOiH;V6Fj2@;fo`Cx8oxy!$WXhy6OQn(B4T5Uu+kcb>*+di~gj=DV&G3M~MQ=9e z%ELtY6wpBLXpVzbO}Q&qA$g2eMKWc{R5*UdkhS-6#H~j3Jsz@(-dI#0G=_Vx7MePL z8ptuMSXXO{f7wU0<9SKip`zvENu&>~>v63mR!qgcU9ilRhJ9Mz=o(fTnt0MfEc7)Ref;xfYl*&^#q5*KB?|=7Gep`ttd- zDbCQWv6H#WRXf{Jg<;CQcnr(aYJ4VDlkZ)Yfm-1;3453934Y0bCC6y=d|-7qE-CA3lNiFGzf+g{2V1Hot~$0kh$5PI z^V`>+V(tiWeoJ{tr|e5!8q`8}_8_;o`#8EK-*&a=ozL8;ULLT#QzBz)w0g4+wvx4q z>Epq@eEes96Jw||4&)5X_1%zZPXaF_AVno64RwY9Qu?<~Mw?v%b>HEJD2wmg(Eio( z&wt>?GVdCfp#F}=e2M(4+5UIFGT48*z(xlDA67Ere-$yeadgtRvZ8gfw)(%3rvHJI zU8z~i?eoKb+wA+UUYZv74rMVQ2P)n~6?@3jo|KxHx7O9$p4ou>e8JEZ({%g^he*XH zn6-AzNzZma))&0+IQBeE9?lB(kHBi?6lJax3I#Dlft%`lkcz6=C?%`r{l$H>*BiouUy>OXB=#@pn>q_kPijP@w}#wpww(UD`4MlmI)eIR!j zK!56=^_QZ+<>e8$4L}F-VL6ZDrbDaMhD5lbcrW1%a^2 z$aHW)2?D*Nhz*lzl<_b_ipOnJQ5v_6jYhURrslO|x(1N-gk56fV(VZvGp$>L#K_Jm z*{6Oabbumkb=&_S_Myt|vUG{d(;AfUyDx=e2l&&u@_4P+K7B*2MGr>w@Mi9V0v_cw z^~yje3^Z=coiQxGq72=OTDLf-zZuIzV83_TaA^i~`-gjxgP9!Y9%5gtfm@trM2DR)2W0p;GR(i5BOs4S zfzgtJ>e8IYYzBq4&%+;Mi2A@dq)E#JoMIs6>j9%0okBC8^`vwhJDV9Gz4tzyeJx&hu2$D~=u!_G5SSz}1l^m)`h2nQP zJq^iIdvn1cvKvIZz2en=@gfXxR&3!pD6sC~1xQKQK1Cs-M;?Evucj+}!jl6!!-t`S znU2;UA)VtAU7G0hKJi!qQ|1CTwZZ z`exa53N7(Jxd+dJq2ZswQLU=&Fm^3qW9-PQvg1@Uxd0Y6>1-5XQfaA!i-@*!8F8!` zBT2Ufx~>4L?7lV21doV(bq*NY_NPy3{4dSXg44nhNrxe!IO#K#+HNXW8ARBnOlzFq z=I`2BniQY=9^YlfML+CKKY;(743V{$H{$#qa65(n*9!i3a`}H-e9m^J4*Ev_BmH)J zBFi-R_k_&`J^Y^!s^>T%#G++_R${XYOqczBXJ|h7S7iBpNdv)n!1g98f*RiIWyo-OFzOYqK_KY+cDCj@Rf23%Nci5%{V%ezPH#Q zr|@k{QJ|e0NH&Z-I1p^{{o!xiunq8__;lx4weoRV##?QAL>&2O{!>gmvXOx+1E^NB zSqPYkI7{VTEfh2E0KMqD;T3WMIc@}oXw2m<&@>HF)S{|(fZtlKbz&!P(5ZAx9{o^t zv|5#uZq$L@VR|+pp=-z2 zKArw^24HZ7OBPM5mo@F_78A;w2l0|D)hd&dFw^Mjhr<3 zsGf&QsAbPpGW_}**FTn##DbbiB$;1G4OQw8!}B0+DXt4*S9a{sR7i@;j|kU|GXs72 zTzq)F99A3S5aHbFxF}Qy5@u7)YTxS-`|7{Jd)4nM7VeZf`B z^wts{FK?dp#5+5xv~}eyK$Qh{y54T<&V_%pbhmu35o{pxMW>B6{JJM7XA^F(ufy~w zK-S-JsQ+_D z0>poAHCFmIrvHyS*1y~K{uQ0JUv3B?U zo{6EzNHTFwVjt-vDLm~NGJp}|`z}^b9V08ZW<)`gDMf<*Nz-O8J+s!+B&&afqRFY6 zm7Qbq$TMqV56K>eiVwd6-dB~=$-=fDW$iSEn8QS*Xr3M4IR7inkXuf!SwCI&$SV+8 zuX<8RKTQ+zIB1O8VK~-x#q~0KGpoHLXnk-Gwcr4xDAG} zt`!s9k<8hrKPbq1K&4;p)ufgR*MApc0W_TZ1eSzN8ZGPvaBz!$XbaW3uJBc*CuuCJ zjMn*A2em)CS!;@9eY##T%7bDyJ|jS5Y~k1koGTLRpYK6l*pY+1r^5v-02e8kP6HBd zigXl7K;zlb+f8eJI|E-1USBx!fm|`w!Z9-V+%BBILeAE9Z3E7b)rf82&dABfl?A&o zv-55$bYtR|VJqMeqIlj&o8vtkIz38^lrdE@pT881c&TE`A=R%MnIJ8Ht1Oa^Rj)N` z@gS;fI`JtzL3m4M({>lK$pAOLV0q2jl|T_4#VU$e8@V4|c1}+3xBIXzqgK5KFMWws zVbkDgk?U24Rux(Y&1pvSIqikTy8|RU{ciF%!8*fuJRnZRSJS@4nYLjyG*o}!X@GeG zRLbhX&1_mo7PSs(_a)n!)pHikp#6|h{G#~CVxR%^sT#?6FVyr^eziV#z2#&INnP}> zN^$Hgz#MgcZXQB8cTLzBA5)2wA8fP=tx@iiboOt$4=k?kS^nwKon90qeVpF9JwS-R z7f7IWv3u4GBC=+Z#38B^H?a)d3HoH9#K8c)s$Zq`;1_S>v(4zh888~IU|)%ICq&Fo zKl5395{Iao*ugO#qd+VdvS@=Tx%Z@eohAg$W9m(Q3Fud}xhB>6AqAPoXb8d6Cmq;u z8w96tr1zUcN)bU?7cZC`6voE56Vr!!T+VOenRf?%YrgJ5)J$>Xee%%y#GY7h$#uy zX}-wBS(FE(vtHK7rEa81h5gWI*S7ntxwc>1;#Y%OJJP@pyDH4?mc4K9*Q zz<4wzNr!)K{X~2E*lH+Q8t>Q+5z+uF0g;mhgkp?Ar`!{-_3<#Dr{d!pa_@`sd3VaHzq7^^BaX;t-RN4uR^Uq8Jbi-`!YYo}b#Go5A^IP07rj|DV`HnI zADb}gtrBqI?RON`zGPT&qPAe+B|n^xW^l}hQ|1-iOGw^T=k%!{a}Aj#{1c62_3dB4 zr6j8mu`*mDob)+N+z8D+aYbZJ^4WRP$bE2uB?DfsCU)H|!*{tfGqR?T3Gra>>EG5s zlgT<@k-ffZ;d^5&8dv<3)k4QOP~dSm(cs_>RnE!UySm#13lUgwceXafq_;R>)lwTZ zMb_j~?%CUdMgxc{F8bBczrv-{+^sJazSckoXQK;JCgZOI`qpGiii3CIa6@e44Y;@F z49K`uBCKyU)ef==sW_ONE86byJj;YobMFO13QlmH0{8u4Y(+hHX<9&)Hx)AD#9P;X z9WX`ylJp^6)o+p?G0UbZ+l-ry+Y-VkOqeg-RXP0bR_AXCY$xf*)*EKf4f3@DYlin~ z{o&e974yaFl{!$cY8&Y4ULEtnYL|L)K57f(>Rt=^!D^NIgIT{5@dUH1Ig6&~bt5;W z6a-nWoC!S>*h^{zz9_LAloZj0Yaz^#LDE;*! zjDQg}a^l_n@JzHn5D&u1)K$d4K-I1yz!GguC{nQ^{tQ2vN3lh@Z3hm8FO8Z=ftEYm8omHiuS=NPtgm9P>dlOk)Q++rVID+yb61cHhK5n}P&LPmtrVx74pAMAp9> zh_BahohM+WcA^9!4IT35C#o`KxVLg@0s*JJ+GP2hW7 z_bkFvnHw#iL*UKs-^1J|1S# z4+&w$_7Hd6)kXi!1K@CfC0EWNB3VMh@5^dasix6f324O|bXk`Ul6F~W?i&Yr(M zEYB;k;I5X?@f!qb5M!`P!xNGgW~mn@LG42(Yd=iwOA>*f^k;VDNZBq!KR(d7oT})l z^j*$gpJX}tT;|Giwl;$9-?y^uG9UTp#u4D%+p`xQC_6mUOc+g<%qSe#Hgo>`?i)(= z;S3UrwfLHXHFL}ZCYIZUOkizsA>C9gePY(Rj4S2>&pg(u8W2u{d>Q^i7cp>)h3VMx z(1cw;uJG*yBSB;W$l15#PzGaL|2ZyTOmUT^N8yc{W}iMbC(eY-L|3<}l{FJo!M0%V!Az7Jpg**-WJm{<)B621>~eGW z*gDbq*y7|M7JN=h|FP%@so@#ZJhKpa!#RA zRicekSVvXn*;?@&HMGuD9Ey<<=I#GQz`=U#G@6&(d4MOiD~;;VN0jD1*!cCpq9t%G ze>|QnEp0&GU{Z*`5u?`jpx@T^fG~v6*8||5>=NheKP16Xv9=QYUkfXP=3kQF-*w+C4mkQz5D&PdDvFSjhaOVI-f&!V{F0P$%!ZOuEf&lj=J?8}5D>+qYTZm2 zcGcyKlmckT%*n-!T$!;K8zfo_5t99T-Rkx!{ZYkDipbyw%v2S57*$rdNcP6`)ZCzL zh-qnNq!sYwLY+Y{$&fvJ+MS_t=mBx068ud9v;vEyHL-TCz2Uq;us_PL`Q6ZJ#*L^2 z!y1s%AxqX6Cv^q08IWr@zcLNxl7lL`oHf2&n;~7ko zQ796MXE=bZ8*;~Jx%5V#_MDl+aNu|~C3jaw^?sF|98r5d+_9x-6Kig;FB5NHTLz54 zYVtsl8Nhr5Y^`4cfJ_w-2Ng7Lzcef0ac1)Hu_CHvC^qm}0n5u=f3{c?gX|QltO@ z5bOPdX1$Tttn#qJJfK+Xm=%vjrnfYE7nkY~vfnEQD9(&h!L*?0p8tKIj$hp0Yo_}U zFb<=PYijNVrI7kG3rS;N~Nl<>VoFM&i`*PTZ*!4Xo@(Cv7` zC?kd3%K=<9@4e9=j6%KQPgCYRaED)HFLW344vYcD?sCXQ&Q0Eq2A~ zi!05Om6xC|1qX&7)Ql_>O#s%Kc|<5ns#_A-CbGW|jTov&??APEsf4}pHNWO)BAh(m z+oVT7o|XhC!V-kL9ie`RFQQcx6@`Woi)(p_2H6xV}uvj>d~O@l&O{w(}CV3{j3Xfc=#jW z5Y^ZG>+h9*d5;>(LQ-L;Z=`mwiLe@WqCE-U=ei0e)w zHsj*iC|5;aT)zEECJ7bZs<2yZ$pL9aNi?IyQBZYZk%}yg;!inR+lQ-*NA+_zAcHAf ziujvyn^f>RV8uSb^|2UF=$Sm-S9{a0KfK&*UA)AVYuPFap<|O>TUVXNPe@*xQ^ZT#wZ@A+>0gwM-yK7qi z11|rYd({UNuuh9u+WJAW)^u@P{G@$;XmAoyKm)8%VBOqMlvqZ5t^M;c1x*pY7-?wg zxTUEF(^OC5@M^bhD*Sa%sWZq024>Z8tHk;M)VFIpi8NM#;fW@KtF>kdMR70?7)bJU zEjIR<2bXA{O!inp>Hcpj)+@VX@&Vp+j*;vP!eDd5Ux5Oe7l--x*O#JA*#{#a-i3xRh{cBw*4@oB<$VrdrONi7*Co+>4rQ%64R&g0i<@GJFb@qKvW%Y70q z{G2dCpAHHiw6pUe&`$4mj|LFraf^ zg9ZCtbHWY1O49st4Iy7-^&24g;u6E5&8>WVAATcwt zkEOGty}JWD_Xz_FDu^Vm7xQ>poVS->PW&3oy!!|ae;#=j1XuG`^H+g$urJgqyJMl- zQ{8GDI9!39?8YKBu-X6y6jA$#qTfs8a&u>A=QsDH_!=M!50Bg0&f1vxXE`6-f>p<( z5^}_W#5cq@$M)hFF+Z;cpVjzQE#V_8rsqW@*GSv0yf;sA_^>Dx71c{tV z=Cpm`k)jz1-})Md+qYLJ515M_5`OlJW8!a1=*a`ezTE}jxZylr;%_ekjE}P+IljVkVynSaK$IuO^oQ9W(LD2}6gow# zINo{4L22B{gS?)&34Zg{C6LZ9jInrk!0bSrW?hi+dWEwt)~zM3ua{|9Kz!8w6AsVp zzrcSLvo@nO0p^!2p2wIv1o}5yJB)NHxAVY2J2dXB$nG3(eqY1l9~my;Q!8eB(Lh(z%QWYVv~`Y(d@h++Y06ZX=@>MF6vAYRM1=M% zc0Zc9$i?gA&Rx*fAvX^m_FVRpfyleRTY4_;O@0ASG2sW#15s{|`7hu0>y4P%)_UA_zcFB!wtY z+80~VyswLYu+^Wp%rJBWGAl@XX`KC0C3WC2z_-BnZoSqOCt$RWsu8h6vC$2Izr`QxY((D7J(7jI*G9NE$N$4QgVs$J(nymQ zjg*trNrq6wWVFkVV-WuF6o0Ajy=?y|df|Zn_kyWiI7wW@`Wc_bv9%`84&V5n7oo~ zG*JGd?0n8d`FAr&`7CvZ8E>oFAsIuN27^w_3{e*gAR3oBbS0=*XreYGGD2BGRX+|t zI8PHK8>dlpmtD*x+@onTLnFdBUadAt?}a2+^ESF6L=(IPv(Qa4jlv*B*%KBE;TJY? z^UPxHKAn0=7CR+nVYOfklUtqeth`~12GDRItFYr|Om91kBkrWpp6Kf%ua-8_`F62-29 zBIC5@o#Uprnlypwq6&4(I*PqYCG@P?b;wz^n&a}yC!m)guYU9m(6}^UQY=UQ&#S4> zj5@QunhGp9hN$YZE?Rt)Wdder-ynbam8o?%Jyv-;7Z{#>vB!v{uq5R-Dc;c}E%b5F z&m{%MLwV%p4??(gewSjt8?|W`8#R{prl&_ds6r8H zBP-$Gxti{oO2V!-VbB2r)Cr(uZCp|UClSJoAnHIf9bH6*!0{Zz`4$hB!s?96QwhNB z8dM-Z_)*QiE}s_9j0Xag>utUPV~`res(TNx*upwRFEo)T6KXXb5hE-IpN9cQ-T{Xr zxo@;XTLVJz#0rYFl9K_rLS*wzbchSUV|-tFoG%XzO$p z(%irad-2-Y{*rNT4Ly7Yz|N;fP;GwdWyp$Euw1fREa-Vg>qB7p^wCODFV1~_e(wgV z@C5;f7kCd{Dziik4}Q@Xo8}?E;!;~cbBaQjJsPZMQP5tBVw5yde2^xXbU>`J-BlgTfGM9y!3^<|n~&@$#C`X$;SnMJ(re20O&6xi1`=r9S5Vd5 z=LV=kU~xzEc`cb&-twhl@sO;F%+p_T*G4@e11lq8)UZ_OQG1Yz*wyYX0ZM;rTA83A zG8@Zp4Cur_35T;uN$?t9{|wB#ccm}mD|9u>9M^~lT~aG8nkW~tf)3eV<=t`d9(os9 zi!IRgCu0>GxI-)+J`3PR~m{(u_>?(F`j^8NP!I_$D2f&rsm3mfak8$(;8bPkI zGBIPJZ~$X6b`y@hlkfzs${hZc6X%!+j;+FfhQS+NREoR5jS_Ia!yn|>eu{C?Cuko8 zW)`znIyKE*eZh;qvfcsLbp8$t45MCp15#RHp?81A!;7$&wAAR8>e+7OHY%=ajSy&M z@7B62nW;DSXPwv+F}Rg5{s^PBCz?)-JX#aik9kpoNIZr>x-CUc>7?zEffio;%SsjM zhG$PS$F93bUN&jf+9hiq-6Z#8zH8nqB6tWlwUD8fH>!k@z*@u=2(v6!#KRzC>ZGiC zl&G(~N3!1`z|04r9N)+%WH;h&Yr45>o#3L5mk66sSbFQc#l!RR@$zX3>*Vn=*Ay*P z)pTWv%-YR?DOo;sn}G&5$tdUT&_xUkY3v+&gAop;1p|iHtZWpWhOD$lA#Yr{oI^#{8UiyYHbiFIji(BRG#~(mhjTbL91p&QW4>kOrEoVOKEr z^Tt?boyuMckrJ^uw%~iwMvr?U%0q7~pYvyVG&y zTDHPcp+}OKd-16Me(8R*AK=OKD3-k@Z^7c~m7KzeAvIko>=WU5b(|7Om)3T@Z$8Z^ z@i2iDAnq*!YL2Z*JU&g-My6XxDXAzYwt`R5$ZB0uT>hd|F^`S0bSHF1(*4W%mXox; zMTT^a40FbEk`SspYScc0veMBMR9x4?j?KB-nSI)M)PEz*TRgiH8+h9+Ayt()G_Ik4 zB)rLaN1VZ^IB>=-Z&VGixqi%8~= z&Io?x6s}z>U0Ot0kd)i3WcqgN<38-x;aULW@(8OOL#Rj#DBZx0SjoR#2UT$zMGvukMk|b3AGLL; z08Ea^gAhLMIoVw6uMI7LqRu~GYs3*o87YHhO}^iK^NIG4I;1O8Tkrjg3>o75mpc4+ zhur@%#{Ms;HcKWWGw=^rvwyz-OJ@Fe_S?T=mjAPmu#>)`@&65-^iQ>aCmDvwir2PfL*YzCY#$kV_Gz&@kX7x9m8MZob?Y5(34^IEd0o%~6O zi)6ir&n(2(8RH;lSivHFL$MM8#Es;4;FH$?Z4@R-2St6eCeSDO)axbz){-L3m70}; z&6r3g9D6)j*u~|&3zI_aCr=Od2m9(hh~Mv(7t>IXx*nL^g&)B=qqpbm&F0rD0Yb;e)#=oyGAAtlhe6<4W`C6gB8VZkt%y6a=Ychlav04^4~XTXcn1MCCy=oNS67=s#kE;qJ>|_GCmbRKD*lY7 z*r8*Dc>up9%IpoB4IHHILKRAyG*!by?0SYLJ&m9us8Og3u#fvzCgIsh0P;kI8u$(! zw{!;eEz4E&pZd$-E~GfIkOV3|>G`x3YcY+XM~LiZEAbdo#i+mAYLU9yl>j22(5N@<(u{1e_T+@o zbJ~RHiVpN$>AlmR`m_hbW-YoO9eRib>aIxlmpd8y}q8$i*s0jV$<1y^t0W6t<^Mu{v`Fi=*8sUaz>NI%Y~O_NZxZpt;H7TQ6>#^Kx3_@J*R;4 zGAxVQ$M*!9W#+ES3eKjB+M*-e@+Zm;wcZWsbcL;Hi%_9K>+763v1^Fj4XlIQ#JxOD z<~2ZlQEMTlfrH~Da;+K$BKujfQoVXqla=D<(+6<7&yq|rQe42>^P_#r_p{sx51R++ zSfr1zX!Z=`IYXAcikE3w!30cWteAFBuLDrU{Ps%BZGIN_TvYyhYw9v)=O`FB)Jk<= zJ0G^ga#Jq&yD3<^MtKdT`A9hTyVJva$O&g|E(u+Ot;89$SKx(nv$MBi-HI zAn?%LAzhLZBHbX3bax}&NJ=+S66f*Vd!G;Yr=Iul>*3Ee#+qxcHRoDujQd7dbqHJ7 z?}(~&vA%UqXHEU|K!>s4<*u9W{GcbU{XwC$w)c!}$G%jB)tJ!G3H&S4=6rORS1oN~ zxNh&tzM`Y1Ljpuw9pwhc4ri5+lTK6y-yaG50?Tny^iL38KqOUbeM_>9(()E9# z>qeGWxouFDw=0;Cxnx+RsHDX!vnrnu+zcmY(WpD+Mht80Qeb+1Ihp7VUB-L1hK+T=P_l9`vDAN9YFP39b^dhvv&9 zq`^%WoC=E&p!%1Zv{lb@izTYHBS#u2Yod+HR2dqixa6p?U8tbfA$f- z`oMA0pR(LrBV#o^m3_Vk*mn?ULUI%m!Dbk)Li(g(SAR}STQ$Cd(-Y~ljtU* zfveUuR5VsQwzTTP`^lNWNA9E~NuPe&ET&>lJoWo38^&^OnslmpSsL176?=Mpxst8a z$$s6@GNu65xqd6ax`N?YPD9&n8h*~DAZRJJO9P8he)~Nv!BFx^%{Q$6KH@;Yj+J+OdrKQqy*oPv<;iTFZfPIvrzUl`#X#~lyfqsYxh+t=IXK(kr=t*Y z#Ik+8hBJ?|k0{*lh~NA2yMGc4=U7!QAbSjGrB2^Lcke?iWrwNR(TG)?^tbU(P6v}# zPhYi;9oFV76{?UGFAL#xZsZ^kXb6_!^MMEjaX|FejYCOX>F|OCEEl!UtM8TTF43<9 zf*pT2P&3m)#KmKGnpFqO&%aJ0H9g7rSM6)T+mh zSLsPaH}9HNpWPLc!@%qAxVr*X#1P|mA$qDI>m_{5*3kfJgK?R(M~M>YC@8C zGZgocpdIQs*Xzcpsw!i;jjcd?_dLl1V>6Vo#gYCo8Kz233%n|8Y*y8CTfI*8eT%zU zy`(IcSZ8<_mSVdlqOPWPw`sU59x<6V!SYuQ;#4?=Y`Z8fQq9sU!GkJvw-OE=m5Zv5 z;xc+~tCY}W5$HK*yt&kHF2O!&HaYTM=XCI+01LsbdZ->qw!3T_y-?*H6SPVtTq}jA$9r^X1;^Q^&`oj~&Gz_#oZPk`0rS2qFd881N{yL!`{ zo55SdQ+JlD>d7N^vfWHA9=z1t^5vV(C1Rt z2z+)*0<67rPEFRbi=ClEz6w>~@1Hv9*>H!v*~O|e@@uMjz}>Q_n?=lkJuM7wLi18D z(8MTWWrALDVngmvSY(l(77v`W3cDP(6;xj^+H*B5sPjRNcg>3h?P6=baSJRzngr7t z^knn~NYj$~3qofgkn}&DKKMoRO@b^r4~llKpiI5N>LOaH)b6zDAyoE(u2mYV9HnoI zG=&Udvj|m%cp-So&Mv6}{whld#T5=g@_2EY9*|y!8V13;CTUOZzgtpmT~79{do+u) z5}=~R)UXChf+j2|C2n|3zmp?7nO_QUJpxmX)oq^DscRZrOGWcFW`f;w6@`|}E>ki) z9SuhU@wVFM7S31_Lq4)9iv+8ZAb^IaN3DN*FZ@<{E`&_JSnMly-*JVXz~-j@85 zutchbv?;|fjH0VVVyI2cbThTR+rg%@#O>K4S&}@zkq{vK_N4daUShX>JOuA{7`8~b zc2)obsD?iiIj{SyWQH1rUwqH(n_~ZYTqkTFmL#@yngKTkB|H{n%3*})*6;i5->u*( z7U~UEFla?K)WRq_$>*t7dy=V!7-9m#=nlJIBY>PP4lyLxL5LZ?AyLWalE6V3b&HRR zndkoO6c%T}ZC}W)^VXThCcq!rQGZ~re{w+*Eq;M7^^;`747TnEQL$_WN*>DXxFAZ8 zS{8ar$4b%8TI=j}*EPj!aC|57C$aQ>5gGW9VC&q-gkDKw(c2sgYl10CocBy|ow2QX zutF`3Y6|2!lyI`_8kq}^pY%~wUi(BNK?!ZVJG!(a88|?cbOns+2}W*+d$UYVp{nkl zd}w0ly@NQ%gmHOmcYpfu^hnxDk%6F}?vQG4P!uGm4XKQjD&u&ZOVS}o-?C(OK{vwgOUFgc(G-rk(PjCqoo{ZD zA^fKx9i-&q^N?fTNFx;q(PZ?Df_th@7MSFrV!D=6JmBd90#&Kt!lZ~qhIAFY zpoRGZT0)Kb%xZ%pf~jAWGvgB{rz9b@2DeEVLXW4ctwM07FbruigOUs#BHVa(4+heQ z5%DP!1os!!*VHEyD}u;9C`_lU&S43@Y7THLfe)ZJ?l#0rP*hgNe~ZH2EAusJgnw#q zWsz;I0OV>-TPQuRNi?9Bm=3j#!=R(iH83yj)j><@KpV!l22&N$SGS736>Awo z9n7K3f6fHJ$tZ%QdU68&DN#j;gJHC;eGYbNaF!?B!<2eG7ZgYiE+60=)s<3lvJ{H8 zUuOar!}y^Co1HdD<=1$|AYKp(#IKCk4A~EcW}9hr7mT}-c^H5;TLMXuoer1&R$YO# z5=N-=t>P;Laa2k%)(@hVa*90g5NG_IX3RF+?EXCVpW3T^ZQC5npYTq#N9Xm;Y5YcG z-7dGwAd$M1aQBF}VAWm}!iPo7Cz6QyM2`zdGe2=szHaKoqhp+Yw|7Q3-e3_GPGkqs z^~F&8G^CSG`UH5@q7j(wgfN&h$W`4TiIwYX7qMkx?q@#s_>GE%?6ZHq-=TU(`#L8D z55{mftx#;~qx?EM?09}dxQC*I2Gj`n{@B9K+?<`-4HgrvZk_)y-NS?89ilTCqbe`x z6$xdO6rVSW$Bm)9hv!zunn#msDH(!K2ZEWuVPddE@vFT1*t1}!2x0@_m8~6AT@buo z#F%zT{<(PxJdhZmQ<2wq&+iD_Ev$yqYlpA;13P_lH}Y%VxuITc&H*iIPv8`LKc;oE zpJDP@;L5)VGLz*3bes+u3!GkSf^aJGxL5E_-hYlC86zcDsu+7J%J3ym-$It)A14#O zlYob*NLYFkMGUTh6c3nTG6CguC}Fykti8%AAm zXIA8XYQH2>uN<4QVwECVQ!4Vg{BB=Tm-R*~a*&!gFoa~6qjpGK%m!sGPV=?jlQoRj3P|5m4boPyPMW0&pB&!uRAp(f+z5q#9`E$yXV7BC27J zzy+F9aTvHxKPhfmKo>d5_!lm%8!59 z)4PGLH3b{NrV5Qgj3ywtNYZv+r--lb%L~USJSq|=#^XZrvM{zG!MCkyd54fa#`1Hb zwZCx85dR8u*$&iFfv9Gp-iGfJN}YU|Zh_7a zO1Z9I)<0vQA|xMb@#1_}!DXvkuwZztTNE@eVndZdumkZOc5$Yg(v}<_r@tfvWK;5} zJ@W9uc0}Lvfwl=n0CdQ;e?Fo194rTOv{u)GGxa;>a+0DwPFCe{up)E9+jxKVcLDAy zuQxfVL#qUpU{wON$j}MC2?va}&N}Q)lv3aarCZ5AjO?6{WwyU6>2qolIK5*Jd%!Ln zPo3iQTH(4H_J{B|-i~o?B(vNohUQ3-Camq7Zw;7Eak-Yr{jLcZy%x7;e*C)aXpU(u z86g|hrK&Y-l`Y`rW*#ZUKq?)YyZU~sb)T&ms#%B2c>}@#DUS$ECN2xmDDJLP zYG+C=D7B6vHU^t@D1#0Twf2Z~kYD>j$Pj;mRF^uA!zQIC1HEvcPBd25xeocfO=2+5 ziAJtyI8fg1r3@Fsy3>?*lZtf`mx|i_{(=s792mv+{LoEx4Ltg$>zaqEkUTYsaGl`J zwgoothlLk_7AFT+>(h=m1n=1CEqz`YYL+|hCS*|6Ur!r!6U%%5*lBK%q9!tCz`)j_ zek`19EeP$o^PO(-kL-}3f|Hp_=1S^F$G0`Row$!fTRpA&bmXj}{L{ zU0oAlrBAxT)9QF_Y9Fs7{q>bk-9$Gym@?f)qqG1++qdF?GqSvwcG1h6GF=6Gbg;b5 zDc&1J7zXj2oMcwvgdVJoN(lBA+-cq{>ZVYy?!czZi@Yg-O)3s3MBJ9~uRb~ml#@Xw zmbew~36DnjDZzupQ(y5BZA`4E4N@nHEF$Z9OZn&0Az#aj!#t)`<^_}ZNK2XRtCA4OY!@sX z+)vPuj_2;x>pq0wU$7D)h}tC#)ol8J%C9DZ&n6(qw`m{_%RoQ)Ig$L*>1tL-I1S2; zkav?BUN|3GDkez*yEiojWA|5`(`v4T{1Yx2QL`o5g{f+(xnh5@YYmzM8 z$f}vkK#VG?xIxeNuGx9ReZ3FW`XN5?__Tdcs=CYz4OW8QD7-4!c_W&!+258bRbS|> zp*VeQGBWHtX8=>mKVZtZG|!uqly{Di4UEfy3p}dkxK^L1z$O;&Vm5viYyZsE*6(#Xh!geT}tmf#0ZrYmW1cn?NKZ}EYTOO~4?G@xF*JGpl1 zT6@+wP%V#LYgGp_ZjFfy$w^V#pA1qaV{Nl0M&_pI#FaBeTL6^@4Tn=ue4>15145(# zSz=R*G^6ru5Ijo(`eSlA<>vNE9NnxK+Qf*Q_Bi#lOjgqSVmZqJ@xuKat@XN2T}k%Q zz^!x$RAP^5&ny>pWc!8)NzP+{B%VcTbQ2|w-RJ2YlSk}_-2}W_=(ca+)<7-lgdiQ^ zt4zbqhb?Lws_zd6L(^=>Yw05EirWXXpi}Tn(9jF9Gx~=7Y|0M8={nSi4p6=TSCx_P zAyB&2C5e~_zN69kuhKi5sQlFZ6sZh0yAu+o!+Ls?@as)q^i}$XrYX~Z`$Wp0z96YX ztz$7|wnJP?Ifbo5Jy`t}q*s-U$U>i@sAA})-vANH>E?kL%3Hbht{9L-47bnrCx(LV zt_x9L-wQ;QZ*V$%^I_MVRtntO`?xT;^ih`55IGAy^X-~~40FIad;)}RMVhR%g1w$g zj(S16ks2R~n(Q>}8mF8ZmcG5CCocFq*@%gu%@a(?TL&}xk$9W#p@K%od5%b&&ULt# zlT7*={q~=g%g@e+QdT$8weAa>acs_Cs@{DaIH4Fw$i(#L4{PNP)bj|w_E&Up2=Nbq z&_Lvv4+wf88%u0LW;15LBC?8R>c*MjeK^V()`|@G4KWc>Lz%-HZohl9i#sPOMb-ov zPUp4T^rQHi5IVZ0)Et&UhK~cY5+fs{ex||M`7IISW9vZwqKMs@6~_qz zK5XuEeQ~rcwRELwmBJfd%Ipte>FU1Fg)9B%-Ett()sx8`y;HYSWckpIrLQ!-ng`*X z1eTRM`YEv8p>pG2>&GagMlg_qsd2vSgVEbZ#VN`yya*}A&`g%(86$HiyrHU#>S0Ir zPxuB)q97g}(=uI{(GN$8!`CfFya{hbG_|^4q!=N`LCIE@;3!kF#N<}%+RL13W3&Lg zM)mowzG`jRJ6&mV^x-7?$i36BOy%QT*Oun+UoYmP0uT4qDd@C$91A)%$ptg`dt_wrRT#gApNM?`Niq33&0mG2c#{!(0&Y;H8 z`?uxSI_TU6-~}s^wMTUNnNvb=&ayv|hfgrg5?msoW+ZtUpZT(VM*H}J?P^Q4QoRAH zby1Qex-6uH744d>t3n-FhoHLp`W&V&EeN5-g`N9#Z`o62R8&!oN&nWTq%%7lGez$A zrk~#WId`s1%j_2ny?EQHHOn21(Dfx1*CfGjOV?VaH`n;m6L7V4qi8;yiI4iGtAQOu zWU^nhDftp?ORl|fSpUTW_-S})qN#@59U^AX5oscugmYrTz&3LQ>Ss^TyVvz{(c4@R zI~y`Xlb#mpOD3J_DopXVf~SxTmsWz ziaIVvruQ`7ilnA_37`eDUYP~23TLTej`Jp`+@t!YAdwp4>xAY|XgfE=yeh|`MSRoZ zoY&vBm3J@IS}P)ft6obk*ThcOdE>sPTgndYePCSZhiMQqR>OHS_w_aKM z>)Z@t@!3l}f*Lz>gyvmfpRm-Mw@<==WRyOBmyEI+rBEExy8?vZ%URJn2TI)2L!+SH z<$-4ZY2@6u$6o*;%B#`q_S1*aog5GDX%H^}Q1>(f91|9d>kP2$&7gj+!R=|$H4(K4 zFYcD_GgfubVZe1gFDxXDj5$y$bTJB<-=lJYMk<>RC=|^=f(ZH2hqJF+Y9|jd#%I3i ziGyx!UqxU2av67S;_|U zt=Mrht*5(+Y{{~?C-jR8F4WXb4Zm8|3HSBAw;f}fx)??f33+g3ENr-7I|eI~GgQ5{ z6A$;BuEu>jgyFAVS#pH&eZz}z73^V#E??_L)^HA;5ouHmW8n{?H0Rz9;WPD$*kn!X zn)03qzW%gf(SZSjBxmXK`pJR6H4va`krL$5nnW5LE~%siA&&D9P9NLdbaoaIH(qJM z+98?1>?B+&kd@^Hj>neNw!YOU0houjX^~3i&Rq5{Zz-2>a3fCjibHw?^IP8}R^8Oz zjnIT+A!{m&MVp2B)cY~Xhq1u8fryH;rWqn<5A8UY>utf{`G*L->^)4*hI%wJ3P^7B zIHRE>YWFE$Y=f0p@;iZys051M&;Z=&jw9*ee_lDY8$A@{ZK=P?ohc5r}3cvHMonxAQHq3EJC;=HG)}06qjv zmLn%^=+&0it=K|0mZa$=cr_w2LJOe~5<=PESQ&7e3uoJSvg%L5(D6{I+Tx+@z|(5& z2*-`dw5!qi7;=MsCe(_f)v1s%dT`cQt}4j0k)p8MI;%xv#rpaV|3!JC*VkJJ4k}-? z(JnTQw#l#-`%6;s?!3NBP?_Zkfe9kekEPr%q2EosVs@#0{8&ql5NN2-+YDV_vk)_A z&z?cq?T#ylVX1bkf9aBfU~0)y!3$gaq&W}M2+&iHmeWtFn`n{hOf-3 zE;F|W4S}#K$})M^s_s#L@f~+H(an?SKpk>uADkKgns4}34wtv{iQz}f@}isQx|5uH zrSnX#irP$4m&H$(?5_*^3;PLTZsC6v)hNR+uw(-TC>$W4Pyc_{!XW+qG0e%?!`jS= z!NkewPhZ1+O#XaTxtK*Jlvc7cqJmIqJyBthY_*zx-=a{$lKclyBtkU!{1>UPZZyY(e@=1+8|1`&L#6BuBx7TIj9 z%{r9~w{uQ(Biqv3@m$jJ8LUYbXk0m|g1d<0Jl)*)^Jj%9xXj8Ul`gB6W+d3Wic7II zrJ9yx=Z|A?^RX7&D*5%`K+3J9D=lJo<#&3+%mj>LUuU5P(P9PIZ+@Ya zn=iCD&SYBT8zCc#DHk3NMQzM_OM?C(mxoh1_a2(1e^X1t2mVT1NikcJHG%+RkoRMH z(&BOwD>YJ6;Mis1uv2pIxU@c1@(}5Ywf1J6Jg`2^J~pGg;wD63=xgqIj~5i=rmrx; z{?50}zD|bjnS#dH3=XPpnMm!<#nE2sh&vGE#oiopLR>JX3H< z4OAyhImef8^Xmqe9P32@CsdpHsEdau@~W1n`gI(#`R?hDVmraHQOi#|Y0Qhu-OK|9 z7os0-F;`ss?5FHJ4$g^Ym?hcb#!NI3UvRO8Cd)rU|Co3Qw+b@^z=_xQ$BBph_r!BG zb9DMITrvM6@}r{nmUss&;1OdAQxyqW+N{1gj$VZ1onBb&K!p5 zW?&~mpHqWK_LbawDK10oMMEj#Dn_W2y^?t}}GL5C{CX355ayHrcx`G#c_Z%qX~+6UavMR$@=T-U)eoDNw9m zE3rvuND3fG47xL=b=sqez)Ug8i`QRJM;Y=-GOeTy0xT zS+`BuaK_Kb#eKH6rKhh#AyvROy4?)wB*idfeiKdPR*<)bM&G7=6Bncy?m0-|z};kr zA!9I)79e1bU?8o+C~b=m?3mLEv~=Mbfqton4DppiQ@xAPnx-y2MZ9vq@s^F*${k<% z!px{xL0d@GyAtWs#1{6?HRvxoDw>xxjOM=avFgihq z4=K0X#N8{Gd}G4l%Y}eVS^DZ>6R17n&dSB=f=9&?|G^0;uHTGqt|jQo@lH+TMWnzX z`07QwVB&~(3e2@6U|&X!=BR76y1B4osGytw2?O)RRP8)uFl?@2*=1yVJDZwWex8#% zVki;&_tSLY_eN#h+Zi3mMl3NjN8_wRK5RGLp@OrrUvZH;?FX|LjO4|J4!)0fPO{P6gyi=*Lf>0MGx{_kjcfy#-b%D$4v1>ECyH zgr7*lf0InCfeJrnzc9W960QDIMfn%;pF)azez;v;m5$gh?{zWu4G64WR zZ-2R;`VCx^1;)w-2=@i<+OU6uI|HjaejNBe=>n~Ejf{<)eyt7m8~SM}SNAQDUX2L? zLh=(`0_k6LplR>l=^MB>TU!5mQ}~-ON<;;tX}~teWCjor{GY;9VEpeerpEvO5uQ67 z)BF^nmiT{1_@OrrG}JS;u>?vv+1dX0;ehA!7q-dIr{fovpTqsGGWRzl_1y9F=i^{v z{UsPU1O6K`<==#RZcg~~`A_EjL%82o2LC3^a|5KGk4lH{{}JYozK8z`_p7t>;@M<) z64Dp_IoxkOmi|Tl=Td(zZ%_P;To+LJ8}c8$mVOiExvuB){Obaw6n@VCx5D}VBL8Y+ zpr7UcBvc1{0{n3|`ISoOzhM6i#&hvPv}drofbahU`;%JeZ@AB8;(q43>;F%Ze-J|a z7xz~O5Bn_NCn0mAzqr4aock|uKQAO@&vCz!p8FT^p85ui0|YSLU(uhxT|xY55EjG!O858mmA@F*AFpMeKe7Hi zuDe-(A^-T221xz!lIEXr{b!;*e;h&jDbbzWzp1}}_WebsKlWA6cW=l)F%1j;V*cr| Z=Kn$=fxD3(n*@B2YgiDF;Nl 4 - [modulename] => assign - [name] => Fully Automated DTA Assignment - [intro] => This assignment was created by the final automation script. - [introformat] => 1 - [section] => 1 - [visible] => 1 - [assignsubmission_dta_enabled] => 1 - [assignsubmission_file_enabled] => 1 - [dta] => 1 - [tests_draft_dta] => 121728303 - [assignsubmission_file_maxsizebytes] => 0 - [cmidnumber] => - [assignsubmission_file_maxfiles] => 20 - [assignsubmission_onlinetext_enabled] => 0 - [submissiondrafts] => 0 - [requiresubmissionstatement] => 0 - [requireallteammemberssubmit] => 0 - [teamsubmission] => 0 - [sendnotifications] => 1 - [sendlatenotifications] => 0 - [sendstudentnotifications] => 1 - [grade] => 100 - [blindmarking] => 0 - [markingworkflow] => 0 - [markingallocation] => 0 - [attemptreopenmethod] => none - [allowsubmissionsfromdate] => 1753175949 - [duedate] => 1753780749 - [cutoffdate] => 0 - [gradingduedate] => 0 - [module] => 1 - [timemodified] => 1753175949 - [alwaysshowdescription] => 0 - [completionsubmit] => 0 -) - -DEBUG: Returned object from add_moduleinfo(): -stdClass Object -( - [course] => 4 - [modulename] => assign - [name] => Fully Automated DTA Assignment - [intro] => This assignment was created by the final automation script. - [introformat] => 1 - [section] => 1 - [visible] => 1 - [assignsubmission_dta_enabled] => 1 - [assignsubmission_file_enabled] => 1 - [dta] => 1 - [tests_draft_dta] => 121728303 - [assignsubmission_file_maxsizebytes] => 0 - [cmidnumber] => - [assignsubmission_file_maxfiles] => 20 - [assignsubmission_onlinetext_enabled] => 0 - [submissiondrafts] => 0 - [requiresubmissionstatement] => 0 - [requireallteammemberssubmit] => 0 - [teamsubmission] => 0 - [sendnotifications] => 1 - [sendlatenotifications] => 0 - [sendstudentnotifications] => 1 - [grade] => 100 - [blindmarking] => 0 - [markingworkflow] => 0 - [markingallocation] => 0 - [attemptreopenmethod] => none - [allowsubmissionsfromdate] => 1753175949 - [duedate] => 1753780749 - [cutoffdate] => 0 - [gradingduedate] => 0 - [module] => 1 - [timemodified] => 1753175949 - [alwaysshowdescription] => 0 - [completionsubmit] => 0 - [instance] => 3 - [coursemodule] => 6 - [groupingid] => 0 - [completion] => 0 - [completionview] => 0 - [completionexpected] => 0 - [completionpassgrade] => 0 - [completiongradeitemnumber] => - [conditiongradegroup] => Array - ( - ) - - [conditionfieldgroup] => Array - ( - ) - - [visibleoncoursepage] => 1 - [downloadcontent] => 1 - [groupmode] => 0 - [beforemod] => - [gradingman] => grading_manager Object - ( - [context:protected] => core\context\module Object - ( - [_id:protected] => 19 - [_contextlevel:protected] => 70 - [_instanceid:protected] => 6 - [_path:protected] => /1/3/18/19 - [_depth:protected] => 4 - [_locked:protected] => 0 - ) - - [component:protected] => mod_assign - [area:protected] => - [areacache:grading_manager:private] => - ) - - [showgradingmanagement] => -) - -3 diff --git a/newupdatedFile/moodle.ini b/newupdatedFile/moodle.ini deleted file mode 100644 index cc0cce8..0000000 --- a/newupdatedFile/moodle.ini +++ /dev/null @@ -1 +0,0 @@ -max_input_vars = 5000 diff --git a/newupdatedFile/nginx.conf b/newupdatedFile/nginx.conf deleted file mode 100644 index 7d7cb39..0000000 --- a/newupdatedFile/nginx.conf +++ /dev/null @@ -1,34 +0,0 @@ -worker_processes 5; -worker_rlimit_nofile 8192; - -events { - worker_connections 4096; -} - -http { - default_type application/octet-stream; - sendfile on; - tcp_nopush on; - server_names_hash_bucket_size 128; - - proxy_cache_path /opt/cache levels=1:2 keys_zone=dta_cache:10m max_size=10g inactive=60m use_temp_path=off; - - server { - listen 8080; - - location /v1/task { - proxy_cache dta_cache; - proxy_cache_background_update on; - proxy_cache_lock on; - proxy_cache_key "$scheme$request_uri"; - proxy_cache_valid 200 302 1d; - proxy_cache_methods GET HEAD POST; - - proxy_pass http://backendcomposedta:8080/v1/task; - } - - location /v1/unittest { - proxy_pass http://backendcomposedta:8080/v1/unittest; - } - } -} diff --git a/newupdatedFile/student-dir.txt b/newupdatedFile/student-dir.txt deleted file mode 100644 index 50f42b1..0000000 --- a/newupdatedFile/student-dir.txt +++ /dev/null @@ -1 +0,0 @@ -dta::https://transfer.hft-stuttgart.de/gitlab/cota/examples/cota-submission-calc-jdk.git::none::none::/ diff --git a/newupdatedFile/teacher-dta-dir-test-runner-jdk21.txt b/newupdatedFile/teacher-dta-dir-test-runner-jdk21.txt deleted file mode 100644 index f5c9b2f..0000000 --- a/newupdatedFile/teacher-dta-dir-test-runner-jdk21.txt +++ /dev/null @@ -1 +0,0 @@ -dta::https://transfer.hft-stuttgart.de/gitlab/cota/examples/cota-test-course-jdk.git::none::none::/topic02/exercise04::cota-runner-jdk21-junit5 \ No newline at end of file diff --git a/newupdatedFile/test-moodle.sh b/newupdatedFile/test-moodle.sh deleted file mode 100644 index 8e94cd0..0000000 --- a/newupdatedFile/test-moodle.sh +++ /dev/null @@ -1,524 +0,0 @@ -#!/bin/bash -set -e - -# ============================================================================== -# DEFINITIVE Moodle CI/CD AUTOMATION SCRIPT V10 (ROBUST ERROR CHECKING) -# ============================================================================== - -# --- Configuration (UPDATE THESE!) --- -MOODLE_URL="http://localhost:8080" -BACKEND_URL="http://localhost:8081" -MOODLE_ADMIN_USER="user" -MOODLE_ADMIN_PASS="QAYwsx@12345" -# Automation User (Course creator) -AUTOMATION_USER_NAME="maaeruser" -AUTOMATION_USER_PASS="AutomationPassword123" -# Student User (created by script) -STUDENT_USER_NAME="tstudent" -STUDENT_USER_PASS="StudentPassword123" -DTA_CONFIG_FILE="teacher-dta-dir-test-runner-jdk21.txt" -DTA_STUDENT_SUBMISSION_FILE="student-dir.txt" -# --- Static Configuration --- -MOODLE_CONTAINER="moodle-web-official" -MOODLE_SYSTEM_USER="www-data" -MOODLE_ROOT_IN_CONTAINER="/var/www/html" -PLUGIN_ZIP_NAME="dta.zip" -CUSTOM_SERVICE_NAME="dta_automation_service" -DB_CONTAINER_NAME="moodle-db-official" -DB_ROOT_PASSWORD="verysecretpassword" -DB_NAME="moodle" - -# ============================================================================== -# SCRIPT EXECUTION -# ============================================================================== - -echo "🚀 Starting Definitive Moodle Automation Script..." - -# --- Pre-flight Checks & Plugin Install (Step 1) --- -if ! docker ps --format '{{.Names}}' | grep -qw "$MOODLE_CONTAINER"; then echo "❌ Moodle container not running." && exit 1; fi -if [ ! -f "$PLUGIN_ZIP_NAME" ]; then echo "❌ Plugin ZIP file not found." && exit 1; fi -if [ ! -f "$DTA_CONFIG_FILE" ]; then echo "❌ DTA config file not found." && exit 1; fi -echo "✅ Pre-flight checks passed." - -echo "📦 Step 1: Installing custom plugin..." -# docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" bash -c "set -e; mkdir -p '$MOODLE_ROOT_IN_CONTAINER/mod/assign/submission'; unzip -o '/tmp/$PLUGIN_ZIP_NAME' -d '$MOODLE_ROOT_IN_CONTAINER/mod/assign/submission'; php '$MOODLE_ROOT_IN_CONTAINER/admin/cli/upgrade.php' --non-interactive; php '$MOODLE_ROOT_IN_CONTAINER/admin/cli/purge_caches.php'" -echo "✅ Plugin installed." - -echo " -> Enabling Web Service " -docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$MOODLE_ROOT_IN_CONTAINER/admin/cli/cfg.php" --name=enablemobilewebservice --set=1 - -# --- Step 2: Create and Configure a Custom Web Service --- -echo " -> Clearing cURL security settings..." -docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$MOODLE_ROOT_IN_CONTAINER/admin/cli/cfg.php" --name=curlsecurityblockedhosts --set="" -docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$MOODLE_ROOT_IN_CONTAINER/admin/cli/cfg.php" --name=curlsecurityallowedport --set="" - -echo " -> Enabling Web Service File Uploads (System-wide)..." -docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$MOODLE_ROOT_IN_CONTAINER/admin/cli/cfg.php" --name=webserviceuploaddisabled --set=0 -# --- THIS IS THE FINAL FIX --- -# Enable username/password authentication for web service token generation -echo " -> Enabling password authentication for web services..." -docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$MOODLE_ROOT_IN_CONTAINER/admin/cli/cfg.php" --name=enablewsauthpassword --set=1 - -# --- Step 4: Create Dedicated API Role and Automation User --- -echo "🧑‍💻 Step 4: Creating dedicated API Role and Automation User..." -# This is a more advanced version of the user creation script -PHP_USER_SCRIPT_PATH="/tmp/create_api_user.php" -LOCAL_PHP_USER_SCRIPT_PATH="/tmp/local_create_api_user.php" -cat > "$LOCAL_PHP_USER_SCRIPT_PATH" <libdir.'/clilib.php'); -require_once(\$CFG->dirroot.'/user/lib.php'); -global \$DB; - -\$username = \$argv[1]; \$password = \$argv[2]; -if (!\$DB->record_exists('user', ['username' => \$username])) { - \$user = new stdClass(); - \$user->username = \$username; \$user->firstname = 'API'; \$user->lastname = 'User'; - \$user->email = 'api.user@example.com'; \$user->confirmed = 1; - \$user->mnethostid = \$CFG->mnet_localhost_id; \$user->auth = 'manual'; - \$userid = user_create_user(\$user, false, false); - \$user = \$DB->get_record('user', ['id' => \$userid]); - \update_internal_user_password(\$user, \$password); - echo "Created user '\$username' with ID: \$userid\n"; - - // Create a new role for API users if it doesn't exist - \$rolename = 'API User'; - \$apicapability = 'moodle/webservice:createtoken'; - \$restapicapability = 'webservice/rest:use'; - \$coursecapability = 'moodle/course:create'; - \$enrolcapability = 'enrol/manual:enrol'; - if (!\$apirole = \$DB->get_record('role', ['shortname' => 'apiuser'])) { - \$apiroleid = create_role(\$rolename, 'apiuser', 'Role for API access'); - \$apirole = \$DB->get_record('role', ['id' => \$apiroleid]); - echo "Created role '\$rolename' with ID: \$apiroleid\n"; - } - // Ensure the capability is assigned to the role - \$context = \core\context\system::instance(); - assign_capability(\$apicapability, CAP_ALLOW, \$apirole->id, \$context->id); - assign_capability(\$restapicapability, CAP_ALLOW, \$apirole->id, \$context->id); - assign_capability(\$coursecapability, CAP_ALLOW, \$apirole->id, \$context->id); - assign_capability(\$enrolcapability, CAP_ALLOW, \$apirole->id, \$context->id); - echo "Assigned capability '\$apicapability' to role '\$rolename'\n"; - - // Assign our automation user to this new role at the system level - role_assign(\$apirole->id, \$userid, \$context->id); -// Get the IDs for the roles we need to assign -\$managerroleid = \$DB->get_field('role', 'id', ['shortname' => 'manager']); -\$coursecreatorroleid = \$DB->get_field('role', 'id', ['shortname' => 'coursecreator']); -\$systemcontext = \core\context\system::instance(); - -// Assign the 'Manager' and 'Course creator' roles to the new user at the system context -role_assign(\$managerroleid, \$userid, \$systemcontext->id); -role_assign(\$coursecreatorroleid, \$userid, \$systemcontext->id); - echo "Assigned user '\$username' to role '\$rolename'\n"; -} else { - \$userid = \$DB->get_field('user', 'id', ['username' => \$username]); - echo "User '\$username' already exists with ID: \$userid\n"; -} - -// Create Student User -\$student_username = \$argv[3]; \$student_password = \$argv[4]; -if (!\$DB->record_exists('user', ['username' => \$student_username])) { - \$student = new stdClass(); \$student->username = \$student_username; \$student->firstname = 'Test'; \$student->lastname = 'Student'; - \$student->email = 'student@example.com'; \$student->confirmed = 1; \$student->mnethostid = \$CFG->mnet_localhost_id; \$student->auth = 'manual'; - \$studentid = user_create_user(\$student, false, false); - \$student = \$DB->get_record('user', ['id' => \$studentid]); - \update_internal_user_password(\$student, \$student_password); - echo "Created user '\$student_username' with ID: \$studentid\n"; - - // Create a new role for API users if it doesn't exist - \$rolename = 'API Student user'; - \$apicapability = 'moodle/webservice:createtoken'; - \$restapicapability = 'webservice/rest:use'; - if (!\$apirole = \$DB->get_record('role', ['shortname' => 'apistudentuser'])) { - \$apiroleid = create_role(\$rolename, 'apistudentuser', 'Role for API access'); - \$apirole = \$DB->get_record('role', ['id' => \$apiroleid]); - echo "Created role '\$rolename' with ID: \$apiroleid\n"; - } - // Ensure the capability is assigned to the role - \$context = \core\context\system::instance(); - assign_capability(\$apicapability, CAP_ALLOW, \$apirole->id, \$context->id); - assign_capability(\$restapicapability, CAP_ALLOW, \$apirole->id, \$context->id); - echo "Assigned capability '\$apicapability' to role '\$rolename'\n"; - - // Assign our automation user to this new role at the system level - role_assign(\$apirole->id, \$studentid, \$context->id); - echo "Assigned user '\$username' to role '\$rolename'\n"; -} -EOF -docker cp "$LOCAL_PHP_USER_SCRIPT_PATH" "$MOODLE_CONTAINER:$PHP_USER_SCRIPT_PATH" -docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$PHP_USER_SCRIPT_PATH" "$AUTOMATION_USER_NAME" "$AUTOMATION_USER_PASS" "$STUDENT_USER_NAME" "$STUDENT_USER_PASS" -docker exec "$MOODLE_CONTAINER" rm "$PHP_USER_SCRIPT_PATH" -rm "$LOCAL_PHP_USER_SCRIPT_PATH" -echo "✅ Automation and student users are configured." - -echo "⚙️ Step 2: Creating and configuring a dedicated web service..." -docker exec -i "$DB_CONTAINER_NAME" mysql -u root -p"$DB_ROOT_PASSWORD" "$DB_NAME" < Purging Moodle caches.." -docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$MOODLE_ROOT_IN_CONTAINER/admin/cli/purge_caches.php" -sleep 15 - -# --- Step 6: Assign System Roles via API (The Definitive Fix) --- -echo "🛡️ Step 6: Assigning System Roles to Automation User..." -# We use the main site admin's token for this one-time setup task, as it's guaranteed to have permissions. -ADMIN_SETUP_TOKEN_RESPONSE=$(curl --fail -sS -L -X POST "$MOODLE_URL/login/token.php" --data-urlencode "username=$MOODLE_ADMIN_USER" --data-urlencode "password=$MOODLE_ADMIN_PASS" --data-urlencode "service=moodle_mobile_app") -ADMIN_SETUP_TOKEN=$(echo "$ADMIN_SETUP_TOKEN_RESPONSE" | jq -r '.token') - -# Get IDs needed for role assignment -AUTOMATION_USER_ID=$(docker exec "$DB_CONTAINER_NAME" mysql -u root -p"$DB_ROOT_PASSWORD" "$DB_NAME" -se "SELECT id FROM mdl_user WHERE username='$AUTOMATION_USER_NAME';") -MANAGER_ROLE_ID=$(docker exec "$DB_CONTAINER_NAME" mysql -u root -p"$DB_ROOT_PASSWORD" "$DB_NAME" -se "SELECT id FROM mdl_role WHERE shortname='manager';") -COURSE_CREATOR_ROLE_ID=$(docker exec "$DB_CONTAINER_NAME" mysql -u root -p"$DB_ROOT_PASSWORD" "$DB_NAME" -se "SELECT id FROM mdl_role WHERE shortname='coursecreator';") -SYSTEM_CONTEXT_ID=10 # The ID for the System context is always 1 - -# Assign Manager and Course Creator roles to the automation user -curl --fail -sS -L -X POST "$MOODLE_URL/webservice/rest/server.php" \ - -d "wstoken=$ADMIN_SETUP_TOKEN" -d "wsfunction=core_role_assign_roles" -d "moodlewsrestformat=json" \ - -d "assignments[0][roleid]=$MANAGER_ROLE_ID" -d "assignments[0][userid]=$AUTOMATION_USER_ID" -d "assignments[0][contextid]=$SYSTEM_CONTEXT_ID" \ - -d "assignments[1][roleid]=$COURSE_CREATOR_ROLE_ID" -d "assignments[1][userid]=$AUTOMATION_USER_ID" -d "assignments[1][contextid]=$SYSTEM_CONTEXT_ID" > /dev/null -echo "✅ Assigned System Roles to Automation User." -docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$MOODLE_ROOT_IN_CONTAINER/admin/cli/purge_caches.php" -sleep 15 - -# --- THIS IS THE CRITICAL FIX --- -echo " -> Purging Moodle caches to apply service changes..." -docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$MOODLE_ROOT_IN_CONTAINER/admin/cli/purge_caches.php" -echo "✅ Caches purged." -sleep 5 - - -echo "🔐 Step 3: Obtaining API Token...$AUTOMATION_USER_NAME..$AUTOMATION_USER_PASS" -TOKEN_RESPONSE=$(curl --fail -sS -L -X POST "$MOODLE_URL/login/token.php" \ - --data-urlencode "username=$AUTOMATION_USER_NAME" \ - --data-urlencode "password=$AUTOMATION_USER_PASS" \ - --data-urlencode "service=$CUSTOM_SERVICE_NAME") - -# Check for a Moodle error in the response FIRST -if echo "$TOKEN_RESPONSE" | jq -e 'if type=="object" and .error then true else false end' > /dev/null; then - echo "❌ Moodle API returned an error when requesting a token:" - echo "$TOKEN_RESPONSE" | jq . - exit 1 -fi - -TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.token') -echo "✅ Successfully obtained API token. $TOKEN" - - -# --- Step 4: Create a Course --- -echo "📚 Step 4: Creating a new course..." -COURSE_SHORTNAME="DTA-Course-$(date +%s)" -COURSE_RESPONSE=$(curl --fail -sS -L -X POST "$MOODLE_URL/webservice/rest/server.php" \ - -d "wstoken=$TOKEN" -d "wsfunction=core_course_create_courses" -d "moodlewsrestformat=json" \ - -d "courses[0][fullname]=DTA Test Course" -d "courses[0][shortname]=$COURSE_SHORTNAME" -d "courses[0][categoryid]=1") - -# --- THIS IS THE ROBUST ERROR CHECK --- -# First, check if the response is a Moodle exception object. -# The `jq -e` command sets an exit code, which works perfectly with `if`. -if echo "$COURSE_RESPONSE" | jq -e 'if type=="object" and .exception then true else false end' > /dev/null; then - echo "❌ Moodle API returned an exception during course creation:" - echo "$COURSE_RESPONSE" | jq . # Pretty-print the JSON error - exit 1 -fi - -# If not an exception, try to parse the ID. This handles both single-object and array responses. -NEW_COURSE_ID=$(echo "$COURSE_RESPONSE" | jq -r 'if type=="array" then .[0].id else .id end') - -if [ -z "$NEW_COURSE_ID" ] || [ "$NEW_COURSE_ID" == "null" ]; then - echo "❌ Failed to parse a valid Course ID from the API response." - echo "Full API Response:" - echo "$COURSE_RESPONSE" | jq . - exit 1 -fi -echo "✅ Successfully created course '$COURSE_SHORTNAME' with ID: $NEW_COURSE_ID" -# --- END OF ROBUST ERROR CHECK --- - -# --- Step 5: Enroll Admin User into the New Course --- -echo "🧑‍🏫 Step 5: Enrolling you as a teacher in the new course..." -# First, get the User ID for your admin user -AUTOMATION_USER_ID=$(docker exec "$DB_CONTAINER_NAME" mysql -u root -p"$DB_ROOT_PASSWORD" "$DB_NAME" -se "SELECT id FROM mdl_user WHERE username='$AUTOMATION_USER_NAME';") -# The roleid for a Teacher is typically 3 (editingteacher) or 4 (teacher). We'll use 3. -TEACHER_ROLE_ID=3 - -# Add the enrol_manual_enrol_users function to our service -docker exec -i "$DB_CONTAINER_NAME" mysql -u root -p"$DB_ROOT_PASSWORD" "$DB_NAME" -e "INSERT IGNORE INTO mdl_external_services_functions (externalserviceid, functionname) VALUES ((SELECT id FROM mdl_external_services WHERE shortname = '$CUSTOM_SERVICE_NAME'), 'enrol_manual_enrol_users');" -# Purge cache to recognize the new function -docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$MOODLE_ROOT_IN_CONTAINER/admin/cli/purge_caches.php" > /dev/null - -ENROL_RESPONSE=$(curl --fail -sS -L -X POST "$MOODLE_URL/webservice/rest/server.php" \ - -d "wstoken=$TOKEN" -d "wsfunction=enrol_manual_enrol_users" -d "moodlewsrestformat=json" \ - -d "enrolments[0][roleid]=$TEACHER_ROLE_ID" \ - -d "enrolments[0][userid]=$AUTOMATION_USER_ID" \ - -d "enrolments[0][courseid]=$NEW_COURSE_ID") - -# A successful response is null, so we check for an exception instead. -if echo "$ENROL_RESPONSE" | jq -e 'if type=="object" and .exception then true else false end' > /dev/null; then - echo "❌ Moodle API returned an exception during user enrollment:" - echo "$ENROL_RESPONSE" | jq . - exit 1 -fi -echo "✅ Successfully enrolled you in the course." - -# --- Step 5: Upload Configuration File for DTA Plugin --- -echo "📤 Step 5: Uploading config file for DTA plugin..." -FILE_UPLOAD_RESPONSE=$(curl --fail -sS -L -X POST "$MOODLE_URL/webservice/upload.php" \ - -F "token=$TOKEN" -F "filearea=draft" -F "itemid=0" -F "file=@$DTA_CONFIG_FILE") -DTA_FILE_ITEMID=$(echo "$FILE_UPLOAD_RESPONSE" | jq -r 'if type=="array" then .[0].itemid else .itemid end') - -if [ -z "$DTA_FILE_ITEMID" ] || [ "$DTA_FILE_ITEMID" == "null" ]; then - echo "❌ Failed to upload DTA config file. Response:" - echo "$FILE_UPLOAD_RESPONSE" | jq . - exit 1 -fi -echo "✅ DTA config file uploaded. Received Item ID: $DTA_FILE_ITEMID" - -# --- Step 6: Create Assignment via Custom PHP Script --- -echo "📝 Step 6: Creating assignment with detailed error logging..." -PHP_ASSIGN_SCRIPT_PATH="/tmp/create_assignment.php" -LOCAL_PHP_ASSIGN_SCRIPT_PATH="/tmp/local_create_assignment.php" - -# --- This PHP script now has a try/catch block for detailed errors --- -cat > "$LOCAL_PHP_ASSIGN_SCRIPT_PATH" <dirroot.'/course/modlib.php'); -global \$DB, \$USER; - -// programmatically "log in" as the admin user -// This gives file_save_draft_area_files the user context it needs. -\$adminusername = "$AUTOMATION_USER_NAME"; -\$adminuser = \$DB->get_record('user', ['username' => \$adminusername, 'deleted' => 0], '*', MUST_EXIST); -\core\session\manager::set_user(\$adminuser); -// The global $USER object is now populated. - -if (count(\$argv) < 3) { exit("Error: Course ID and DTA Item ID are required.\n"); } -\$courseid = (int)\$argv[1]; -\$dta_itemid = (int)\$argv[2]; - -\$course = \$DB->get_record('course', ['id' => \$courseid], '*', MUST_EXIST); - -\$data = new stdClass(); - -// --- Core Module Details --- -\$data->course = \$courseid; -\$data->modulename = 'assign'; -\$data->name = 'Fully Automated DTA Assignment'; -\$data->intro = 'This assignment was created by the final automation script.'; -\$data->introformat = FORMAT_HTML; -\$data->section = 1; -\$data->visible = 1; - -// --- Submission Settings --- -\$data->assignsubmission_dta_enabled = 1; -\$data->assignsubmission_file_enabled = 1; - -// --- THIS IS FIX #1: Correct property name for the DTA file manager --- -// This assumes the form element for your file manager is named 'package_filemanager' -// The final property name becomes assignsubmission_dta_package_filemanager -\$data->dta = 1; // Explicitly enable the dta group of settings -\$data->tests_draft_dta = \$dta_itemid; -\$data->assignsubmission_file_maxsizebytes = 0; -\$data->cmidnumber = ''; -\$data->assignsubmission_file_maxfiles = 20; - -\$data->assignsubmission_onlinetext_enabled = 0; -\$data->submissiondrafts = 0; // Final submissions cannot be edited -\$data->requiresubmissionstatement = 0; // The direct fix for the error -\$data->requireallteammemberssubmit = 0; -\$data->teamsubmission = 0; - -// --- Notification Settings --- -\$data->sendnotifications = 1; -\$data->sendlatenotifications = 0; -\$data->sendstudentnotifications = 1; - -// --- Grading Settings --- -\$data->grade = 100; // Max grade -\$data->blindmarking = 0; -\$data->markingworkflow = 0; -\$data->markingallocation = 0; -\$data->attemptreopenmethod = 'none'; - -// --- Date Settings --- -\$data->allowsubmissionsfromdate = time(); -\$data->duedate = time() + (7 * 24 * 60 * 60); // Due in 7 days -\$data->cutoffdate = 0; // No hard cutoff -\$data->gradingduedate = 0; - -// --- Moodle Magic - these fields are necessary but we don't need to change them --- -\$data->module = \$DB->get_field('modules', 'id', ['name' => 'assign'], MUST_EXIST); -\$data->timemodified = time(); -\$data->alwaysshowdescription = 0; -\$data->completionsubmit = 0; - -// --- DEBUG: Print the exact data object we are sending --- -fwrite(STDERR, "DEBUG: Data object being sent to add_moduleinfo():\n"); -fwrite(STDERR, print_r(\$data, true) . "\n"); - -try { - // Attempt to create the assignment - \$cm = add_moduleinfo(\$data, \$course); - fwrite(STDERR, "DEBUG: Returned object from add_moduleinfo():\n"); - fwrite(STDERR, print_r(\$cm, true) . "\n"); - if (!\$cm || !is_object(\$cm) || !\$cm->coursemodule) { - fwrite(STDERR, "Error: add_moduleinfo() returned invalid object\n"); - exit(1); - } - // Some versions: id = course module id, instance = assignment id - \$assignmentid = property_exists(\$cm, 'instance') ? \$cm->instance : null; - \$coursemoduleid = property_exists(\$cm, 'coursemodule') ? \$cm->coursemodule : null; - - if (!\$assignmentid) { - fwrite(STDERR, "Error: Could not extract assignment ID\n"); - exit(1); - } - - rebuild_course_cache(\$course->id, true); - - // ✅ Echo only the assignment ID (for Bash) - echo \$assignmentid . "\n"; - -} catch (Exception \$e) { - // If anything goes wrong, catch the exception and print all details - echo "\n!!!!!! SCRIPT FAILED: An Exception was caught! !!!!!!\n\n"; - echo "Error Type: " . get_class(\$e) . "\n"; - echo "Error Message: " . \$e->getMessage() . "\n\n"; - - // Moodle's database exceptions often have extra useful debug info - if (!empty(\$e->debuginfo)) { - echo "Moodle Debug Info:\n" . \$e->debuginfo . "\n\n"; - } - - echo "Stack Trace:\n" . \$e->getTraceAsString() . "\n"; - exit(1); // Exit with an error code -} -EOF - -docker cp "$LOCAL_PHP_ASSIGN_SCRIPT_PATH" "$MOODLE_CONTAINER:$PHP_ASSIGN_SCRIPT_PATH" -NEW_ASSIGNMENT_ID=$(docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$PHP_ASSIGN_SCRIPT_PATH" "$NEW_COURSE_ID" "$DTA_FILE_ITEMID" 2>&1 | tee full_output.log | tail -n1) -docker exec "$MOODLE_CONTAINER" rm "$PHP_ASSIGN_SCRIPT_PATH" -rm "$LOCAL_PHP_ASSIGN_SCRIPT_PATH" -echo " -> Created assignment with ID: $NEW_ASSIGNMENT_ID" -echo "✅ Test scenario created." - -# --- Step 8: Student Submission Workflow --- -echo "🧑‍🎓 Step 8: Beginning student submission workflow..." -STUDENT_USER_ID=$(docker exec "$DB_CONTAINER_NAME" mysql -u root -p"$DB_ROOT_PASSWORD" "$DB_NAME" -se "SELECT id FROM mdl_user WHERE username='$STUDENT_USER_NAME';") -# Role 5 is the standard 'Student' role -STUDENT_ROLE_ID=5 -STUDENT_ENROL_RESPONSE=$(curl --fail -sS -L -X POST "$MOODLE_URL/webservice/rest/server.php" \ - -d "wstoken=$TOKEN" -d "wsfunction=enrol_manual_enrol_users" -d "moodlewsrestformat=json" \ - -d "enrolments[0][roleid]=$STUDENT_ROLE_ID" \ - -d "enrolments[0][userid]=$STUDENT_USER_ID" \ - -d "enrolments[0][courseid]=$NEW_COURSE_ID") -echo " -> Enrolled student in course." - -echo " -> Getting token for student..." -STUDENT_TOKEN_RESPONSE=$(curl --fail -sS -L -X POST "$MOODLE_URL/login/token.php" \ - --data-urlencode "username=$STUDENT_USER_NAME" --data-urlencode "password=$STUDENT_USER_PASS" --data-urlencode "service=$CUSTOM_SERVICE_NAME") -STUDENT_TOKEN=$(echo "$STUDENT_TOKEN_RESPONSE" | jq -r '.token') -if [ -z "$STUDENT_TOKEN" ] || [ "$STUDENT_TOKEN" == "null" ]; then echo "❌ Failed to get API token for student. Response: $STUDENT_TOKEN_RESPONSE" && exit 1; fi -echo " -> Successfully obtained token for student. $STUDENT_TOKEN" - -echo " -> Uploading student submission file..." -STUDENT_FILE_UPLOAD_RESPONSE=$(curl --fail -sS -L -X POST "$MOODLE_URL/webservice/upload.php" -F "token=$STUDENT_TOKEN" -F "filearea=draft" -F "itemid=0" -F "file=@$DTA_STUDENT_SUBMISSION_FILE") -STUDENT_FILE_ITEMID=$(echo "$STUDENT_FILE_UPLOAD_RESPONSE" | jq -r 'if type=="array" then .[0].itemid else .itemid end') -echo " -> Student file uploaded. Item ID: $STUDENT_FILE_ITEMID" -echo " -> Saving final submission..." - -# ============================================================================== -# CORRECTED SUBMISSION STEP (V12 - USER SPECIFIED) -# ============================================================================== -# We call the external backend directly and pass the student's wstoken as a form -# field, as required by this specific custom plugin's workflow. - -echo " -> Sending student file directly to the DTA Backend for testing..." -BACKEND_RESPONSE=$(curl --fail -sS -L -X POST "$BACKEND_URL/v1/task/$SUBMISSION_ID" \ - --form "taskFile=@\"$DTA_STUDENT_SUBMISSION_FILE\"" \ - --form "assignmentId=\"$NEW_ASSIGNMENT_ID\"" \ - --form "wstoken=\"$STUDENT_TOKEN\"") - -# Check if the backend returned a valid JSON test report -if ! echo "$BACKEND_RESPONSE" | jq -e '.results' > /dev/null; then - echo "❌ The DTA backend did not return a valid test report. Response:" - echo "$BACKEND_RESPONSE" - exit 1 -fi -echo "✅ File successfully sent to backend for grading." -echo " -> Backend Test Report Summary:" -echo "$BACKEND_RESPONSE" | jq '.results[] | .name + ": " + .failureType' - -# --- Step 9: Verify Grade and Feedback in Moodle Database --- -echo "🧐 Step 9: Verifying grade and feedback has been posted back to Moodle..." -echo " -> Waiting 15 seconds for DTA backend to make the callback..." -sleep 15 - -# Query the database directly to see if the summary record was created by the backend -SUMMARY_COUNT=$(docker exec "$DB_CONTAINER_NAME" mysql -u root -p"$DB_ROOT_PASSWORD" "$DB_NAME" -se "SELECT COUNT(*) FROM mdl_assignsubmission_dta_summary WHERE submission_id = '$SUBMISSION_ID';") - -if [ "$SUMMARY_COUNT" -eq 0 ]; then - echo "❌ ERROR: No feedback summary found in the database for submission ID $SUBMISSION_ID." - echo "The backend callback likely failed. Check the backend logs:" - docker logs backend - exit 1 -fi - -echo "--- FINAL MOODLE DATABASE STATUS ---" -echo "✅ Found DTA summary record in the database." -echo "Detailed Test Results from mdl_assignsubmission_dta_result:" -docker exec "$DB_CONTAINER_NAME" mysql -u root -p"$DB_ROOT_PASSWORD" "$DB_NAME" -e "SELECT name, state, failure_type, failure_reason FROM mdl_assignsubmission_dta_result WHERE submission_id = '$SUBMISSION_ID';" - -# Final check on the core Moodle grading status -STATUS_RESPONSE=$(curl --fail -sS -L -X POST "$MOODLE_URL/webservice/rest/server.php" \ - -d "wstoken=$TOKEN" \ - -d "wsfunction=mod_assign_get_submission_status" \ - -d "moodlewsrestformat=json" \ - -d "assignid=$NEW_ASSIGNMENT_ID" \ - -d "userid=$STUDENT_USER_ID") -GRADE_STATUS=$(echo "$STATUS_RESPONSE" | jq -r '.lastattempt.gradingstatus') - -if [ "$GRADE_STATUS" == "graded" ]; then - echo "🎉 SUCCESS! The submission is marked as 'graded' in the Moodle GUI." -else - echo "⚠️ WARNING: The submission status is '$GRADE_STATUS', not 'graded'. The backend wrote to its custom tables but may have failed to update the core gradebook." - exit 1 -fi - - -echo "🎉🎉 Moodle automation complete. The environment is fully configured. 🎉🎉" -- GitLab