From 5e37bb9bcade99dd81d8876b2601ea58557614d1 Mon Sep 17 00:00:00 2001 From: edsomft Date: Fri, 22 May 2026 21:09:44 -0300 Subject: [PATCH 1/9] Edsom 22/05/2026 --- .../src/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 159 bytes Guia1/src/__pycache__/main.cpython-313.pyc | Bin 0 -> 1376 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 166 bytes .../__pycache__/settings.cpython-313.pyc | Bin 0 -> 765 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 166 bytes .../models/__pycache__/record.cpython-313.pyc | Bin 0 -> 1460 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 172 bytes .../abstract_repository.cpython-313.pyc | Bin 0 -> 823 bytes .../record_repository.cpython-313.pyc | Bin 0 -> 2664 bytes Guia1/src/repositories/record_repository.py | 22 ++++++----- .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 168 bytes .../record_service.cpython-313.pyc | Bin 0 -> 1309 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 165 bytes .../__pycache__/file_loader.cpython-313.pyc | Bin 0 -> 1204 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 161 bytes .../__pycache__/test_runner.cpython-313.pyc | Bin 0 -> 5309 bytes .../src/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 279 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 363 bytes .../__pycache__/desenvolvedor.cpython-313.pyc | Bin 0 -> 2047 bytes .../__pycache__/estagiario.cpython-313.pyc | Bin 0 -> 1681 bytes .../__pycache__/funcionario.cpython-313.pyc | Bin 0 -> 3494 bytes .../__pycache__/gerente.cpython-313.pyc | Bin 0 -> 1926 bytes Guia2/src/folha_pagamento/desenvolvedor.py | 36 +++++++++++++++++- Guia2/src/folha_pagamento/estagiario.py | 25 +++++++++++- Guia2/src/folha_pagamento/gerente.py | 31 ++++++++++++++- .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 161 bytes .../__pycache__/integrity.cpython-313.pyc | Bin 0 -> 1507 bytes ...desenvolvedor.cpython-313-pytest-9.0.3.pyc | Bin 0 -> 6025 bytes ...st_estagiario.cpython-313-pytest-9.0.3.pyc | Bin 0 -> 5908 bytes .../test_gerente.cpython-313-pytest-9.0.3.pyc | Bin 0 -> 6010 bytes Guia2/tests/integrity.py | 3 ++ 31 files changed, 102 insertions(+), 15 deletions(-) create mode 100644 Guia1/src/__pycache__/__init__.cpython-313.pyc create mode 100644 Guia1/src/__pycache__/main.cpython-313.pyc create mode 100644 Guia1/src/config/__pycache__/__init__.cpython-313.pyc create mode 100644 Guia1/src/config/__pycache__/settings.cpython-313.pyc create mode 100644 Guia1/src/models/__pycache__/__init__.cpython-313.pyc create mode 100644 Guia1/src/models/__pycache__/record.cpython-313.pyc create mode 100644 Guia1/src/repositories/__pycache__/__init__.cpython-313.pyc create mode 100644 Guia1/src/repositories/__pycache__/abstract_repository.cpython-313.pyc create mode 100644 Guia1/src/repositories/__pycache__/record_repository.cpython-313.pyc create mode 100644 Guia1/src/services/__pycache__/__init__.cpython-313.pyc create mode 100644 Guia1/src/services/__pycache__/record_service.cpython-313.pyc create mode 100644 Guia1/src/utils/__pycache__/__init__.cpython-313.pyc create mode 100644 Guia1/src/utils/__pycache__/file_loader.cpython-313.pyc create mode 100644 Guia1/tests/__pycache__/__init__.cpython-313.pyc create mode 100644 Guia1/tests/__pycache__/test_runner.cpython-313.pyc create mode 100644 Guia2/src/__pycache__/__init__.cpython-313.pyc create mode 100644 Guia2/src/folha_pagamento/__pycache__/__init__.cpython-313.pyc create mode 100644 Guia2/src/folha_pagamento/__pycache__/desenvolvedor.cpython-313.pyc create mode 100644 Guia2/src/folha_pagamento/__pycache__/estagiario.cpython-313.pyc create mode 100644 Guia2/src/folha_pagamento/__pycache__/funcionario.cpython-313.pyc create mode 100644 Guia2/src/folha_pagamento/__pycache__/gerente.cpython-313.pyc create mode 100644 Guia2/tests/__pycache__/__init__.cpython-313.pyc create mode 100644 Guia2/tests/__pycache__/integrity.cpython-313.pyc create mode 100644 Guia2/tests/__pycache__/test_desenvolvedor.cpython-313-pytest-9.0.3.pyc create mode 100644 Guia2/tests/__pycache__/test_estagiario.cpython-313-pytest-9.0.3.pyc create mode 100644 Guia2/tests/__pycache__/test_gerente.cpython-313-pytest-9.0.3.pyc diff --git a/Guia1/src/__pycache__/__init__.cpython-313.pyc b/Guia1/src/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b41715e770961658f0e22f23f20b070f86f8a0c0 GIT binary patch literal 159 zcmey&%ge<81dA?nW`XF(AOZ#$p^VQgK*m&tbOudEzm*I{OhDdekkl!J!Z0TBWGq&2oVYx3DP^q8HwCVx>n8jO3YlGru=)=FRwx zlgSuR`g#z3^j8SrH?|0fu6-Kcrs6YDLFMklJ&te+(?j<|_Ygr;M`<9uCaCujU~HC(=8I6{z{0WkDlg zT%#4CD>;X%CfYGZ?1`5-DtIR;?Jey zMav)7amO`n%M(ni({;VDgAG!Dm<@XoXEL^4BtUb-9$)kfCTu18=`w@0+y<^+DWk0sH_xAR*?XGFOsX3&swGGqCbw2WV zrjSJ%krm+lNZBC_Q<*P!&gI;xoExNW9B%*4M^9pFXMA=P3JY^TOUvg{_EgFaq*r^9 zCkxkl`C($Azw*h>p9rMoiv$R7s z5e{K`~CM88G=s5zS*R} z$&=#`g@3s$nq{=Hu73w(ni3F+GEcnKwj13RzD?K!vd6_)rHtmzaZh3WDXcx?U*P1? Y+rTG!2LrVH3om|rXM})HlVuA07uVq`o&W#< literal 0 HcmV?d00001 diff --git a/Guia1/src/config/__pycache__/__init__.cpython-313.pyc b/Guia1/src/config/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d0b4b84a27f0e8b2e9ed8fb4d35391bb7a6095ac GIT binary patch literal 166 zcmey&%ge<81dA?nW`XF(AOZ#$p^VQgK*m&tbOudEzm*I{OhDdekkl<#XRDad;?$zz z7}u2I{M;Cq{N&Qy)Vz}7n1Z7GtkjbH_=5cWm;itO828f5M8lZkqU4z5{JgZx^qBbg p%)HE!_;|g7%3B;Zx%nxjIjMFKEC9OFDvJOB literal 0 HcmV?d00001 diff --git a/Guia1/src/config/__pycache__/settings.cpython-313.pyc b/Guia1/src/config/__pycache__/settings.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f27647336a1368335500e582559c0792255dc6e7 GIT binary patch literal 765 zcmcIiF>ljA6h1o!JE^0TLJ?vCv1KV@=z^e9Ab|!@i(-%%PC6Oe362t9cz3P=2^k3q zsZ-UVYC9FN&`e$VA6}|sWT6`(11ooSA_0Pt8@~I#``){E@9rkmY6Z#hAuIPk%K3{D zvtdlhK(ldqEOAdEnB5a^Bu zC2Vx^%D;vq)T#cimHID{=CW2)`f~>BAN9`u+RJD55(Lb@R^J_w^A zqSDqW6UNBp&xGlRprX`1-7wF!ZZ(`1X|_AU=+iI~Td`5@3NxgU&M4aIf(0;%v8BvNKuCL>}ap+#d3_R@7 zs2lb$o3^_&dI{>+Txhx}P4xQ8G@B(sBCfMRs*H zyVTBV_ui}?&oAtn+vcvlZSOsKzj63Fo4@(C4d-Ut)^)IFJU@M5Ey^`v-6(Ex1Fm!F@JCcy+`~(>Tr#S!s literal 0 HcmV?d00001 diff --git a/Guia1/src/models/__pycache__/__init__.cpython-313.pyc b/Guia1/src/models/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..678fa76b07eaab0e06588d4836f18a42436d4441 GIT binary patch literal 166 zcmey&%ge<81dA?nW`XF(AOZ#$p^VQgK*m&tbOudEzm*I{OhDdekkl<#XRDad;?$zz z7}u2I{M;Cq{N&Qy)Vz}7n1Z7GtkjbH_=5cWm;itO828f5M8lZkqU4y|{FKz3;+XjO o%)HE!_;|g7%3B;ZK*7?SRJ$Tpps65Ri$RQ!%#4hTMa)1J0KIuCnE(I) literal 0 HcmV?d00001 diff --git a/Guia1/src/models/__pycache__/record.cpython-313.pyc b/Guia1/src/models/__pycache__/record.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..157ad2dc262a1ef9424dcd500f42a69a808c31c8 GIT binary patch literal 1460 zcmbtTJ#5oZ5I);+;-pCfluB(VB7%xG2q6%#Aj$%WP6c6pGFUu9>{gDFINWn2h=jz1 zs8qxhsZ1cIW@2S56Kf=v$`gW#Ef5eZceWdq(v6dRzkct#yLUIt%at!%mZJ?65Otx&Sz4W-z-lR>}s+n@-h3`y1sBK^j9t zxOR|eRvJ!_s@OI#zr1I%{TqeK@^pvHZ7HE96 zF2cJF5noq(rZ{(ylT7QG<|8c(5pGV zCt99o-MD_;y4H3pXDo?TtKr#BU0P8=r&^B#LkPF&`a%TxsN_@I4$e~UC-xM!CT7-* zt;y-N%+}PA7r7xhp*pIF4A#Ti3#4@6Hryn+n`Qyioz+g?nEr3< z++NsRWiTFN$1l&#*a81!^-15D`4^s#hE2aiN|*L$@3xPFsZwM#bo6sf6wxTc1GS4N zk~o{1g_6G1$wbM`yY@oII9cTWEX;MXb0>>&x@4(yCFF(Q-C7#S#mc-XGg<49hJWf!GicbiVrqp;>S{XmmG;y$`2FlcL94*HLA zlCK^u9L{ibQV8i+e77npzK`yMw(m$GdJyImjxX|Cgp5a8-Xr!52CAkP-y9nNi48Lp z@6Y~ZYmkBD_;*#&r-mA71J(w~*jE_&3M1d)k?-)Yq@tIV2%1rsXs1H-V#5OegO0E$RCW~BbfjI literal 0 HcmV?d00001 diff --git a/Guia1/src/repositories/__pycache__/__init__.cpython-313.pyc b/Guia1/src/repositories/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d2292177e73202b797a78dcdfd5537705e0e493 GIT binary patch literal 172 zcmey&%ge<81dA?nW`XF(AOZ#$p^VQgK*m&tbOudEzm*I{OhDdekkl=2XRDad;?$zz z7}u2I{M;Cq{N&Qy)Vz}7n1Z7GtkjbH_=5cWm;itO828f5M8lZkqU4yO)Pnrt%#!?~ v%+%tT`1s7c%#!$cy@JYH95%W6DWy57c15f}vq5$jgBTx~85tRin1L(+0jMoQ literal 0 HcmV?d00001 diff --git a/Guia1/src/repositories/__pycache__/abstract_repository.cpython-313.pyc b/Guia1/src/repositories/__pycache__/abstract_repository.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc6646b6ec3b2d42cd0c6a663851d989724d90cb GIT binary patch literal 823 zcmZuuJ#Q015Z%46oP$jO2MGd&1=Udmp^yeeNFgAkAUTK=R4c8{=Z$mLo!6ON7w)L| z2bNOP<*(4vZ9^ogbVdg~JHER}C~WcO&Ayp8`%!i}>%^|F-&^BvHvYtk31r=vqfT;(OtKy1$X2{{v{@nW8D2$2j*rnP#Tg{7eb%NVOkYd zSa*?f&*lrl2+){-6H{=Rhu*t&HTX7nS@7J6y)82KuJ+erAHq=(t(iOf^_&l4DpjT> z%rDVDrL_kJel= z7FzPDlp|NF6DW~DdSdSXXL=>AJT|cT76Q8U zqm~IdpCpCg+^ln6$l)wU-sb#tmgKdv!TE6pO6S=`OeE)kFAN2D{_TadbJqPsY*=~!1qBYkq5uE@ literal 0 HcmV?d00001 diff --git a/Guia1/src/repositories/__pycache__/record_repository.cpython-313.pyc b/Guia1/src/repositories/__pycache__/record_repository.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..87491ea8cf944242bf46effb3b111e335a338f6f GIT binary patch literal 2664 zcma)8U2GFa5T5(9oj~G?&%sF`#02wW8cbS3k@%-cXmLmgHjb!baXRcvICSxuy>lq4 zkdTlnS87!djR-<2@I;aN5Z-tM`qI87b&I2GkdXSsTWF{Xed_Gl7f4DaR`TBL?Ci|! z?0hp`sH>|59O{h~XMW}Z{-6tY2vuSsj>H5cA<6VXoFPn@(rh0a=LqM}Tpu47h~UtC zpC|4io-i~)J0yh`NP5!D4z8k$ct^ca-!r<1GIrqLDbpfK#!9Gp!_+K;oJX!Fp=Jz{ zMXL6YrmIH`C94u#)f94C7Ps(MQC6AR>SEzTBqksYgh>KnlMLaKY?|+4lUxg&;favs zTYz|yg2VGTJa5uVe51Z-jV)G;zINo92i4g%Bj|ygSFF<+*A6vDX?mlDwKz?{2~o#y zWo&~K=#0A>g}cnaUDmFZ7Zuq)nK&0TqpZ!Fsy;&K6PQ$IIBXy9-jz<7DlyZ&S<@Ix z_ZXSNn3}W9bevu4%@|dA z?HG>)X4dktqpC5-q!58K(-=i8wFJAl1h8;mef?|{u}S)0!(W2z0dSIK6H zYMO+;Nns6633XIMxEWzBH~!;oUCSahnlp%kM6{OBVA(z$(I#iiaY7Tp=Cqt;3wTJ& z+qFZAUQl}pF$kgewmnKdujaCZKCUg~3Cc!!BGMI~RV>Bkh;fdr!nH|7!Z-*GPSuE_ z>u*U$$3AY+c$iZ-U4%J*bJ^eii@*I=*PZ>h_m}+Ji~R3WsMtNvdh7PgH8xF3lgF<& zUp;x5ot0Ky9>21?EJbFdNJ-lGeS1mjnBt}m->WZo_RMtllsXUn)L!a5GCi1>P7Rd? zhvr%$H#(++8=nT7uJvE(F9$cz1ULV%YKpz@x!3nVdhqGPwWa9bqB4RSI_3EU>1|Qe}uyJZ4gZH zt~^5Qd=+9`1fuM~k~&7iyB&WEJvi-S*CW~{5Q`Y1MgXJXIJX=5dznR}7;?labDX98 zahMQLeW;>-6n1hW454p-7oxn)#yV|pUeT3tf;d1ZA})(=O$bUXK} z)b*(Mm1FEuYnX}ph||mj-8iQb#B*NPEC<5`-3QW)6FRBugtoJ&*TFD}P_968*R;J@ zNea4U+6*CE=#lquGHI=*s16@EP`Qt%;n@X$xYy*fSScJ$dlv z;p>M>fsN%r$4sE36o?hO=lp@+0`Z5vr9iywkC*)MSuu32{!0Di*~j9#=QR*$o38~i zRIDdK^n=jM8^HFD{gKv9?2omwJZ5AI*!5+5r7WK&;kWj!m6MYxwEx&+yo)uQ#TK@v@ z^QUj6&^o`SMhH!A{~PdAIZKZXM16Jyn|^Gu!DG{pDb+txZ?t_v)7x&KqIe-yH}M4h zXHcovjg()oG-NyfOlaLziPHp}WU08g)NR|hrxKcdWIyVr70aX04n_Y0{5r1x3){Xr8vpK<2&q%FJ(O-umER9}vB5^)7s106(fr-(6|#J^9S~WDl>C3c@Z3 z5_EwX?7YtvC~KE3RMs9_Xu2(r1NPFDTA{Z@*>Mt7c@W1g!+ugoovefd+UCrQRGX+< zdh$~3-(39`v#rB-6I+BY&i2pF=&CdN*_l{%CR)zqiam)Mpt4edwf|4@3E7vR!Y1g{ z6zl+!${XS52IzqC*qJ-zM}^<*LiUW#`~{=-SnZGvM_G@03;o-gad6EU1`Epc=Yn|GPZWRUx)Us>_62SI=wP*dP8pytvZL6)0T5& z#XgdCmxidPIzvy1cCH@O1npcO=e4BTq({Wn0O$9cLEN@DoXSV&dO~KCK;a<$ z0f#ipYTe1XOoKEEd5{w1*=8yv=kFm)m}AoGi<;>n$WdZXVa;NNQ_BZ`10BDLtZ;a( zXcmsXKK=*j$YXWHjBBbfdOlO#7a0jjf?l`{uQYqNSqeNrQU7u)`)m&G93CWt-W929 UUo#nF-{AZotDhbH1;nzLzjVwa^8f$< literal 0 HcmV?d00001 diff --git a/Guia1/src/utils/__pycache__/__init__.cpython-313.pyc b/Guia1/src/utils/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..84bd6ee2b0a0090c8c6f321ecba7b3c948f794ad GIT binary patch literal 165 zcmey&%ge<81dA?nW`XF(AOZ#$p^VQgK*m&tbOudEzm*I{OhDdekkl;~XRDad;?$zz z7}u2I{M;Cq{N&Qy)Vz}7n1Z7GtkjbH_=5cWm;itO828f5M8lZkqU4y;lFXdqnE3e2 oyv&mLc)fzkTO2mI`6;D2sdh!IKtn;67K0ccnHd=wi`JHC2v-{rhTh@`sRJ_Z}!ck zQ&SZ{`R>d-_=5oaBNv5eIY=!8M__|Z-Ukk|a0P7TGT7>}GWOtPpER_ma?D518BW`BRxkm|Zl` zfg9iRu+s}EA(DJrL+cSNLi>VkVaGOBcu!RzB<;LgfV;)cE|LK5sYRCa$WUp^teLcw z!Wt>~1@I|$ZNXbmhpX^*?%BcUu42IwSorM2y1E2iQipaqcYqSO(|}xiqOjOn3toep z7d3p+&gLO`^j-i6)&F?w?WrQNr51htKi zq6ea~6?pMoUk*xWy&#H3RgP|t#XBqsJ2C?o=JrF+AH)H}peW`IjT=~4XmX~);NxiG zM{+V2irC#;by|_nBWJr4vA(m#yrl1kapVj*JMd#h2aGu%-nrxKCV_jyiMZz^anOsL z?3nbjgL8fGu_#MOWutG%w#FywdSTSDUUZ1+GdR~uW^+`Xc@%u>9q;~GUHerV%`JSh z`1Rrs+S%N-N5<&V{NtUkK7Fo3ZT+w68vb5BGvM`l3dGzdquTs%@!b>i_>lP zIJq^No|RlPqi+7Pc|v~PIbInqtew@?hm-3s&s8K}L^rVf=Mx(yd|6*ne=-rAR5py# zsiGo0H8O41ZyW0AJC|+~^*5p;{t@;XWl^Rybo)M~qCzQ7Ho+T0#U!Qo6StQ$)0B1t z9>u*N^g|};D0bt(>-+IN*5OsG&fi2OW+)vq)Qw{ve3Zm~L@A#|F2xu4j8t+$nUa?- znn(2oJkhJ?N?C6{-b?}iQWcC^Dh+(8@NOu4vcOr&F(p%q^4hrdCbw{j*9f8-D*QZz Q{0)`o+5~C-3#hV~S5~tXLI3~& literal 0 HcmV?d00001 diff --git a/Guia1/tests/__pycache__/__init__.cpython-313.pyc b/Guia1/tests/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6400053e4a486e2019bf1a2005ec22f7c0853621 GIT binary patch literal 161 zcmey&%ge<81dA?nW`XF(AOZ#$p^VQgK*m&tbOudEzm*I{OhDdekkl;)XRDad;?$zz z7}u2I{M;Cq{N&Qy)Vz}7n1Z7GtkjbH_=5cWm;itO828f5M8lYp)MB7qe0*kJW=VX! jUP0w84x8Nkl+v73yCPPgfglTuL5z>gjEsy$%s>_Z$5JSt literal 0 HcmV?d00001 diff --git a/Guia1/tests/__pycache__/test_runner.cpython-313.pyc b/Guia1/tests/__pycache__/test_runner.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0ce6ea161e7d4f9fe69d1babeac6d4d056f97c8a GIT binary patch literal 5309 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 literal 0 HcmV?d00001 diff --git a/Guia2/src/folha_pagamento/__pycache__/__init__.cpython-313.pyc b/Guia2/src/folha_pagamento/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b23a162ccf0409c9b8279cb07562c3eea9c232db GIT binary patch literal 363 zcmYjMO-sW-5Z%pZX^rCDLlF<=;z2x#c(JvRKr0k)3rlFWF(t`_*`%O9qu`J5F9qtw zlQ*GwcT(%e9A@8pJMRs%@w_gh`g}TAtmu7f$xj(Ou(=@c%%*J0Ll$u4{T3Uc5twLh zu^C!{jrJB>p%b|1_E}>4&O0Zo&zs(DUCdG_B20m3cc`l}1W3*X;C_9pN6SU#3ZBzyJUM literal 0 HcmV?d00001 diff --git a/Guia2/src/folha_pagamento/__pycache__/desenvolvedor.cpython-313.pyc b/Guia2/src/folha_pagamento/__pycache__/desenvolvedor.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a39269ba3ce1b9ece8d814c6f558f50409897c69 GIT binary patch literal 2047 zcma)7PfXiZ7=KP|$00FU6AF~TnpL!5U{q?0CZP=`tzD&7qgB03NO(LM#{?rM&Yo?w zb~|v|iZrRcAkkgMaa?xVWtSb+%Z^0ZMD)~En|9)cnpW#^-?IY*w7|Z@_rCYO-}n6e zd!HM!+<}0WzD_M~@d*9RHv1$rg~lZi){%--?l!u`5iW;%(FLSN`j8scxQS?pBatGn z2)k^fa5V9uUp6eKtP{&ILO{qP%}rwxgn77ELxfWiiKrZj=Fv5d-McIiUXAu4BB(rY zQ5As4RFOz|^d3jzgGh-{`MT+vsfs z`V{oD>3nqG{~XHm!%cl5fzaH3{1Ep97b;9HFZ>S-e=pX&sAZXG%6Vi%rbEgLryvh} zC?X{?^B}I8rQC{cJ46ww;QAGlPzhtJYdj!$&kH*XN)5Kwx=s?fM?m5aV!G)puA z$+8?`74(7$+2dW)UZi~4DVa20(gAJwwoa3-ZnK)<1>H4C65J#*FdE0$uyxnP*k!Th z0$Qc&2a{SJfUb6<;5sGkx?}hyv+TKAg*eNm=irLtXm{`2(Qf*denoSMp)ET0l8!5S zQD;a;E3}qzymFt!*$2CiRCjzoNLFC?BVeyr(KAV2i&drGjjr#sLkpErkZNP=dXcn2th_z|Z zJU@f5j`HXpJ3U4!H#v)toAFb9=E#wEe1z@YFo=EK^veiU(S-L&3|qRtU=7C!XfrObh*eJR6q!7D3%(e=a4zaIQJ zt#Cp3qz~*`gHIoX2d!&3(8DgxwMoDMqK93=atBFBWp+}zYAUyRt(qFzmWEF7dHF=2 zgBF0Y6eDdKG;A@3;M*X=Hev{|7*ix@mAm)7C8un?gfq(D(pPlrF%vtOs2VU;JZl~f zc7FxeAVtB9IG&=+L5lQ(75oP*m%h~|M~9H$nk!+F5~)X1Yf~GuwZz4GrW;ECXnt*e zW2GjyZojO_qoDmW(;egxy7pjXDIIJymSM=S(TH)$Dfl*%lNf*I>vmI z9t6h0^dVXBfxOAsMPT$Sj6F~HECcHvOv(k{Ghq@^xj7UK)3z~&T%-XUbjtg|Ga+nz z2aE}BKd{$dp;wWZaQQ`A6vke>!3o3vh?3B=hkykk1n?OpO0&o2D@5MDRLR~P$T4a0o#jm9QOyh`cfL;dLG|?g>A8AW+Vh+FgJm2TT|5PD}o*hUtzr1#Lf5|a!xjh;hRs)8)pmM*}5VzE>sZ7Tah zG^M!kP82vE4G8n7BhH2^H4u6WAlv}IHe!+me_IlhYv{Zr{BEc$ZOIkH6e|swu@u0p zm0@ZPy&?ixl+BsZw?U)6!teqI)NV*kC02OUa#^fm>;+zkah(5<_>sHB7-g~I zM2yo|a|mk?e3LS03xOu3x$CdT>3}xfIM*a0^PI>hagh^WG!Cy3?lLFhHyidWuRuxnt*?WH=WP5W)?L`^pcx!q!P1Jc7SO6`@aSM4j2N9JwL9DAMm zHwkW$1`(Fjc1P2iYkkUcB3k)yf^);F#1Q$L0FSoNT~+%!+g7J>?xB;zdq>gE{JhDh1W1);JGQgSa^1jGr z{|5oH0{#&oIO*MhK&F&j;{Oz19u-gOUh*h;Jf1zO`a}H$IOXDrf@u}>y+XBJsP2?H zh3C8F$*wlGr=4nRr*`H$+6zY=9LVUznWS-Ms-Kgfn3)&k4S)}VycjoWJ@SQI#Q5Wg z_ydiO@w&%&=zD=1P>d6W(1rDh$n!dAqhPPlMqO~7KJX5Hr59GbzI-a7#cDL z#>qvvoZMF?-n9gTz=gj@U!sSptn$)>lBAq_kOBBRlToG)5I|BwV!mw3@yVwsJ-2@N w#xudI#PW9k#`Ni-F7}JO$mT#Vc*DgA4`fM_en+qVrA|sy-=2JkU`t~C3zr;!!TDY$W;n$VZ#q5ng{|A2)k5=&`KTMB_b$jEu`%$;;PNp>jBf@WuD=JsRe zH?yloI2XZ6JpTsYC#6BD^Hb19+NAB;C2Q0Y&_JIz05N7EIY0#QA1S7QR zw?^TnE@X`&QnaZJcgzrT9bj$1wU7` zl6r|!oiahToEDVZUJJ`^w}CxxsM;z8R1+FNf=D;en;B^AvkRxCWmv+HF69k6tvW()pjUnH|{gPEEkFe zQ8q)h7JI=oW^4AL!i4cj7A8^BeEME{QAM;2Rr^G9Rk7s*#JEPAHo(Q z(9vNa?hRnTSi-#lC(ubWZSUgY(4ZUqae6%iAd!y*E4a|)>ebk|vf(;pSK)^{u!_Tig zyHZPzen0lzSZyRz9XVMZIay02pWk|RtC~1bPMp{a2(e(D2!UX)8_|W98y^Al7B`|z zS8LrACqWL*Yu#)vt1gQ^m#WD+7J!N=VxyP0iVBP56b&mIl~?p)$zX%dSjiWxVqY*= z+!5wgBd0_CFnZiN?AWw`rWNj%poX$3w}%h2IsscXE&%~&hho*xXgM@mi^Qvu@p5Fm z5}Dlda^b066dWx_j#eTwdx9sNt@}uHuo{^zN2V*0V|#tx@J!u%U<5ELtqA-B%v&Px zpMiW3j3GV;02!okId!*IOhAj3tgE@SnBHB@rNx@p)mmCeAiteJepjNT!P*YJU`IUY z3Ut7UFN=1CF&l7AlEF#FTQkvVJ&s5T9 z_XYx(uz`aT*13szSbmeN*-7)N=UCR!S;~D52jP}`FTnQ=Ojxblkfwg~4R$1CP~)%e9WRvR?Z>L^MP+$q}foZdEbRT>u%$rJehO? z7qjqo3OS6HjYbQI)+rLQY?$y8%gKrf&(xKYslo+edl(-8_Ok&mC5i-#gz*QEuSne! z5{_?)bpqXwcK5TekE8ko@kQz$PDnk?H3)PKZ%Y6r$yTMeK0sC4QKcQ##FmPx2}d(Fq=GOx0dqj-ATiL9QQk!+~==yT=?na8~Cfm H*|Yx(zlpv} literal 0 HcmV?d00001 diff --git a/Guia2/src/folha_pagamento/__pycache__/gerente.cpython-313.pyc b/Guia2/src/folha_pagamento/__pycache__/gerente.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6c90997180fba59921ed9b20242d9cc633dabe94 GIT binary patch literal 1926 zcmah~L2ukt5Pr7TyPIUUPIuEJY(i^FO6vw9L0ch((5(b*Q4wj0yQm_DC&%k74&L?V zJsXLhasd$uR0$C&z$Kh$|3SEL;ndr%7O9a{qDowdxI|4WdSu?Soumy3a~RKiGjHC^ zeDlUz`FuA5@)ni3ODTl@WQ&C4SlD_Age9aSU3eE=5s09oL3AAHVhQPzA)J#UJ0jLn zn!Ldlnh^F}3L2KHHor?9<9)zZo%pv1*LZq;Tik#S(a92gv#c@c%eA-^xvA=P_jVlAUU2~zj z5!H~En!MSi+jaFb)Aa~p6}dUQy;i*-Qm9VD0=4aXnjFeBXxb#qV(c^=ALFq51Je!c zi-dRtlwd@{?s>n8?fJlI+EAVpwcXh;)$r zeO>9hUl?AV?i93EPFq)=TmJC&t3P7cYd!x)r!djVO{^U$ecioQ8u>=q0vE`N5o0o9 zwdI|#4#E3_VZ4)iXK zGw=JyCP$~CtJ0H%6|(lj_l$liZ?LCbJHzR>a=d-Ftce_O(hJ%<0?EUNvXZH!06aWo znJ#M-f@i!&K*Rn7tf%)OAVeKt3uvuJxm8{)FPoj7V{65Md&QAF(;(`)&| zx9}ofp6}$KIWfLt=eLWat=wp0M>_czc9gM`O$i-3PWs@s{{~kD&Hms< z1=7UH+Un6S{(gV;*Rm$??8yL}+nsx1JIAU`Ef0E^hNV6EZ|nH+BKUwBD8+k<+E&z+ zvu)*MD|_;QXx}+7S`IhhQ4{xR)~F$vMXDw<(EGuk(}G9K;dlOmes>;$9MZo{F$eP-Ex 5 and self.qtd_equipe <= 10: + return self.salario_base * 0.15 + elif self.qtd_equipe > 10: + return self.salario_base * 0.2 + + def calcular_descontos(self): + return self.salario_base * 0.12 + + def calcular_adicionais(self): + + if self.qtd_equipe > 10: + return 2000 + elif self.qtd_equipe > 5: + return 1000 + else: + return 500 diff --git a/Guia2/tests/__pycache__/__init__.cpython-313.pyc b/Guia2/tests/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..acdf7582377e6b15d90848db4ca4ec2f895d7f10 GIT binary patch literal 161 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 literal 0 HcmV?d00001 diff --git a/Guia2/tests/__pycache__/test_estagiario.cpython-313-pytest-9.0.3.pyc b/Guia2/tests/__pycache__/test_estagiario.cpython-313-pytest-9.0.3.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a3a5d37da6137d1019d088d26ece7b6bff26d563 GIT binary patch literal 5908 zcmeHL-EZ606~C9FC|R`QkF=fRbgAk(f$1)a{Gp^xt<=s(qorQbiVzkoMg>Mxg`GH0cjggdRvzRWA5vJ;4ko^!p76|mz>~|mU)0I<1 zkGxA$lIzo>ftrJQA5E9D4f0Qg^uO@Y%E#f4f4cee``Na-(3BUL3ci9rB{2Hn)W{)kD{h&q*mSR+n!?MNF3eDTU>>opHSZ9mS(vSLyGLMQa>pnDXW?MrXXxm38Pp zXI-N(rp`=yM^kZ)Cq_yKjtq&&|I;;*{qctr%1QVKR3o~gM{i zxQ9_uOSC2cP?}aLoD!T0oS`(WT`CbRdbM=_*suQa?)^W!xkVpJ>9j`IZy%qmt<7n6 z>_bn9r{k#2h%QjPuB2us3Pz>Ss2D7N)2cS?*zHLzb~~O<$0sr841`Yc>}iN~E28G* zzXtiX0m)@6f77td*lmqJs#}T^s;wna%%GSfqVx z7X$iG)@PN*WICcIbBI9*X3SqdZx>ovk}7L;$ZmDJA-@y_}$9iDt}gM6VrzoSmH)QP5?X{r-#^#mxJXzfgATGJWq24#vXQ<;{U*#Q^-wK3y_ zZ@S6@IrE#deqXC&fdlG4o(WYpy>35kTh81Y^DQVMEG#nJ8G#=H%BOPXTTK5J)8S9u zef*c+bN@q3|M0m#UHqNs~=Nr)Z7_yEpLw$#a{ywp@D z+v*f3n?(>isijtG3A;f_a3!(SQkQnX#eZ$gnDR|mc_1$#&UqoNjs*^=|9B=;*(~}A zuCgsJ-TRT>2StR1MN*v+_#vQtDlc)ITaOnERxAIz*`4iR;FDunNhSf zMU!@)gC*Gg9SG3z|G{Ql9dF6*L#{J?Z7|$+Kn$+)?t8!sK}TB1&VVQ&FBXTtEqIUf z!2%%nFX z_Sx$bBRiP#?76?sw#9uo5cl`9ko)W5gv|M>!r80hvDe7gV6PFuT1D%z*A8P9pq70~ zP!o?uIe=a=FN^lU^#bRL{7nIYL`hf&}Igk1FW!$ScU3S5@KW(iSkpzAqw4*MV{ z3YYDJ0-P4*V`Asz&M{xqh6bP3jy5#Rn(0JXBg1ejf#a-&_!$Qqsz<~ph!Ola@ z^KjaK1hGN(Mo9eRw*2DfNC~x;sx{@srmD5oS5Y~&*isjFWbfBT*Q>tiDi7pE#6&No z)v>?yDvbwFX*NeWDqR#$ z!vRq#l#>CKTrvBFgaUTwZN`4lg66~WrR0``+ zy)PH`P)Ye3sH6xq60L_yu%E8Y!9F^6mA^8}e}`9z)9>}=6%TbOok6bf9 zn_OXULFhQ*5_@$4J%3bj2{Hj%KRLpbi9dO|1JG%s8FGMN*3n9NGzZm#48QW8c6#ezF zJIB71B)UlVLqrJ9 Pe;Cv~SB=Ru%?I*t4TdE5 literal 0 HcmV?d00001 diff --git a/Guia2/tests/__pycache__/test_gerente.cpython-313-pytest-9.0.3.pyc b/Guia2/tests/__pycache__/test_gerente.cpython-313-pytest-9.0.3.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4a6b47108fe3271cec8119f5dcc8ee756ddb26a6 GIT binary patch literal 6010 zcmdUzO>7&-6@X`dxTLuHcag|)?NEwkYa{Fb->GD$k)_l@?S^Zn0wk1Jk!yu1#bstm z)KXzUMq4z;UJRr?)@6GP)t|YpW4Ik14 z`S#7cd2fEW^UYiCH8sTvT>sfSzwDP;82-QnOGpGvSd%p&-F`F9G5% zSrXvJE?v+?UD9PeGVU$mS)wV|oM_ToF=@$gq{*r2Ro3+KXO|uE#!q&z9mQbx$sQV< zxi>E^r)-nj%U80twYEHGWy)(X1bewit@q86l`dM=^3SecUtTEZjFIJ%X@d=Adet54 zKylrPdOB@_nAnH?GKfv`twI`4{9*bd^W%5#-FPruQ^zavI90*6aq5FoHf?@T&K1oI zMU!Tr&$5wEuULh$ttpzggQNWVPk+Sgua`8DHb9xSfB>6EE?I2Na|>BaB)wnXuL7kXrd5zqBq@h$UEP}N_MbK@m69&L zMTm8?*K4>SWWjd{$IPx|jBCNY8=m=;G6p~BqCO^$dwuBA65BP{F4-8g=33C}>rj3oxc&CiPp(ypJPWMtDnIe&I8fc?_jl{WFBjJt4GikErNNj_Q?p1j)>_e^rqc-K^h!CGFXalhqvS1UVLKgZ`#r0i z&$6cJ)x5O=JHKG8nNCC6Hf{n}iqvU-gAFZb6|PWfQRhVZrbX9`Qrf&#%o_!x1QxA< zO_O%P!aJ=8F+vgWj*3HYr#T!hjD`#2Zo%zgGF+Ip5j+WgQQU^kX*OlXD&Qkcmy9)| z@Sf%IjE+DX#E|_L5SwJTophevl3#k<^2&DiL=`?Q6Ps^6R@=ALuBzHqkq0YkS518t zl#g#!)xmAq`_$0#s$X=Khw|WulYU(_D1iauxR$z}TNpp=87leJkK{oH=!}2Nz#MQ1 z*b%hFXbBXAii4Nc7VxMb>Vh>?kE7O`DH&O6B~y_@oE!ue`Fu_KoC$gYPKu0^(qd1D zlk$?!)TB?$uouq-d`1RTD?IK2AFncw~Uolic!L@{>(9^ZLR9%oHE zV--r49Wc2ULrns{Ut^>l27bGgN6lk8;CCR5+W2ja!A(;r9PEVt5XET_2L z4-aj$gdjdV3KYlcQ7!JY`>^)MM7uG}Srk1e&Y?Ju0*^_mp?D2N9|(@ueykV(5k}@j zxG?RK-oxxLg^f(yDi^84Bl}8Y^`P3$pvp&@zL~pa(HV$?6u18dVv~G{%?-yO=p*)W)^_o%@Dc_Y2}Tfh`@1~vropF!llFp<&1K+FgR_vI+AI-95bt~ok|)8J zpaOJEzIOfvSOy(80KM$iV{)=nfdm(lU(A>38zAZ@zrQ$?`~uqRB)`^ZfOcyxB{FcQ zcw*%xxB(pNgIh>NSc3cG5V)o9A*7@bSn7k@hwPCp1Dw5h9Q%g?{~cH=AN&gS$L@RW zOK=sI;3{5%8(lg+WTX6O;&(5>4ZzBIntV>4@@q)VdKHOKVj^0b7X&m*GU@Z!RZWo{~1Im zX+-3mtE%TJ@@z#tS5tdYxiDK*XSZeVQ$tg)Uv!m+@+`8=Yf=qLV1PibrLN}|>mck3 z{rN>-c_hys&J_Lk>MTPGyw#H6%~}@=P_7G@?hs%`Qvw6b9DlHn2^c9Q17PIE;SgXV z-$TGej>Lw)Nsnylqexd?_)Pu$l>ZLL0*vnkfPuGW?+rSl$Ml9&!~=}-41iHMSoqxo z3}kS{G05EF*LHBfa~o!5>puD@fqNykW1EPXSDa510d?j!l`U zOBqaho(>q?WgcL{FQf~Uqzm- zsC_lHACw0jRdsq>_C7VV^!r6uc_>dKU%V#Opacep<67!^ZejeeXQ<>?Ka!^zU9>hQ z#{WQI*{X9g?f&I3;AWeUF%3}jS2S0GzYqK>C+gL)g?AL!#gS~9p)xFj6NUV;RHi0d zZbx1-=r0`={Z<_u=UfxxR82kO1J7>NTMPXu3B4X<;s5{u literal 0 HcmV?d00001 diff --git a/Guia2/tests/integrity.py b/Guia2/tests/integrity.py index 6d3ebd9..0e2fbc5 100644 --- a/Guia2/tests/integrity.py +++ b/Guia2/tests/integrity.py @@ -1,3 +1,6 @@ + + + from pathlib import Path import hashlib From 3f59287c416ae511055407ba480f0bb8f1d4e618 Mon Sep 17 00:00:00 2001 From: edsomft Date: Fri, 22 May 2026 21:35:28 -0300 Subject: [PATCH 2/9] Primeiro commit edsom 22/05/2026 --- .../record_repository.cpython-313.pyc | Bin 2664 -> 2669 bytes Guia1/src/repositories/record_repository.py | 9 ++++++++- .../__pycache__/test_runner.cpython-313.pyc | Bin 5309 -> 5313 bytes Guia1/tests/test_runner.py | 2 +- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Guia1/src/repositories/__pycache__/record_repository.cpython-313.pyc b/Guia1/src/repositories/__pycache__/record_repository.cpython-313.pyc index 87491ea8cf944242bf46effb3b111e335a338f6f..e158126a9db219d66b6e4760b2ab8fb933aa1084 100644 GIT binary patch delta 36 qcmaDM@>YcVGcPX}0}vQ~64=Py&d3-!c`~C4OMG5_+2(DGn>YZ*3kwzi delta 31 lcmaDW@~w>>llTl7N}enR$HOEq2#ib z^^W2bDtqfU=khOQW^~*vEUd<8>;hC>#0DZnK!iDnkOvVqAVLa6hy#gR95%W6DWy57 Lc0~b`b3_6GaRwr) delta 121 zcmX@8xmT0-GcPX}0}$+(%boROBkw6*)>|xD`H7n!@YXR3OD+((EUdahbVJHzO{*Qr zCq(wWHa^HWN5QtgTY JCl`nW0szRmAwd8D diff --git a/Guia1/tests/test_runner.py b/Guia1/tests/test_runner.py index f8004a8..005ea34 100644 --- a/Guia1/tests/test_runner.py +++ b/Guia1/tests/test_runner.py @@ -61,7 +61,7 @@ def test_search_multiple_terms(self): for r in results: text = (r.name + " " + r.address).lower() - if "joao" not in text or "rua" not in text or "a" not in text: + if "joão" not in text or "rua" not in text or "a" not in text: print("FALHA: Resultado incorreto na busca") return From 82bfb77e6ad9a969d8b0cdfbc1694c4773599051 Mon Sep 17 00:00:00 2001 From: edsomft Date: Thu, 28 May 2026 20:06:35 -0300 Subject: [PATCH 3/9] =?UTF-8?q?implementa=C3=A7=C3=A3o=20Guia3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../record_repository.cpython-313.pyc | Bin 2664 -> 2669 bytes Guia1/src/repositories/record_repository.py | 9 ++++++- .../__pycache__/test_runner.cpython-313.pyc | Bin 5309 -> 5313 bytes Guia1/tests/test_runner.py | 2 +- Guia3/src/alternativa.py | 8 +++++- Guia3/src/pergunta.py | 3 ++- Guia3/src/perguntadiscursiva.py | 13 +++++++++- Guia3/src/perguntamultiplaescolha.py | 23 ++++++++++++++++-- Guia3/src/questionario.py | 10 +++++++- Guia3/src/tentativaquestionario.py | 2 +- 10 files changed, 61 insertions(+), 9 deletions(-) diff --git a/Guia1/src/repositories/__pycache__/record_repository.cpython-313.pyc b/Guia1/src/repositories/__pycache__/record_repository.cpython-313.pyc index 87491ea8cf944242bf46effb3b111e335a338f6f..e158126a9db219d66b6e4760b2ab8fb933aa1084 100644 GIT binary patch delta 36 qcmaDM@>YcVGcPX}0}vQ~64=Py&d3-!c`~C4OMG5_+2(DGn>YZ*3kwzi delta 31 lcmaDW@~w>>llTl7N}enR$HOEq2#ib z^^W2bDtqfU=khOQW^~*vEUd<8>;hC>#0DZnK!iDnkOvVqAVLa6hy#gR95%W6DWy57 Lc0~b`b3_6GaRwr) delta 121 zcmX@8xmT0-GcPX}0}$+(%boROBkw6*)>|xD`H7n!@YXR3OD+((EUdahbVJHzO{*Qr zCq(wWHa^HWN5QtgTY JCl`nW0szRmAwd8D diff --git a/Guia1/tests/test_runner.py b/Guia1/tests/test_runner.py index f8004a8..005ea34 100644 --- a/Guia1/tests/test_runner.py +++ b/Guia1/tests/test_runner.py @@ -61,7 +61,7 @@ def test_search_multiple_terms(self): for r in results: text = (r.name + " " + r.address).lower() - if "joao" not in text or "rua" not in text or "a" not in text: + if "joão" not in text or "rua" not in text or "a" not in text: print("FALHA: Resultado incorreto na busca") return diff --git a/Guia3/src/alternativa.py b/Guia3/src/alternativa.py index 4dde61f..f2caa32 100644 --- a/Guia3/src/alternativa.py +++ b/Guia3/src/alternativa.py @@ -1,4 +1,10 @@ from typing import List, Tuple, Dict class Alternativa: - pass \ No newline at end of file + def __init__(self, texto, correta, explicacao = None): + self.texto = texto + self.correta = correta + self.explicacao = explicacao + + def get_correta(self): + return self.correta \ No newline at end of file diff --git a/Guia3/src/pergunta.py b/Guia3/src/pergunta.py index 5b3763d..4161784 100644 --- a/Guia3/src/pergunta.py +++ b/Guia3/src/pergunta.py @@ -1,4 +1,5 @@ from typing import List, Tuple, Dict +from abc import ABC -class Pergunta: +class Pergunta(): pass \ No newline at end of file diff --git a/Guia3/src/perguntadiscursiva.py b/Guia3/src/perguntadiscursiva.py index f4c26af..c743c9c 100644 --- a/Guia3/src/perguntadiscursiva.py +++ b/Guia3/src/perguntadiscursiva.py @@ -1,4 +1,15 @@ from typing import List, Tuple, Dict class PerguntaDiscursiva: - pass \ No newline at end of file + def __init__(self, texto, resposta_esperada = None): + self.texto = texto + self.resposta_esperada = resposta_esperada + + def validar_resposta(self, res): + if res == self.resposta_esperada: + return True + else: + return False + + def get_tipo(self): + return "discursiva" \ No newline at end of file diff --git a/Guia3/src/perguntamultiplaescolha.py b/Guia3/src/perguntamultiplaescolha.py index bcbe94d..64ed8de 100644 --- a/Guia3/src/perguntamultiplaescolha.py +++ b/Guia3/src/perguntamultiplaescolha.py @@ -1,4 +1,23 @@ from typing import List, Tuple, Dict -class PerguntaMultiplaEscolha: - pass \ No newline at end of file +class PerguntaMultiplaEscolha(): + def __init__(self, texto, alternativas, explicacao_geral = None): + self.texto = texto + self.alternativas = alternativas + self.explicacao_geral = explicacao_geral + + def validar_resposta(self, indice): + resposta = self.alternativas[indice] + return resposta.get_correta() + + def get_alternativa_correta(self): + for res in self.alternativas: + if res.get_correta() == True: + return res + + def get_tipo(self): + return "multipla_escolha" + + def get_explicacao(self): + return self.explicacao_geral + \ No newline at end of file diff --git a/Guia3/src/questionario.py b/Guia3/src/questionario.py index 7525582..90e4537 100644 --- a/Guia3/src/questionario.py +++ b/Guia3/src/questionario.py @@ -1,4 +1,12 @@ from typing import List, Tuple, Dict class Questionario: - pass + def __init__(self, titulo): + self.titulo = titulo + self.perguntas = [] + + def adicionar_pergunta(self, pergunta): + self.perguntas.append(pergunta) + + def criar_attempt(self, nome) + pass diff --git a/Guia3/src/tentativaquestionario.py b/Guia3/src/tentativaquestionario.py index 9947dd1..9529974 100644 --- a/Guia3/src/tentativaquestionario.py +++ b/Guia3/src/tentativaquestionario.py @@ -1,4 +1,4 @@ from typing import List, Tuple, Dict class TentativaQuestionario: - pass \ No newline at end of file + \ No newline at end of file From 2f20b58b2338a2585852a9261bee9d6a667347d3 Mon Sep 17 00:00:00 2001 From: edsomft Date: Fri, 29 May 2026 21:40:35 -0300 Subject: [PATCH 4/9] =?UTF-8?q?finaliza=C3=A7=C3=A3o=20Guia3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Guia3/src/questionario.py | 10 ++++++++-- Guia3/src/respostadiscursiva.py | 11 ++++++++++- Guia3/src/respostaobjetiva.py | 15 ++++++++++++++- Guia3/src/tentativaquestionario.py | 24 +++++++++++++++++++++++- 4 files changed, 55 insertions(+), 5 deletions(-) diff --git a/Guia3/src/questionario.py b/Guia3/src/questionario.py index 90e4537..e30401a 100644 --- a/Guia3/src/questionario.py +++ b/Guia3/src/questionario.py @@ -1,4 +1,5 @@ from typing import List, Tuple, Dict +from tentativaquestionario import TentativaQuestionario class Questionario: def __init__(self, titulo): @@ -8,5 +9,10 @@ def __init__(self, titulo): def adicionar_pergunta(self, pergunta): self.perguntas.append(pergunta) - def criar_attempt(self, nome) - pass + @property + def usuario(self): + return self.usuario + + def criar_attempt(self, usuario): + return TentativaQuestionario(self, usuario) + diff --git a/Guia3/src/respostadiscursiva.py b/Guia3/src/respostadiscursiva.py index 4ea6dbb..b02c5e5 100644 --- a/Guia3/src/respostadiscursiva.py +++ b/Guia3/src/respostadiscursiva.py @@ -1,4 +1,13 @@ from typing import List, Tuple, Dict class RespostaDiscursiva: - pass \ No newline at end of file + def __init__(self, pergunta, texto_resposta): + self.pergunta = pergunta + self.texto_resposta = texto_resposta + @property + def esta_correta(self): + return self.pergunta.validar_resposta(self.texto_resposta) + + def calcular_pontuacao(self): + if self.esta_correta == True: + return 1 \ No newline at end of file diff --git a/Guia3/src/respostaobjetiva.py b/Guia3/src/respostaobjetiva.py index 72ed2d0..294f514 100644 --- a/Guia3/src/respostaobjetiva.py +++ b/Guia3/src/respostaobjetiva.py @@ -1,4 +1,17 @@ from typing import List, Tuple, Dict class RespostaObjetiva: - pass \ No newline at end of file + def __init__(self, pergunta, indice_escolhido): + self.pergunta = pergunta + self.indiece_escolhido = indice_escolhido + + @property + def esta_correta(self): + return self.pergunta.validar_resposta(self.indiece_escolhido) + + def calcular_pontuacao(self): + if self.esta_correta == True: + return 1 + else: + return 0 + diff --git a/Guia3/src/tentativaquestionario.py b/Guia3/src/tentativaquestionario.py index 9529974..083be47 100644 --- a/Guia3/src/tentativaquestionario.py +++ b/Guia3/src/tentativaquestionario.py @@ -1,4 +1,26 @@ from typing import List, Tuple, Dict class TentativaQuestionario: - \ No newline at end of file + def __init__(self, quiz, usuario): + self.quiz = quiz + self.usuario = usuario + self.respostas = [] + + + @usuario.setter + def usuario(self, novo_usuario): + self.usuario = novo_usuario + + def registrar_resposta(self, indice, resposta): + registro = [indice, resposta] + self.respostas.append(registro) + + def calcular_pontuacao(self): + pontuacao = 0 + for res in self.respostas: + pergunta = self.quiz.perguntas[res[0]] + + if pergunta.validar_resposta(res[1]) == True: + pontuacao = pontuacao +1 + return pontuacao + \ No newline at end of file From ca599c94d1f19dc2c6c2fbe385a0f46c6e8db3ed Mon Sep 17 00:00:00 2001 From: edsomft Date: Fri, 29 May 2026 23:03:31 -0300 Subject: [PATCH 5/9] =?UTF-8?q?finaliza=C3=A7=C3=A3o=20Guia3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Guia3/src/questionario.py | 6 +----- Guia3/src/tentativaquestionario.py | 21 +++++++++++++-------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Guia3/src/questionario.py b/Guia3/src/questionario.py index e30401a..5563fa5 100644 --- a/Guia3/src/questionario.py +++ b/Guia3/src/questionario.py @@ -1,5 +1,5 @@ from typing import List, Tuple, Dict -from tentativaquestionario import TentativaQuestionario +from src.tentativaquestionario import TentativaQuestionario class Questionario: def __init__(self, titulo): @@ -9,10 +9,6 @@ def __init__(self, titulo): def adicionar_pergunta(self, pergunta): self.perguntas.append(pergunta) - @property - def usuario(self): - return self.usuario - def criar_attempt(self, usuario): return TentativaQuestionario(self, usuario) diff --git a/Guia3/src/tentativaquestionario.py b/Guia3/src/tentativaquestionario.py index 083be47..950b25f 100644 --- a/Guia3/src/tentativaquestionario.py +++ b/Guia3/src/tentativaquestionario.py @@ -1,16 +1,11 @@ from typing import List, Tuple, Dict class TentativaQuestionario: - def __init__(self, quiz, usuario): - self.quiz = quiz + def __init__(self, questionario, usuario): + self.questionario = questionario self.usuario = usuario self.respostas = [] - - @usuario.setter - def usuario(self, novo_usuario): - self.usuario = novo_usuario - def registrar_resposta(self, indice, resposta): registro = [indice, resposta] self.respostas.append(registro) @@ -18,9 +13,19 @@ def registrar_resposta(self, indice, resposta): def calcular_pontuacao(self): pontuacao = 0 for res in self.respostas: - pergunta = self.quiz.perguntas[res[0]] + pergunta = self.questionario.perguntas[res[0]] if pergunta.validar_resposta(res[1]) == True: pontuacao = pontuacao +1 return pontuacao + + def finalizar(self): + self._finalizado = True + pontuacao = self.calcular_pontuacao() + total = len(self.questionario.perguntas) + feedback = f"Você acertou {pontuacao} de {total} questões." + return pontuacao, feedback + + def is_finalizado(self): + return self._finalizado \ No newline at end of file From 3ff32e6c99fbe3823405d81a17a68a296314ffd9 Mon Sep 17 00:00:00 2001 From: edsomft Date: Fri, 12 Jun 2026 21:04:35 -0300 Subject: [PATCH 6/9] guia 3 --- .../record_repository.cpython-313.pyc | Bin 2669 -> 2669 bytes .../test_runner.cpython-313-pytest-9.0.3.pyc | Bin 0 -> 5427 bytes Guia3/src/pergunta.py | 19 +++++++-- Guia3/src/perguntadiscursiva.py | 8 ++-- Guia3/src/perguntamultiplaescolha.py | 8 ++-- Guia3/src/questionario.py | 6 +-- Guia3/src/resposta.py | 15 +++++++- Guia3/src/respostadiscursiva.py | 11 +++--- Guia3/src/respostaobjetiva.py | 19 ++++----- Guia3/src/tentativaquestionario.py | 36 +++++++++--------- 10 files changed, 74 insertions(+), 48 deletions(-) create mode 100644 Guia1/tests/__pycache__/test_runner.cpython-313-pytest-9.0.3.pyc diff --git a/Guia1/src/repositories/__pycache__/record_repository.cpython-313.pyc b/Guia1/src/repositories/__pycache__/record_repository.cpython-313.pyc index e158126a9db219d66b6e4760b2ab8fb933aa1084..83b0a73ff40bf8ea11e95c713f1a579d381c8c44 100644 GIT binary patch delta 20 acmaDW@>YcVGcPX}0}%MUm)OXi#03CCuLYn0 delta 20 acmaDW@>YcVGcPX}0}vQ~64=O{#03CB+y#ID diff --git a/Guia1/tests/__pycache__/test_runner.cpython-313-pytest-9.0.3.pyc b/Guia1/tests/__pycache__/test_runner.cpython-313-pytest-9.0.3.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6a14ebaebf6dda69044ad72ab1064227f7e69d80 GIT binary patch literal 5427 zcmb_gU2GKB6~41GflyM$xF|LF$!k-{2sgqdq))M}H^DiPK0R&GOiHI0jI<6FQcBO| z40FUzmklSJd$HUG#&t5p9*G809OY91Sd5V3p%ihUv?3)!8Dxu;Qb8ITkrE*%+^^fY zzPxGb^wS_rlJ=&=nelz{@LL{-+UsNx>)6rfKC^d?=RUFgs%U?Q5f$Dn``ndyXOsJe z*+SmP(s_VOajC#QdFxYAi_DXL}+ZP-z?1*?}Pek#gQnrJwcjbd*a^6%<&%O#s{%Nh)G zMXhR#m=@JDP7`ajb&aM+)viTm;&QjpHi2J7PrSSN-agR zycX*BwUu3ml!VB@L~G!7PSuE>Q?+c?M-rdaL0FExk=Dm^hGiyrh7Tv`A&t{*+&)Dq zqJ^%-SvHlSg<)Q(Td#@Z{tg5-% z{_{6)YK5u|i`8{A^}FY*_dG^p|9tg0)0N*yjDWCxuyp6haRhVIKKy==jHaVgE@MlN}~KPumeSk zhYNOo1=#sjz|JRm26i~F1bZt?;U%Fm)b&-Kd{VdeD)%T>dt>jly*Kx~cVIF({nGSHcQ<~J z_#pAS#A0pZOw;kX+7lnuHhmT(_3Qo>CXt$%nx1+2=ogQR9O_~mYQM+bwP$KN7vzJp z@S&jS0EK4FFSGuobZX}b#PQsORH2oTaIO=(@&(ue-?nZx%}e5zuA z?}Qa*qJ*S^t_|c*Nv@4lPOhMG$a^A%tU~2*sda>a->UX*?NV7NqjKedunLuhq`;^Q z=Q1kGy?KPQN97mJDo65mS_49jDPs@+z~Ttt7X}jGpTg%Y@HsTEDO%B$ZF<&W*${dF zBNg3DTPCBh!zp=;2+mtTZj3xMhtnoD`y$|tFkl3d*iuYQIfskLZ-rq<{^F9pvK(bN zZ>GV4X=cWnh?hG?ZwBTIL(kH@q8KJo)CsyB3sfaX%vu+9T4cQFyF^@`O?P4kA*a8Q zE3zLS2-)w_e?2z0JWKyoqcP0?>_0={lD~aU+P@^!gxVg-)xSLbv(pRmwpn@GoV@*G z&uJ!R>l1f&7V6ut9DnS(&DRT63UiwsbJd-Xs_Tm~5pR6I;#S3E?3O(F`t+r#OSAFa z3k~hF4ef=716PGdv8Ku9LTt;OmbE7lGif3D?Q`Z^*YxUlu^D5IO7^fG-=~ z7l{BF6UstcVK3yuP2{|k;=LXaW7IOS;`)5K^JYSsD#^pk5QbqK0N+30&l?Zo%Jo&2 zMtGJdSFFJPPSuUkq|Vc6>4PBM4sxQ^LTvk08WeCqU1oy;a~D zUnyol%cw_(xa*{!^uaQoJxWPiGp1Aq0VRa5E&|oH44k=Pmgih9<~h>AtTuKoqaAWu zR>_lF4`wZ>whw_(?A8l+vm;P^ahXhN)5BB4clvK$iWKVf1+lej8RaSj)e3}7G>i|=9MWhh|DOMyzUb}`m;qw`mt??H0?II`wy;%5I` zWYeU!*tl+PecRoh4;%Mi6(3c{Z&Y2YniM~*-trkwnht!RTI_lu_6yapJCdLe0cnl>R&ag zezVRa=`u6Dsp>|fNofqnt Kcp@~h+x!>mB!#d5 literal 0 HcmV?d00001 diff --git a/Guia3/src/pergunta.py b/Guia3/src/pergunta.py index 4161784..4f00f88 100644 --- a/Guia3/src/pergunta.py +++ b/Guia3/src/pergunta.py @@ -1,5 +1,18 @@ from typing import List, Tuple, Dict -from abc import ABC +from abc import ABC, abstractmethod -class Pergunta(): - pass \ No newline at end of file +class Pergunta(ABC): + def __init__(self, texto, explicacao_geral=None): + self.texto = texto + self.explicacao_geral = explicacao_geral + + @abstractmethod + def validar_resposta(self, resposta): + pass + + def get_explicacao(self): + return self.explicacao_geral + + @abstractmethod + def get_tipo(self): + pass \ No newline at end of file diff --git a/Guia3/src/perguntadiscursiva.py b/Guia3/src/perguntadiscursiva.py index c743c9c..ac82262 100644 --- a/Guia3/src/perguntadiscursiva.py +++ b/Guia3/src/perguntadiscursiva.py @@ -1,9 +1,11 @@ from typing import List, Tuple, Dict +from .pergunta import Pergunta -class PerguntaDiscursiva: - def __init__(self, texto, resposta_esperada = None): - self.texto = texto +class PerguntaDiscursiva(Pergunta): + def __init__(self, texto, resposta_esperada=None, case_sensitive=True): + super().__init__(texto) self.resposta_esperada = resposta_esperada + self.case_sensitive = case_sensitive def validar_resposta(self, res): if res == self.resposta_esperada: diff --git a/Guia3/src/perguntamultiplaescolha.py b/Guia3/src/perguntamultiplaescolha.py index 64ed8de..a37ec0b 100644 --- a/Guia3/src/perguntamultiplaescolha.py +++ b/Guia3/src/perguntamultiplaescolha.py @@ -1,10 +1,10 @@ from typing import List, Tuple, Dict +from .pergunta import Pergunta -class PerguntaMultiplaEscolha(): - def __init__(self, texto, alternativas, explicacao_geral = None): - self.texto = texto +class PerguntaMultiplaEscolha(Pergunta): + def __init__(self, texto, alternativas, explicacao_geral=None): + super().__init__(texto, explicacao_geral) self.alternativas = alternativas - self.explicacao_geral = explicacao_geral def validar_resposta(self, indice): resposta = self.alternativas[indice] diff --git a/Guia3/src/questionario.py b/Guia3/src/questionario.py index 5563fa5..b8abb92 100644 --- a/Guia3/src/questionario.py +++ b/Guia3/src/questionario.py @@ -1,14 +1,14 @@ from typing import List, Tuple, Dict -from src.tentativaquestionario import TentativaQuestionario +from .tentativaquestionario import TentativaQuestionario class Questionario: def __init__(self, titulo): self.titulo = titulo self.perguntas = [] - + def adicionar_pergunta(self, pergunta): self.perguntas.append(pergunta) - + def criar_attempt(self, usuario): return TentativaQuestionario(self, usuario) diff --git a/Guia3/src/resposta.py b/Guia3/src/resposta.py index 846d771..f5add9a 100644 --- a/Guia3/src/resposta.py +++ b/Guia3/src/resposta.py @@ -1,4 +1,15 @@ from typing import List, Tuple, Dict +from abc import ABC, abstractmethod -class Resposta: - pass \ No newline at end of file +class Resposta(ABC): + def __init__(self, pergunta): + self.pergunta = pergunta + + @property + @abstractmethod + def esta_correta(self): + pass + + @abstractmethod + def calcular_pontuacao(self): + pass \ No newline at end of file diff --git a/Guia3/src/respostadiscursiva.py b/Guia3/src/respostadiscursiva.py index b02c5e5..2939504 100644 --- a/Guia3/src/respostadiscursiva.py +++ b/Guia3/src/respostadiscursiva.py @@ -1,13 +1,14 @@ from typing import List, Tuple, Dict +from .resposta import Resposta -class RespostaDiscursiva: +class RespostaDiscursiva(Resposta): def __init__(self, pergunta, texto_resposta): - self.pergunta = pergunta + super().__init__(pergunta) self.texto_resposta = texto_resposta + @property def esta_correta(self): return self.pergunta.validar_resposta(self.texto_resposta) - + def calcular_pontuacao(self): - if self.esta_correta == True: - return 1 \ No newline at end of file + return 1.0 if self.esta_correta else 0.0 \ No newline at end of file diff --git a/Guia3/src/respostaobjetiva.py b/Guia3/src/respostaobjetiva.py index 294f514..d96d7c1 100644 --- a/Guia3/src/respostaobjetiva.py +++ b/Guia3/src/respostaobjetiva.py @@ -1,17 +1,14 @@ from typing import List, Tuple, Dict +from .resposta import Resposta -class RespostaObjetiva: +class RespostaObjetiva(Resposta): def __init__(self, pergunta, indice_escolhido): - self.pergunta = pergunta - self.indiece_escolhido = indice_escolhido - + super().__init__(pergunta) + self.indice_escolhido = indice_escolhido + @property def esta_correta(self): - return self.pergunta.validar_resposta(self.indiece_escolhido) - + return self.pergunta.validar_resposta(self.indice_escolhido) + def calcular_pontuacao(self): - if self.esta_correta == True: - return 1 - else: - return 0 - + return 1.0 if self.esta_correta else 0.0 \ No newline at end of file diff --git a/Guia3/src/tentativaquestionario.py b/Guia3/src/tentativaquestionario.py index 950b25f..bd752f2 100644 --- a/Guia3/src/tentativaquestionario.py +++ b/Guia3/src/tentativaquestionario.py @@ -1,31 +1,33 @@ -from typing import List, Tuple, Dict +from datetime import datetime +from .respostaobjetiva import RespostaObjetiva +from .respostadiscursiva import RespostaDiscursiva +from .perguntamultiplaescolha import PerguntaMultiplaEscolha +from .perguntadiscursiva import PerguntaDiscursiva class TentativaQuestionario: def __init__(self, questionario, usuario): self.questionario = questionario self.usuario = usuario + self.data_inicio = datetime.now() + self.data_fim = None self.respostas = [] - - def registrar_resposta(self, indice, resposta): - registro = [indice, resposta] - self.respostas.append(registro) + + def registrar_resposta(self, indice_pergunta, valor): + pergunta = self.questionario.perguntas[indice_pergunta] + if isinstance(pergunta, PerguntaMultiplaEscolha): + resposta = RespostaObjetiva(pergunta, valor) + else: + resposta = RespostaDiscursiva(pergunta, valor) + self.respostas.append(resposta) def calcular_pontuacao(self): - pontuacao = 0 - for res in self.respostas: - pergunta = self.questionario.perguntas[res[0]] + return sum(r.calcular_pontuacao() for r in self.respostas) - if pergunta.validar_resposta(res[1]) == True: - pontuacao = pontuacao +1 - return pontuacao - def finalizar(self): - self._finalizado = True + self.data_fim = datetime.now() pontuacao = self.calcular_pontuacao() - total = len(self.questionario.perguntas) - feedback = f"Você acertou {pontuacao} de {total} questões." + feedback = f"Pontuação final: {pontuacao}" return pontuacao, feedback def is_finalizado(self): - return self._finalizado - \ No newline at end of file + return self.data_fim is not None \ No newline at end of file From 8623fbebcb19f3df8faf02d74e45ae64e805b696 Mon Sep 17 00:00:00 2001 From: edsomft Date: Sat, 13 Jun 2026 11:31:38 -0300 Subject: [PATCH 7/9] =?UTF-8?q?Instala=C3=A7=C3=A3o=20dontenv=20para=20rod?= =?UTF-8?q?ar=20teste=20da=20main=20utilizando=20APIKey?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Guia4/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Guia4/main.py b/Guia4/main.py index e02c2f6..258a8f0 100644 --- a/Guia4/main.py +++ b/Guia4/main.py @@ -1,4 +1,4 @@ -from dotenv import load_dotenv +from dotenv import load_dotenv load_dotenv() from src.alternativa import Alternativa From 6343c528e1a60b24bb2d9537f6a1443ef96adc4a Mon Sep 17 00:00:00 2001 From: edsomft Date: Fri, 19 Jun 2026 17:55:30 -0300 Subject: [PATCH 8/9] Envio Guia5. Pendente testes --- Guia4.zip | Bin 9776 -> 0 bytes Guia4/README.md | 4 +- Guia5/.gitignore | 8 + Guia5/Guia5_SistemaEscola_README.md | 330 ++++++++++++++++++++++++++++ Guia5/dados.py | 120 ++++++++++ Guia5/main.py | 225 +++++++++++++++++++ Guia5/src/__init__.py | 7 + Guia5/src/aluno.py | 43 ++++ Guia5/src/disciplina.py | 48 ++++ Guia5/src/pessoa.py | 36 +++ Guia5/src/professor.py | 50 +++++ Guia5/src/turma.py | 97 ++++++++ Guia5/utils/__init__.py | 31 +++ Guia5/utils/leitores.py | 106 +++++++++ Guia5/utils/validador.py | 75 +++++++ 15 files changed, 1177 insertions(+), 3 deletions(-) delete mode 100644 Guia4.zip create mode 100644 Guia5/.gitignore create mode 100644 Guia5/Guia5_SistemaEscola_README.md create mode 100644 Guia5/dados.py create mode 100644 Guia5/main.py create mode 100644 Guia5/src/__init__.py create mode 100644 Guia5/src/aluno.py create mode 100644 Guia5/src/disciplina.py create mode 100644 Guia5/src/pessoa.py create mode 100644 Guia5/src/professor.py create mode 100644 Guia5/src/turma.py create mode 100644 Guia5/utils/__init__.py create mode 100644 Guia5/utils/leitores.py create mode 100644 Guia5/utils/validador.py diff --git a/Guia4.zip b/Guia4.zip deleted file mode 100644 index 41e2bd2289e6aa7d4b9fac599a2a0b5b5ac4d270..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9776 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 -String api_key -String model - -String base_url + -Groq_obj groq +__init__(api_key: str, model: str = "llama3-70b-8192") - +corrigir_resposta(pergunta: PerguntaDiscursiva, resposta_aluno: str) -> Dict -_fazer_chamada_api(prompt: str) -> str - -_tratar_erro(e: Exception) -> None } class Correcao { diff --git a/Guia5/.gitignore b/Guia5/.gitignore new file mode 100644 index 0000000..07b15a4 --- /dev/null +++ b/Guia5/.gitignore @@ -0,0 +1,8 @@ +.venv/ +__pycache__/ +*.pyc +.pytest_cache/ +.coverage +htmlcov/ +dist/ +*.egg-info/ diff --git a/Guia5/Guia5_SistemaEscola_README.md b/Guia5/Guia5_SistemaEscola_README.md new file mode 100644 index 0000000..767c17f --- /dev/null +++ b/Guia5/Guia5_SistemaEscola_README.md @@ -0,0 +1,330 @@ +# Guia5 — Sistema de Escola (POO) + +## Contexto + +Este projeto implementa um **Sistema de Escola** completo utilizando o paradigma de **Orientação a Objetos** em Python. O sistema gerencia alunos, professores, disciplinas e turmas, aplicando os principais conceitos do curso de POO. + +--- + +## 1. Diagrama UML + +### Diagrama de Classes + +```mermaid +classDiagram + class Pessoa { + <> + -_nome: str + -_cpf: str + +__init__(nome, cpf) + +nome() str + +cpf() str + +apresentar()* str + +__str__() str + } + + class Aluno { + -_matricula: int + -_notas: dict + -_contador_matricula: int$ + +__init__(nome, cpf) + +matricula() int + +notas() dict + +adicionar_nota(disciplina, nota) + +media(disciplina) float + +aprovado(disciplina, minimo) bool + +resetar_contador(valor)$ + +apresentar() str + } + + class Professor { + -_especialidade: str + -_senha_hash: str + -_disciplinas_ministradas: list + +__init__(nome, cpf, especialidade, senha) + +especialidade() str + +disciplinas_ministradas() list + +autenticar(senha) bool + +adicionar_disciplina(nome) + +remover_disciplina(nome) + +apresentar() str + } + + class Disciplina { + -_nome: str + -_carga_horaria: int + -_professor: Professor + +__init__(nome, carga_horaria, professor) + +nome() str + +carga_horaria() int + +professor() Professor + +__str__() str + } + + class Turma { + -_codigo: str + -_serie: str + -_capacidade: int + -_alunos: list + -_disciplinas: list + +__init__(codigo, serie, capacidade) + +vagas_disponiveis() int + +matricular_aluno(aluno) + +remover_aluno(aluno) + +adicionar_disciplina(disciplina) + +gerar_boletim(aluno) dict + +gerar_boletim_turma() dict + +total_alunos() int + +__str__() str + } + + class Validador { + <> + +cpf(cpf)$ tuple + +nome(nome)$ tuple + +senha(senha)$ tuple + +carga_horaria(valor)$ tuple + +capacidade(valor)$ tuple + +nota(valor)$ tuple + +texto_nao_vazio(valor, campo)$ tuple + } + + Pessoa <|-- Aluno : herança + Pessoa <|-- Professor : herança + Disciplina "1" --> "1" Professor : ministrada por + Turma "1" --> "0..*" Aluno : matricula + Turma "1" --> "0..*" Disciplina : possui +``` + +--- + +## 2. Descrição das Classes + +### `Pessoa` (abstrata) +Classe base abstrata (via `ABC`) para qualquer pessoa do sistema escolar. Define atributos comuns `_nome` e `_cpf` com encapsulamento via `@property`, e o método abstrato `apresentar()`, que funciona como um **contrato** obrigando subclasses a defini-lo. + +**Conceitos aplicados:** Classe abstrata, ABC, encapsulamento, `@property`, `@abstractmethod`. + +--- + +### `Aluno` +Herda de `Pessoa`. Representa um estudante matriculado. Gera automaticamente um número de **matrícula sequencial** usando um **atributo de classe** (`_contador_matricula`) e um **método de classe** (`resetar_contador`). Armazena notas por disciplina e calcula médias e situação de aprovação. + +**Conceitos aplicados:** Herança, atributo de classe, método de classe (`@classmethod`), composição de dicionário, validação com `@property.setter`. + +--- + +### `Professor` +Herda de `Pessoa`. Representa um docente. Além dos dados básicos e da lista de disciplinas ministradas, armazena um **hash SHA-256 da senha** para autenticação segura. O método `autenticar(senha)` permite verificar o acesso sem expor a senha em texto puro. O lançamento de notas no sistema exige autenticação prévia de um professor. + +**Conceitos aplicados:** Herança, encapsulamento, `@property.setter` com validação, lista mutável encapsulada, hash de senha. + +--- + +### `Disciplina` +Representa uma matéria escolar com nome, carga horária e professor responsável. Na criação, já notifica o professor via `adicionar_disciplina()`. Ao trocar de professor (via setter), remove o vínculo do anterior e cria o vínculo com o novo — demonstrando **composição bidirecional**. + +**Conceitos aplicados:** Composição, `@property.setter` com efeito colateral, referência entre objetos. + +--- + +### `Turma` +Agrega alunos e disciplinas. Controla a capacidade máxima de alunos e garante que não haja duplicatas. O método `gerar_boletim(aluno)` retorna o boletim completo de **um aluno específico**: para cada disciplina da turma, lista as notas lançadas, a média calculada e a situação (Aprovado/Reprovado/Sem notas). O método `gerar_boletim_turma()` retorna o boletim de **todos os alunos matriculados** de uma vez, reutilizando `gerar_boletim` internamente. + +**Conceitos aplicados:** Composição (lista de `Aluno` e `Disciplina`), encapsulamento de listas (retorno de cópias), reuso de método dentro da própria classe. + +--- + +### `Validador` *(novo)* +Classe utilitária com **métodos estáticos** que centralizam toda a lógica de validação de entradas do sistema. Cada método retorna uma tupla `(bool, resultado_ou_mensagem)`, onde `True` indica sucesso e `False` indica falha acompanhada de uma mensagem descritiva. O `main.py` usa esses métodos em loops que repetem a solicitação ao usuário até receber um valor válido. + +| Método | O que valida | +|--------|-------------| +| `cpf(cpf)` | Exatamente 11 dígitos numéricos; aceita pontos e traços; rejeita sequências de dígitos iguais | +| `nome(nome)` | Apenas letras e espaços; obrigatório ter ao menos nome + sobrenome | +| `senha(senha)` | Mínimo de 4 caracteres | +| `carga_horaria(valor)` | Inteiro positivo entre 1 e 400 | +| `capacidade(valor)` | Inteiro positivo entre 1 e 100 | +| `nota(valor)` | Número entre 0 e 10; aceita vírgula como separador decimal | +| `texto_nao_vazio(valor, campo)` | Campo genérico não vazio | + +**Conceitos aplicados:** Classe utilitária, métodos estáticos (`@staticmethod`), separação de responsabilidades, padrão de retorno consistente. + +--- + +## 3. Estrutura do Projeto + +``` +sistema-escola/ +├── src/ ← classes de domínio +│ ├── __init__.py ← exporta Pessoa, Aluno, Professor, Disciplina, Turma +│ ├── pessoa.py +│ ├── aluno.py +│ ├── professor.py +│ ├── disciplina.py +│ └── turma.py +├── utils/ ← ferramentas de suporte +│ ├── __init__.py ← exporta Validador + todas as funções de leitura +│ ├── validador.py ← classe Validador (CPF, nome, nota…) +│ └── leitores.py ← ler_*, escolher_da_lista, separador, autenticar_professor +├── tests/ +│ ├── __init__.py +│ ├── test_aluno.py +│ ├── test_professor.py +│ ├── test_disciplina.py +│ ├── test_turma.py +│ ├── test_sistema_escola.py +│ └── test_validador.py +├── main.py +├── requirements.txt +├── .gitignore +└── Guia5_SistemaEscola_README.md +``` + +### Separação de responsabilidades + +| Pasta | O que contém | Critério | +|-------|-------------|----------| +| `src/` | Classes de domínio (`Aluno`, `Professor`, `Turma`…) | Representam as entidades do problema | +| `utils/` | `Validador` + funções `ler_*` / `escolher_da_lista` | Ferramentas de suporte sem lógica de negócio | +| `tests/` | Suíte completa de testes pytest | Um arquivo por classe testada | + +--- + +## 4. Preparando o Ambiente + +### 4.1 Criar o ambiente virtual + +Na pasta do projeto `...\sistema-escola>`, execute: + +```bash +python -m venv .venv +``` + +### 4.2 Ativar o ambiente virtual + +#### Windows + +```bash +.\.venv\Scripts\activate +``` + +#### Linux / macOS + +```bash +source .venv/bin/activate +``` + +### 4.3 Instalar dependências + +```bash +pip install -r requirements.txt +``` + +### 4.4 Executar os testes + +```bash +pytest -v +``` + +ou + +```bash +python -m pytest -v +``` + +Para ver a **cobertura de testes**: + +```bash +pytest -v --cov=src --cov-report=term-missing +``` + +### 4.5 Executar o sistema + +```bash +python main.py +``` + +--- + +## 5. Funcionalidades do Sistema (main.py) + +Ao rodar `python main.py`, o terminal exibe um menu interativo com as seguintes opções: + +| Opção | Funcionalidade | +|-------|----------------| +| `[1]` | **Cadastrar Professor** — informa nome completo, CPF, especialidade e **senha** (mín. 4 caracteres) | +| `[2]` | **Cadastrar Disciplina** — escolhe o professor em uma lista e define carga horária | +| `[3]` | **Criar Turma** — define código (ex: `9A`), série e capacidade máxima; permite vincular disciplinas escolhendo-as em lista | +| `[4]` | **Matricular Aluno** — escolhe a turma em lista, depois cadastra o aluno (nome completo + CPF) | +| `[5]` | **Lançar Nota** *(requer acesso de professor)* — o professor se autentica com sua senha; só pode lançar notas nas disciplinas que ministra | +| `[6]` | **Boletim de um Aluno** — escolhe **turma** → escolhe **aluno da turma**; exibe notas, média e situação por disciplina | +| `[7]` | **Boletim da Turma Inteira** — escolhe a turma em lista; exibe o boletim de todos os alunos matriculados | +| `[8]` | **Listar Turmas** — exibe todas as turmas com suas disciplinas e alunos | +| `[0]` | **Sair** | + +> **Nenhuma opção do menu exige buscar algo digitando um nome ou código.** Toda seleção (professor, disciplina, turma, aluno) é feita por **lista numerada**. + +> **Validação em loop:** todos os campos de entrada passam pela classe `Validador`. Caso o valor seja inválido, o sistema exibe uma mensagem de erro clara e solicita nova digitação até receber um valor correto. + +> **Padronização de texto:** todo campo de texto (exceto senha) passa por `.strip().upper()` antes de ser processado. + +--- + +## 5.1 Fluxo de Autenticação para Lançar Nota + +``` +[5] Lançar Nota + └─ Lista de professores cadastrados + └─ Escolhe o professor + └─ Digita a senha + ├─ Senha correta → escolhe aluno → escolhe disciplina (só as do professor) → digita nota + └─ Senha errada → "Acesso negado." → volta ao menu +``` + +O professor só visualiza e pode lançar notas nas disciplinas que ele próprio ministra. + +--- + +## 5.2 Fluxo do Boletim de um Aluno + +``` +[6] Boletim de um Aluno + └─ Lista todas as turmas cadastradas + └─ Escolhe a turma + └─ Lista os alunos da turma selecionada + └─ Escolhe o aluno → exibe boletim +``` + +A etapa intermediária de seleção de turma garante que o usuário veja apenas os alunos da turma de interesse. + +--- + +## 6. Exemplo de uso típico + +1. Cadastre um professor: `Rodrigo Alves | Matemática | senha: mat123` +2. Crie a disciplina `Matemática (80h)` vinculada a ele +3. Crie a turma `9A | 9º Ano | capacidade: 30` +4. Matricule alunos na turma +5. Lance notas: autentique-se como professor → escolha aluno → escolha disciplina → informe nota +6. Consulte o boletim: escolha a turma → escolha o aluno → veja o resultado + +--- + +## 7. Conceitos de POO Aplicados + +| Conceito | Onde aparece | +|----------|-------------| +| **Classes e objetos** | Todas as classes | +| **Encapsulamento** | `_atributos` com `@property` e validação nos setters | +| **Herança** | `Aluno` e `Professor` herdam de `Pessoa` | +| **Polimorfismo** | `apresentar()` redefinido em cada subclasse; `__str__` delega a `apresentar()` | +| **Classe abstrata / ABC** | `Pessoa` com `@abstractmethod apresentar()` | +| **Atributo de classe** | `Aluno._contador_matricula` | +| **Método de classe** | `Aluno.resetar_contador()` com `@classmethod` | +| **Método estático** | `Validador.*` com `@staticmethod` | +| **Classe utilitária** | `Validador` em `utils/` — centraliza toda a lógica de validação | +| **Separação de camadas** | `src/` (domínio) vs `utils/` (suporte) — cada pasta com responsabilidade única | +| **Composição** | `Turma` contém listas de `Aluno` e `Disciplina`; `Disciplina` referencia `Professor` | +| **Autenticação com hash** | `Professor._senha_hash` usando SHA-256 | +| **Validação e exceções** | `ValueError`, `OverflowError` em cenários de negócio; `Validador` em entradas do usuário | diff --git a/Guia5/dados.py b/Guia5/dados.py new file mode 100644 index 0000000..ea4bec9 --- /dev/null +++ b/Guia5/dados.py @@ -0,0 +1,120 @@ +from src import * +from utils import * + +def popular_banco( + turmas: list, + professores: list, + disciplinas: list, + alunos: list, +) -> None: + """Popula as listas do sistema com dados de exemplo para demonstração. + + Deve ser chamada no início de menu_principal(), passando as próprias + listas já criadas lá. Exemplo de uso: + + def menu_principal() -> None: + turmas, professores, disciplinas, alunos = [], [], [], [] + popular_banco(turmas, professores, disciplinas, alunos) + ... + """ + + # ── Professores (nome com sobrenome, CPF 11 dígitos sem repetição, senha ≥ 4) ── + prof_mat = Professor("RODRIGO ALVES", "12345678901", "MATEMATICA", "mat123") + prof_por = Professor("ANA LIMA", "23456789012", "PORTUGUES", "por123") + prof_cis = Professor("BEATRIZ COSTA", "34567890123", "CIENCIAS", "cis123") + prof_his = Professor("CARLOS MENDES", "45678901234", "HISTORIA", "his123") + professores.extend([prof_mat, prof_por, prof_cis, prof_his]) + + # ── Disciplinas (nome não vazio, carga horária 1–400) ──────────────────────── + disc_mat = Disciplina("MATEMATICA", 80, prof_mat) + disc_por = Disciplina("PORTUGUES", 80, prof_por) + disc_cis = Disciplina("CIENCIAS", 60, prof_cis) + disc_his = Disciplina("HISTORIA", 60, prof_his) + disc_geo = Disciplina("GEOGRAFIA", 40, prof_his) + disciplinas.extend([disc_mat, disc_por, disc_cis, disc_his, disc_geo]) + + # ── Turmas (código e série não vazios, capacidade 1–100) ───────────────────── + turma_9a = Turma("9A", "9 ANO", 30) + turma_9b = Turma("9B", "9 ANO", 30) + turma_8a = Turma("8A", "8 ANO", 30) + + for disc in [disc_mat, disc_por, disc_cis, disc_his, disc_geo]: + turma_9a.adicionar_disciplina(disc) + turma_9b.adicionar_disciplina(disc) + + for disc in [disc_mat, disc_por, disc_cis]: + turma_8a.adicionar_disciplina(disc) + + turmas.extend([turma_9a, turma_9b, turma_8a]) + + # ── Alunos turma 9A ────────────────────────────────────────────────────────── + dados_9a = [ + ("ALICE SOUZA", "56789012345"), + ("BRUNO SILVA", "67890123456"), + ("CAMILA SANTOS", "78901234567"), + ("DANIEL LIMA", "89012345678"), + ("EDUARDA COSTA", "90123456789"), + ] + alunos_9a = [] + for nome, cpf in dados_9a: + a = Aluno(nome, cpf) + turma_9a.matricular_aluno(a) + alunos_9a.append(a) + alunos.append(a) + + # ── Alunos turma 9B ────────────────────────────────────────────────────────── + dados_9b = [ + ("FELIPE ROCHA", "10234567891"), + ("GABRIELA NUNES", "20345678912"), + ("HENRIQUE DIAS", "30456789123"), + ] + alunos_9b = [] + for nome, cpf in dados_9b: + a = Aluno(nome, cpf) + turma_9b.matricular_aluno(a) + alunos_9b.append(a) + alunos.append(a) + + # ── Alunos turma 8A ────────────────────────────────────────────────────────── + dados_8a = [ + ("IGOR FERREIRA", "40567891234"), + ("JULIA MORAES", "50678912345"), + ("LUCAS MARTINS", "60789123456"), + ] + alunos_8a = [] + for nome, cpf in dados_8a: + a = Aluno(nome, cpf) + turma_8a.matricular_aluno(a) + alunos_8a.append(a) + alunos.append(a) + + # ── Notas turma 9A ─────────────────────────────────────────────────────────── + notas_9a = { + "ALICE SOUZA": {"MATEMATICA": [9.0, 8.5], "PORTUGUES": [7.0, 8.0], "CIENCIAS": [9.5, 10.0], "HISTORIA": [6.0, 7.5], "GEOGRAFIA": [8.0]}, + "BRUNO SILVA": {"MATEMATICA": [4.0, 5.5], "PORTUGUES": [6.0, 6.5], "CIENCIAS": [3.0, 4.0], "HISTORIA": [5.0, 5.5], "GEOGRAFIA": [5.0]}, + "CAMILA SANTOS": {"MATEMATICA": [7.0, 7.5], "PORTUGUES": [9.0, 9.5], "CIENCIAS": [7.0, 8.0], "HISTORIA": [8.0, 8.5], "GEOGRAFIA": [9.0]}, + "DANIEL LIMA": {"MATEMATICA": [6.0, 6.5], "PORTUGUES": [5.0, 5.5], "CIENCIAS": [6.5, 7.0], "HISTORIA": [4.0, 3.5], "GEOGRAFIA": [6.0]}, + "EDUARDA COSTA": {"MATEMATICA": [8.0, 9.0], "PORTUGUES": [8.5, 7.5], "CIENCIAS": [9.0, 9.5], "HISTORIA": [7.0, 8.0], "GEOGRAFIA": [8.5]}, + } + for aluno in alunos_9a: + for disc, notas in notas_9a[aluno.nome].items(): + for n in notas: + aluno.adicionar_nota(disc, n) + + # ── Notas turma 9B (parcialmente lançadas) ─────────────────────────────────── + notas_9b = { + "FELIPE ROCHA": {"MATEMATICA": [7.0], "PORTUGUES": [6.5]}, + "GABRIELA NUNES": {"MATEMATICA": [9.5], "CIENCIAS": [8.0]}, + "HENRIQUE DIAS": {"HISTORIA": [5.0], "GEOGRAFIA": [4.5]}, + } + for aluno in alunos_9b: + for disc, notas in notas_9b[aluno.nome].items(): + for n in notas: + aluno.adicionar_nota(disc, n) + + # ── Notas turma 8A (sem notas — turma recém-criada para demonstrar estado inicial) + # alunos_8a propositalmente sem notas lançadas ────────────────────────── + + print("✔ Banco de dados de exemplo carregado com sucesso!") + print(f" {len(professores)} professores | {len(disciplinas)} disciplinas | " + f"{len(turmas)} turmas | {len(alunos)} alunos") diff --git a/Guia5/main.py b/Guia5/main.py new file mode 100644 index 0000000..07576f2 --- /dev/null +++ b/Guia5/main.py @@ -0,0 +1,225 @@ +from src import * +from utils import * +from dados import popular_banco + + +def menu_principal() -> None: + turmas: list[Turma] = [] + professores: list[Professor] = [] + disciplinas: list[Disciplina] = [] + alunos: list[Aluno] = [] + + popular_banco(turmas, professores, disciplinas, alunos) + + opcoes = { + "1": "Cadastrar Professor", + "2": "Cadastrar Disciplina", + "3": "Criar Turma", + "4": "Matricular Aluno", + "5": "Lançar Nota [requer acesso de professor]", + "6": "Exibir Boletim de um Aluno", + "7": "Exibir Boletim da Turma Inteira", + "8": "Listar Turmas", + "0": "Sair", + } + + while True: + separador("SISTEMA DE ESCOLA — MENU PRINCIPAL") + for k, v in opcoes.items(): + print(f" [{k}] {v}") + escolha = input("\nEscolha uma opção: ").strip() + + # ── 1. Cadastrar Professor ──────────────────────────────────────────── + if escolha == "1": + separador("CADASTRAR PROFESSOR") + nome = ler_nome("Nome completo: ") + cpf = ler_cpf("CPF (somente números): ") + esp = ler_texto_validado("Especialidade: ", "Especialidade") + senha = ler_senha("Senha (mín. 4 caracteres): ") + try: + prof = Professor(nome, cpf, esp, senha) + professores.append(prof) + print(f"\n✔ {prof} cadastrado com sucesso!") + except ValueError as e: + print(f"\n✖ Erro: {e}") + + # ── 2. Cadastrar Disciplina ─────────────────────────────────────────── + elif escolha == "2": + separador("CADASTRAR DISCIPLINA") + prof_sel = escolher_da_lista(professores, "professor") + if prof_sel is None: + continue + nome_disc = ler_texto_validado("Nome da disciplina: ", "Nome da disciplina") + ch = ler_carga_horaria() + try: + disc = Disciplina(nome_disc, ch, prof_sel) + disciplinas.append(disc) + print(f"\n✔ {disc} cadastrada com sucesso!") + except ValueError as e: + print(f"\n✖ Erro: {e}") + + # ── 3. Criar Turma ─────────────────────────────────────────────────── + elif escolha == "3": + separador("CRIAR TURMA") + codigo = ler_texto_validado("Código da turma (ex: 9A): ", "Código") + serie = ler_texto_validado("Série (ex: 9º ANO): ", "Série") + cap = ler_capacidade() + try: + t = Turma(codigo, serie, cap) + turmas.append(t) + print(f"\n✔ {t} criada com sucesso!") + except ValueError as e: + print(f"\n✖ Erro: {e}") + continue + + if disciplinas: + print("\nDeseja adicionar disciplinas a esta turma?") + while True: + disponiveis = [d for d in disciplinas if d not in t.disciplinas] + if not disponiveis: + print("Todas as disciplinas já foram adicionadas.") + break + disc_sel = escolher_da_lista(disponiveis, "disciplina") + if disc_sel is None: + break + t.adicionar_disciplina(disc_sel) + print(f"✔ '{disc_sel.nome}' adicionada à turma.") + if input("Adicionar outra disciplina? (s/n): ").strip().upper() != "S": + break + + # ── 4. Matricular Aluno ────────────────────────────────────────────── + elif escolha == "4": + separador("MATRICULAR ALUNO") + turma_sel = escolher_da_lista(turmas, "turma") + if turma_sel is None: + continue + nome = ler_nome("Nome completo do aluno: ") + cpf = ler_cpf("CPF do aluno (somente números): ") + try: + aluno = Aluno(nome, cpf) + turma_sel.matricular_aluno(aluno) + alunos.append(aluno) + print(f"\n✔ {aluno} matriculado em '{turma_sel.codigo}'!") + except (ValueError, OverflowError) as e: + print(f"\n✖ Erro: {e}") + + # ── 5. Lançar Nota [requer acesso de professor] ───────────────────── + elif escolha == "5": + separador("LANÇAR NOTA") + + if not professores: + print("✖ Nenhum professor cadastrado. Cadastre um professor primeiro.") + continue + + prof_autenticado = autenticar_professor(professores) + if prof_autenticado is None: + continue + + alunos_disponiveis = [ + a for a in alunos + if any( + a in t.alunos and any(d.professor == prof_autenticado for d in t.disciplinas) + for t in turmas + ) + ] + if not alunos_disponiveis: + print("✖ Nenhum aluno encontrado nas turmas com disciplinas deste professor.") + continue + + aluno_sel = escolher_da_lista(alunos_disponiveis, "aluno") + if aluno_sel is None: + continue + + turma_do_aluno = next((t for t in turmas if aluno_sel in t.alunos), None) + if turma_do_aluno is None: + print("✖ Turma do aluno não encontrada.") + continue + + discs_professor = [d for d in turma_do_aluno.disciplinas if d.professor == prof_autenticado] + if not discs_professor: + print("✖ Este professor não ministra disciplinas na turma deste aluno.") + continue + + disc_sel = escolher_da_lista(discs_professor, "disciplina") + if disc_sel is None: + continue + + nota = ler_nota() + try: + aluno_sel.adicionar_nota(disc_sel.nome, nota) + print(f"\n✔ Nota {nota} registrada para '{aluno_sel.nome}' em '{disc_sel.nome}'.") + except ValueError as e: + print(f"\n✖ Erro: {e}") + + # ── 6. Boletim de um Aluno ─────────────────────────────────────────── + elif escolha == "6": + separador("BOLETIM DO ALUNO") + if not turmas: + print("✖ Nenhuma turma cadastrada.") + continue + print("Selecione a turma:") + turma_sel = escolher_da_lista(turmas, "turma") + if turma_sel is None: + continue + if not turma_sel.alunos: + print(f"✖ A turma '{turma_sel.codigo}' não possui alunos matriculados.") + continue + print(f"\nAlunos da turma {turma_sel.codigo} — {turma_sel.serie}:") + aluno_sel = escolher_da_lista(turma_sel.alunos, "aluno") + if aluno_sel is None: + continue + try: + boletim = turma_sel.gerar_boletim(aluno_sel) + print(f"\nBoletim de {aluno_sel.nome} — Turma {turma_sel.codigo}") + print(f"{'DISCIPLINA':<20} {'NOTAS':<20} {'MÉDIA':>8} {'SITUAÇÃO':>12}") + print("-" * 62) + if not boletim: + print("Nenhuma disciplina cadastrada nesta turma.") + for disc_nome, dados in boletim.items(): + notas_str = ", ".join(str(n) for n in dados["notas"]) or "—" + media_str = f"{dados['media']:.2f}" if dados["media"] is not None else "—" + print(f"{disc_nome:<20} {notas_str:<20} {media_str:>8} {dados['situacao']:>12}") + except ValueError as e: + print(f"\n✖ Erro: {e}") + + # ── 7. Boletim da Turma Inteira ────────────────────────────────────── + elif escolha == "7": + separador("BOLETIM DA TURMA INTEIRA") + turma_sel = escolher_da_lista(turmas, "turma") + if turma_sel is None: + continue + boletim_turma = turma_sel.gerar_boletim_turma() + if not boletim_turma: + print("✖ Nenhum aluno matriculado nesta turma.") + continue + for nome_aluno, boletim in boletim_turma.items(): + print(f"\n• {nome_aluno}") + print(f" {'DISCIPLINA':<20} {'NOTAS':<20} {'MÉDIA':>8} {'SITUAÇÃO':>12}") + print(" " + "-" * 60) + if not boletim: + print(" Nenhuma disciplina cadastrada nesta turma.") + for disc_nome, dados in boletim.items(): + notas_str = ", ".join(str(n) for n in dados["notas"]) or "—" + media_str = f"{dados['media']:.2f}" if dados["media"] is not None else "—" + print(f" {disc_nome:<20} {notas_str:<20} {media_str:>8} {dados['situacao']:>12}") + + # ── 8. Listar Turmas ───────────────────────────────────────────────── + elif escolha == "8": + separador("TURMAS CADASTRADAS") + if not turmas: + print("Nenhuma turma cadastrada.") + for t in turmas: + print(f"\n{t}") + print(" Disciplinas:", ", ".join(d.nome for d in t.disciplinas) or "—") + print(" Alunos:", ", ".join(a.nome for a in t.alunos) or "—") + + # ── 0. Sair ────────────────────────────────────────────────────────── + elif escolha == "0": + print("\nAté logo! 👋\n") + break + else: + print("\n✖ Opção inválida. Tente novamente.") + + +if __name__ == "__main__": + menu_principal() diff --git a/Guia5/src/__init__.py b/Guia5/src/__init__.py new file mode 100644 index 0000000..1f53bbc --- /dev/null +++ b/Guia5/src/__init__.py @@ -0,0 +1,7 @@ +from .pessoa import Pessoa +from .aluno import Aluno +from .professor import Professor +from .disciplina import Disciplina +from .turma import Turma + +__all__ = ["Pessoa", "Aluno", "Professor", "Disciplina", "Turma"] diff --git a/Guia5/src/aluno.py b/Guia5/src/aluno.py new file mode 100644 index 0000000..cb48abf --- /dev/null +++ b/Guia5/src/aluno.py @@ -0,0 +1,43 @@ +from .pessoa import Pessoa + + +class Aluno(Pessoa): + _contador_matricula: int = 1000 # atributo de classe + + def __init__(self, nome: str, cpf: str): + super().__init__(nome, cpf) + Aluno._contador_matricula += 1 + self._matricula: int = Aluno._contador_matricula + self._notas: dict[str, list[float]] = {} # disciplina_nome → [notas] + + @property + def matricula(self) -> int: + return self._matricula + + @property + def notas(self) -> dict[str, list[float]]: + return {k: list(v) for k, v in self._notas.items()} + + def adicionar_nota(self, disciplina: str, nota: float) -> None: + if not (0.0 <= nota <= 10.0): + raise ValueError(f"Nota deve estar entre 0 e 10. Recebido: {nota}") + self._notas.setdefault(disciplina, []).append(nota) + + def media(self, disciplina: str) -> float: + notas = self._notas.get(disciplina) + if not notas: + raise ValueError(f"Nenhuma nota registrada para '{disciplina}'.") + return sum(notas) / len(notas) + + def aprovado(self, disciplina: str, minimo: float = 6.0) -> bool: + return self.media(disciplina) >= minimo + + @classmethod + def resetar_contador(cls, valor: int = 1000) -> None: + cls._contador_matricula = valor + + def apresentar(self) -> str: + return f"Aluno: {self._nome} | Matrícula: {self._matricula}" + + def __repr__(self) -> str: + return f"Aluno(nome={self._nome!r}, matricula={self._matricula})" diff --git a/Guia5/src/disciplina.py b/Guia5/src/disciplina.py new file mode 100644 index 0000000..fa7035e --- /dev/null +++ b/Guia5/src/disciplina.py @@ -0,0 +1,48 @@ +from __future__ import annotations +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from .professor import Professor + + +class Disciplina: + + def __init__(self, nome: str, carga_horaria: int, professor: "Professor"): + if not nome or not nome.strip(): + raise ValueError("Nome da disciplina não pode ser vazio.") + if carga_horaria <= 0: + raise ValueError("Carga horária deve ser positiva.") + self._nome: str = nome.strip() + self._carga_horaria: int = carga_horaria + self._professor: Professor = professor + professor.adicionar_disciplina(self._nome) + + @property + def nome(self) -> str: + return self._nome + + @property + def carga_horaria(self) -> int: + return self._carga_horaria + + @carga_horaria.setter + def carga_horaria(self, valor: int) -> None: + if valor <= 0: + raise ValueError("Carga horária deve ser positiva.") + self._carga_horaria = valor + + @property + def professor(self) -> "Professor": + return self._professor + + @professor.setter + def professor(self, novo: "Professor") -> None: + self._professor.remover_disciplina(self._nome) + self._professor = novo + novo.adicionar_disciplina(self._nome) + + def __str__(self) -> str: + return f"Disciplina: {self._nome} ({self._carga_horaria}h) | Prof. {self._professor.nome}" + + def __repr__(self) -> str: + return f"Disciplina(nome={self._nome!r}, carga_horaria={self._carga_horaria})" diff --git a/Guia5/src/pessoa.py b/Guia5/src/pessoa.py new file mode 100644 index 0000000..396ecd5 --- /dev/null +++ b/Guia5/src/pessoa.py @@ -0,0 +1,36 @@ +from abc import ABC, abstractmethod + + +class Pessoa(ABC): + + def __init__(self, nome: str, cpf: str): + if not nome or not nome.strip(): + raise ValueError("Nome não pode ser vazio.") + if not cpf or not cpf.strip(): + raise ValueError("CPF não pode ser vazio.") + self._nome = nome.strip() + self._cpf = cpf.strip() + + @property + def nome(self) -> str: + return self._nome + + @nome.setter + def nome(self, valor: str): + if not valor or not valor.strip(): + raise ValueError("Nome não pode ser vazio.") + self._nome = valor.strip() + + @property + def cpf(self) -> str: + return self._cpf + + @abstractmethod + def apresentar(self) -> str: + """Retorna uma apresentação textual da pessoa.""" + + def __str__(self) -> str: + return self.apresentar() + + def __repr__(self) -> str: + return f"{self.__class__.__name__}(nome={self._nome!r}, cpf={self._cpf!r})" diff --git a/Guia5/src/professor.py b/Guia5/src/professor.py new file mode 100644 index 0000000..b67e428 --- /dev/null +++ b/Guia5/src/professor.py @@ -0,0 +1,50 @@ +import hashlib +from .pessoa import Pessoa + + +class Professor(Pessoa): + + def __init__(self, nome: str, cpf: str, especialidade: str, senha: str): + super().__init__(nome, cpf) + if not especialidade or not especialidade.strip(): + raise ValueError("Especialidade não pode ser vazia.") + if not senha or len(senha) < 4: + raise ValueError("Senha deve ter ao menos 4 caracteres.") + self._especialidade: str = especialidade.strip() + self._disciplinas_ministradas: list[str] = [] + self._senha_hash: str = hashlib.sha256(senha.encode()).hexdigest() + + @property + def especialidade(self) -> str: + return self._especialidade + + @especialidade.setter + def especialidade(self, valor: str) -> None: + if not valor or not valor.strip(): + raise ValueError("Especialidade não pode ser vazia.") + self._especialidade = valor.strip() + + @property + def disciplinas_ministradas(self) -> list[str]: + return list(self._disciplinas_ministradas) + + def autenticar(self, senha: str) -> bool: + return hashlib.sha256(senha.encode()).hexdigest() == self._senha_hash + + def adicionar_disciplina(self, nome_disciplina: str) -> None: + nome = nome_disciplina.strip() + if nome not in self._disciplinas_ministradas: + self._disciplinas_ministradas.append(nome) + + def remover_disciplina(self, nome_disciplina: str) -> None: + nome = nome_disciplina.strip() + if nome in self._disciplinas_ministradas: + self._disciplinas_ministradas.remove(nome) + else: + raise ValueError(f"Disciplina '{nome}' não encontrada para este professor.") + + def apresentar(self) -> str: + return f"Prof. {self._nome} | Especialidade: {self._especialidade}" + + def __repr__(self) -> str: + return f"Professor(nome={self._nome!r}, especialidade={self._especialidade!r})" diff --git a/Guia5/src/turma.py b/Guia5/src/turma.py new file mode 100644 index 0000000..2953f0e --- /dev/null +++ b/Guia5/src/turma.py @@ -0,0 +1,97 @@ +from __future__ import annotations +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from .aluno import Aluno + from .disciplina import Disciplina + + +class Turma: + + def __init__(self, codigo: str, serie: str, capacidade: int = 40): + if not codigo or not codigo.strip(): + raise ValueError("Código da turma não pode ser vazio.") + if capacidade <= 0: + raise ValueError("Capacidade deve ser positiva.") + self._codigo: str = codigo.strip() + self._serie: str = serie.strip() + self._capacidade: int = capacidade + self._alunos: list[Aluno] = [] + self._disciplinas: list[Disciplina] = [] + + @property + def codigo(self) -> str: + return self._codigo + + @property + def serie(self) -> str: + return self._serie + + @property + def capacidade(self) -> int: + return self._capacidade + + @property + def alunos(self) -> list[Aluno]: + return list(self._alunos) + + @property + def disciplinas(self) -> list[Disciplina]: + return list(self._disciplinas) + + @property + def vagas_disponiveis(self) -> int: + return self._capacidade - len(self._alunos) + + def matricular_aluno(self, aluno: "Aluno") -> None: + if self.vagas_disponiveis == 0: + raise OverflowError(f"Turma '{self._codigo}' está cheia.") + if aluno in self._alunos: + raise ValueError(f"Aluno '{aluno.nome}' já está matriculado nesta turma.") + self._alunos.append(aluno) + + def remover_aluno(self, aluno: "Aluno") -> None: + if aluno not in self._alunos: + raise ValueError(f"Aluno '{aluno.nome}' não encontrado na turma.") + self._alunos.remove(aluno) + + def adicionar_disciplina(self, disciplina: "Disciplina") -> None: + if disciplina in self._disciplinas: + raise ValueError(f"Disciplina '{disciplina.nome}' já está na turma.") + self._disciplinas.append(disciplina) + + def gerar_boletim(self, aluno: "Aluno") -> dict[str, dict]: + if aluno not in self._alunos: + raise ValueError(f"Aluno '{aluno.nome}' não está matriculado nesta turma.") + + boletim = {} + for disciplina in self._disciplinas: + nome_disc = disciplina.nome + notas = aluno.notas.get(nome_disc, []) + if notas: + media = round(sum(notas) / len(notas), 2) + situacao = "Aprovado" if media >= 6.0 else "Reprovado" + else: + media = None + situacao = "Sem notas" + boletim[nome_disc] = { + "notas": notas, + "media": media, + "situacao": situacao, + } + return boletim + + def gerar_boletim_turma(self) -> dict[str, dict[str, dict]]: + return {aluno.nome: self.gerar_boletim(aluno) for aluno in self._alunos} + + def total_alunos(self) -> int: + return len(self._alunos) + + def __str__(self) -> str: + return ( + f"Turma {self._codigo} | {self._serie} | " + f"{len(self._alunos)}/{self._capacidade} alunos" + ) + + def __repr__(self) -> str: + return f"Turma(codigo={self._codigo!r}, serie={self._serie!r}, capacidade={self._capacidade})" diff --git a/Guia5/utils/__init__.py b/Guia5/utils/__init__.py new file mode 100644 index 0000000..99fae0f --- /dev/null +++ b/Guia5/utils/__init__.py @@ -0,0 +1,31 @@ +from .validador import Validador +from .leitores import ( + separador, + escolher_da_lista, + ler_texto, + ler_texto_simples, + ler_cpf, + ler_nome, + ler_senha, + ler_carga_horaria, + ler_capacidade, + ler_nota, + ler_texto_validado, + autenticar_professor, +) + +__all__ = [ + "Validador", + "separador", + "escolher_da_lista", + "ler_texto", + "ler_texto_simples", + "ler_cpf", + "ler_nome", + "ler_senha", + "ler_carga_horaria", + "ler_capacidade", + "ler_nota", + "ler_texto_validado", + "autenticar_professor", +] diff --git a/Guia5/utils/leitores.py b/Guia5/utils/leitores.py new file mode 100644 index 0000000..e31d84e --- /dev/null +++ b/Guia5/utils/leitores.py @@ -0,0 +1,106 @@ +from .validador import Validador + +def separador(titulo: str = "") -> None: + print("\n" + "=" * 55) + if titulo: + print(f" {titulo}") + print("=" * 55) + + +def escolher_da_lista(lista: list, rotulo_item: str = "item") -> "object | None": + if not lista: + print(f"✖ Nenhum(a) {rotulo_item} cadastrado(a).") + return None + for i, item in enumerate(lista): + print(f" {i + 1}. {item}") + try: + idx = int(input(f"\nEscolha o número do(a) {rotulo_item}: ")) - 1 + if 0 <= idx < len(lista): + return lista[idx] + print("✖ Número fora da lista.") + return None + except ValueError: + print("✖ Entrada inválida. Digite um número.") + return None + +def ler_texto(rotulo: str) -> str: + return input(rotulo).strip().upper() + + +def ler_texto_simples(rotulo: str) -> str: + return input(rotulo).strip() + + +def ler_cpf(rotulo: str = "CPF: ") -> str: + while True: + valor = input(rotulo).strip() + ok, resultado = Validador.cpf(valor) + if ok: + return resultado + print(f" ✖ {resultado}") + + +def ler_nome(rotulo: str = "Nome: ") -> str: + while True: + valor = ler_texto(rotulo) + ok, resultado = Validador.nome(valor) + if ok: + return resultado + print(f" ✖ {resultado}") + + +def ler_senha(rotulo: str = "Senha: ") -> str: + while True: + valor = ler_texto_simples(rotulo) + ok, resultado = Validador.senha(valor) + if ok: + return resultado + print(f" ✖ {resultado}") + + +def ler_carga_horaria(rotulo: str = "Carga horária (horas): ") -> int: + while True: + valor = input(rotulo).strip() + ok, resultado = Validador.carga_horaria(valor) + if ok: + return resultado + print(f" ✖ {resultado}") + + +def ler_capacidade(rotulo: str = "Capacidade máxima de alunos: ") -> int: + while True: + valor = input(rotulo).strip() + ok, resultado = Validador.capacidade(valor) + if ok: + return resultado + print(f" ✖ {resultado}") + + +def ler_nota(rotulo: str = "Nota (0–10): ") -> float: + while True: + valor = input(rotulo).strip() + ok, resultado = Validador.nota(valor) + if ok: + return resultado + print(f" ✖ {resultado}") + + +def ler_texto_validado(rotulo: str, campo: str) -> str: + while True: + valor = ler_texto(rotulo) + ok, resultado = Validador.texto_nao_vazio(valor, campo) + if ok: + return resultado + print(f" ✖ {resultado}") + +def autenticar_professor(professores: list) -> "object | None": + print("\nIdentifique-se como professor para lançar notas.") + prof_sel = escolher_da_lista(professores, "professor") + if prof_sel is None: + return None + senha = ler_texto_simples("Senha do professor: ") + if prof_sel.autenticar(senha): + print(f"✔ Acesso concedido. Bem-vindo, {prof_sel.nome}!") + return prof_sel + print("✖ Senha incorreta. Acesso negado.") + return None diff --git a/Guia5/utils/validador.py b/Guia5/utils/validador.py new file mode 100644 index 0000000..ef854c6 --- /dev/null +++ b/Guia5/utils/validador.py @@ -0,0 +1,75 @@ +import re + + +class Validador: + """Classe utilitária com métodos estáticos de validação de dados de entrada.""" + + @staticmethod + def cpf(cpf: str) -> tuple[bool, str]: + cpf_limpo = re.sub(r"[\.\-]", "", cpf).strip() + if not cpf_limpo.isdigit(): + return False, "CPF deve conter apenas números (e opcionalmente pontos/traços)." + if len(cpf_limpo) != 11: + return False, f"CPF deve ter exatamente 11 dígitos. Informado: {len(cpf_limpo)} dígito(s)." + if len(set(cpf_limpo)) == 1: + return False, "CPF inválido (todos os dígitos iguais)." + return True, cpf_limpo + + @staticmethod + def nome(nome: str) -> tuple[bool, str]: + nome_limpo = nome.strip() + if not nome_limpo: + return False, "Nome não pode ser vazio." + if not re.match(r"^[A-ZÀ-Ÿa-zà-ÿ\s]+$", nome_limpo): + return False, "Nome deve conter apenas letras e espaços (sem números ou símbolos)." + partes = nome_limpo.split() + if len(partes) < 2: + return False, "Informe nome e sobrenome (ao menos duas palavras)." + return True, nome_limpo + + @staticmethod + def senha(senha: str) -> tuple[bool, str]: + if len(senha) < 4: + return False, "A senha deve ter ao menos 4 caracteres." + return True, senha + + @staticmethod + def carga_horaria(valor: str) -> tuple[bool, str]: + try: + ch = int(valor) + except ValueError: + return False, "Carga horária deve ser um número inteiro." + if ch <= 0: + return False, "Carga horária deve ser positiva." + if ch > 400: + return False, "Carga horária não pode ultrapassar 400 horas." + return True, ch + + @staticmethod + def capacidade(valor: str) -> tuple[bool, str]: + try: + cap = int(valor) + except ValueError: + return False, "Capacidade deve ser um número inteiro." + if cap <= 0: + return False, "Capacidade deve ser positiva." + if cap > 100: + return False, "Capacidade máxima permitida é 100 alunos." + return True, cap + + @staticmethod + def nota(valor: str) -> tuple[bool, str]: + try: + nota = float(valor.replace(",", ".")) + except ValueError: + return False, "Nota deve ser um número (ex: 7.5)." + if not (0.0 <= nota <= 10.0): + return False, f"Nota deve estar entre 0 e 10. Informado: {nota}." + return True, nota + + @staticmethod + def texto_nao_vazio(valor: str, campo: str = "Campo") -> tuple[bool, str]: + limpo = valor.strip() + if not limpo: + return False, f"{campo} não pode ser vazio." + return True, limpo From ef16d145bd6cd7adfb7dd200d31796be1e74d4f0 Mon Sep 17 00:00:00 2001 From: edsomft Date: Tue, 23 Jun 2026 19:37:31 -0300 Subject: [PATCH 9/9] =?UTF-8?q?adi=C3=A7=C3=A3o=20testes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Guia5/Guia5_SistemaEscola_README.md | 2 +- Guia5/tests/__init__.py | 0 Guia5/tests/test_aluno.py | 82 ++++++++++++++++ Guia5/tests/test_disciplina.py | 56 +++++++++++ Guia5/tests/test_professor.py | 86 ++++++++++++++++ Guia5/tests/test_sistema_escola.py | 70 +++++++++++++ Guia5/tests/test_turma.py | 147 ++++++++++++++++++++++++++++ Guia5/tests/test_validador.py | 132 +++++++++++++++++++++++++ 8 files changed, 574 insertions(+), 1 deletion(-) create mode 100644 Guia5/tests/__init__.py create mode 100644 Guia5/tests/test_aluno.py create mode 100644 Guia5/tests/test_disciplina.py create mode 100644 Guia5/tests/test_professor.py create mode 100644 Guia5/tests/test_sistema_escola.py create mode 100644 Guia5/tests/test_turma.py create mode 100644 Guia5/tests/test_validador.py diff --git a/Guia5/Guia5_SistemaEscola_README.md b/Guia5/Guia5_SistemaEscola_README.md index 767c17f..85ea3ae 100644 --- a/Guia5/Guia5_SistemaEscola_README.md +++ b/Guia5/Guia5_SistemaEscola_README.md @@ -195,7 +195,7 @@ sistema-escola/ ### 4.1 Criar o ambiente virtual -Na pasta do projeto `...\sistema-escola>`, execute: +Na pasta do projeto `...\Guia5>`, execute: ```bash python -m venv .venv diff --git a/Guia5/tests/__init__.py b/Guia5/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Guia5/tests/test_aluno.py b/Guia5/tests/test_aluno.py new file mode 100644 index 0000000..29ed260 --- /dev/null +++ b/Guia5/tests/test_aluno.py @@ -0,0 +1,82 @@ +import pytest +from src.aluno import Aluno + + +@pytest.fixture(autouse=True) +def resetar_matricula(): + Aluno.resetar_contador(1000) + yield + + +class TestAlunoCriacao: + def test_criar_aluno_valido(self): + a = Aluno("Maria Silva", "111.222.333-44") + assert a.nome == "Maria Silva" + assert a.cpf == "111.222.333-44" + assert a.matricula == 1001 + + def test_matriculas_sao_incrementadas(self): + a1 = Aluno("Ana", "000.000.000-01") + a2 = Aluno("Bia", "000.000.000-02") + assert a2.matricula == a1.matricula + 1 + + def test_nome_vazio_levanta_erro(self): + with pytest.raises(ValueError): + Aluno("", "111.222.333-44") + + def test_cpf_vazio_levanta_erro(self): + with pytest.raises(ValueError): + Aluno("Maria", "") + + +class TestAlunoNotas: + def test_adicionar_nota_valida(self): + a = Aluno("Carlos", "222.333.444-55") + a.adicionar_nota("Matemática", 8.5) + assert 8.5 in a.notas["Matemática"] + + def test_nota_fora_do_intervalo(self): + a = Aluno("Carlos", "222.333.444-55") + with pytest.raises(ValueError): + a.adicionar_nota("Matemática", 11.0) + + def test_nota_negativa(self): + a = Aluno("Carlos", "222.333.444-55") + with pytest.raises(ValueError): + a.adicionar_nota("Matemática", -1.0) + + def test_media_calculada_corretamente(self): + a = Aluno("Diana", "333.444.555-66") + a.adicionar_nota("Física", 7.0) + a.adicionar_nota("Física", 9.0) + assert a.media("Física") == pytest.approx(8.0) + + def test_media_sem_notas_levanta_erro(self): + a = Aluno("Eva", "444.555.666-77") + with pytest.raises(ValueError): + a.media("Química") + + def test_aprovado_acima_do_minimo(self): + a = Aluno("Fábio", "555.666.777-88") + a.adicionar_nota("História", 7.0) + assert a.aprovado("História") is True + + def test_reprovado_abaixo_do_minimo(self): + a = Aluno("Gabriela", "666.777.888-99") + a.adicionar_nota("História", 4.0) + assert a.aprovado("História") is False + + def test_notas_retorna_copia(self): + a = Aluno("Henrique", "777.888.999-00") + a.adicionar_nota("Arte", 9.0) + copia = a.notas + copia["Arte"].append(1.0) + assert len(a.notas["Arte"]) == 1 + + +class TestAlunoStr: + def test_str_contem_nome_e_matricula(self): + a = Aluno("Igor", "888.999.000-11") + s = str(a) + assert "Igor" in s + assert str(a.matricula) in s diff --git a/Guia5/tests/test_disciplina.py b/Guia5/tests/test_disciplina.py new file mode 100644 index 0000000..bfdb824 --- /dev/null +++ b/Guia5/tests/test_disciplina.py @@ -0,0 +1,56 @@ +import pytest +from src.professor import Professor +from src.disciplina import Disciplina + + +@pytest.fixture +def professor(): + return Professor("Rodrigo Alves", "12345678900", "Física", "senha123") + + +class TestDisciplinaCriacao: + def test_criar_disciplina_valida(self, professor): + d = Disciplina("Física Clássica", 80, professor) + assert d.nome == "Física Clássica" + assert d.carga_horaria == 80 + assert d.professor is professor + + def test_nome_vazio_levanta_erro(self, professor): + with pytest.raises(ValueError): + Disciplina("", 80, professor) + + def test_carga_horaria_invalida(self, professor): + with pytest.raises(ValueError): + Disciplina("Física", 0, professor) + + def test_professor_recebe_disciplina_automaticamente(self, professor): + Disciplina("Física Moderna", 60, professor) + assert "Física Moderna" in professor.disciplinas_ministradas + + +class TestDisciplinaSetters: + def test_setter_carga_horaria(self, professor): + d = Disciplina("Física", 80, professor) + d.carga_horaria = 120 + assert d.carga_horaria == 120 + + def test_setter_carga_horaria_invalida(self, professor): + d = Disciplina("Física", 80, professor) + with pytest.raises(ValueError): + d.carga_horaria = -10 + + def test_trocar_professor(self, professor): + novo_prof = Professor("Beatriz Costa", "98765432100", "Física", "senha123") + d = Disciplina("Física", 80, professor) + d.professor = novo_prof + assert d.professor is novo_prof + assert "Física" not in professor.disciplinas_ministradas + assert "Física" in novo_prof.disciplinas_ministradas + + +class TestDisciplinaStr: + def test_str_contem_nome_e_professor(self, professor): + d = Disciplina("Física", 80, professor) + s = str(d) + assert "Física" in s + assert professor.nome in s diff --git a/Guia5/tests/test_professor.py b/Guia5/tests/test_professor.py new file mode 100644 index 0000000..3a1d43a --- /dev/null +++ b/Guia5/tests/test_professor.py @@ -0,0 +1,86 @@ +import pytest +from src.professor import Professor + + +SENHA_PADRAO = "senha123" + + +def make_professor(nome="João Souza", cpf="99988877766", esp="Matemática", senha=SENHA_PADRAO): + return Professor(nome, cpf, esp, senha) + + +class TestProfessorCriacao: + def test_criar_professor_valido(self): + p = make_professor() + assert p.nome == "João Souza" + assert p.especialidade == "Matemática" + + def test_especialidade_vazia_levanta_erro(self): + with pytest.raises(ValueError): + Professor("João Souza", "99988877766", "", SENHA_PADRAO) + + def test_senha_curta_levanta_erro(self): + with pytest.raises(ValueError): + Professor("João Souza", "99988877766", "Matemática", "abc") + + def test_setter_especialidade(self): + p = make_professor() + p.especialidade = "Física" + assert p.especialidade == "Física" + + def test_setter_especialidade_vazia_levanta_erro(self): + p = make_professor() + with pytest.raises(ValueError): + p.especialidade = " " + + +class TestProfessorAutenticacao: + def test_autenticar_senha_correta(self): + p = make_professor() + assert p.autenticar(SENHA_PADRAO) is True + + def test_autenticar_senha_errada(self): + p = make_professor() + assert p.autenticar("senhaerrada") is False + + def test_autenticar_senha_vazia(self): + p = make_professor() + assert p.autenticar("") is False + + +class TestProfessorDisciplinas: + def test_adicionar_disciplina(self): + p = make_professor(nome="Ana Lima", cpf="11100022233", esp="Química") + p.adicionar_disciplina("Química Orgânica") + assert "Química Orgânica" in p.disciplinas_ministradas + + def test_disciplina_duplicada_ignorada(self): + p = make_professor(nome="Ana Lima", cpf="11100022233", esp="Química") + p.adicionar_disciplina("Química Orgânica") + p.adicionar_disciplina("Química Orgânica") + assert p.disciplinas_ministradas.count("Química Orgânica") == 1 + + def test_remover_disciplina(self): + p = make_professor(nome="Ana Lima", cpf="11100022233", esp="Química") + p.adicionar_disciplina("Química Orgânica") + p.remover_disciplina("Química Orgânica") + assert "Química Orgânica" not in p.disciplinas_ministradas + + def test_remover_disciplina_inexistente_levanta_erro(self): + p = make_professor(nome="Ana Lima", cpf="11100022233", esp="Química") + with pytest.raises(ValueError): + p.remover_disciplina("Biologia") + + def test_disciplinas_retorna_copia(self): + p = make_professor(nome="Ana Lima", cpf="11100022233", esp="Química") + lista = p.disciplinas_ministradas + lista.append("Invasora") + assert "Invasora" not in p.disciplinas_ministradas + + +class TestProfessorStr: + def test_str_contem_nome_e_especialidade(self): + p = make_professor(nome="Carlos Matos", cpf="33344455566", esp="Biologia") + s = str(p) + assert "Carlos Matos" in s + assert "Biologia" in s diff --git a/Guia5/tests/test_sistema_escola.py b/Guia5/tests/test_sistema_escola.py new file mode 100644 index 0000000..bd72d6c --- /dev/null +++ b/Guia5/tests/test_sistema_escola.py @@ -0,0 +1,70 @@ +""" +Testes de integração do Sistema de Escola. +Simulam um cenário realista com turma, disciplina, professor e alunos. +""" +import pytest +from src.aluno import Aluno +from src.professor import Professor +from src.disciplina import Disciplina +from src.turma import Turma + + +SENHA = "senha123" + + +@pytest.fixture(autouse=True) +def resetar_matricula(): + Aluno.resetar_contador(1000) + yield + + +def test_fluxo_completo_turma(): + """Cria turma, matricula alunos, lança notas e gera boletim.""" + prof = Professor("Marcos Vinicius", "01002003040", "Ciências", SENHA) + mat = Disciplina("Ciências", 60, prof) + turma = Turma("6B", "6º Ano", capacidade=5) + turma.adicionar_disciplina(mat) + + nomes = ["Alice Silva", "Bruno Costa", "Camila Dias"] + alunos = [Aluno(n, f"0000000000{i+1}") for i, n in enumerate(nomes)] + for a in alunos: + turma.matricular_aluno(a) + + alunos[0].adicionar_nota("Ciências", 9.0) + alunos[1].adicionar_nota("Ciências", 5.5) + alunos[2].adicionar_nota("Ciências", 7.0) + alunos[2].adicionar_nota("Ciências", 9.0) + + boletim_turma = turma.gerar_boletim_turma() + + assert boletim_turma["Alice Silva"]["Ciências"]["situacao"] == "Aprovado" + assert boletim_turma["Bruno Costa"]["Ciências"]["situacao"] == "Reprovado" + assert boletim_turma["Camila Dias"]["Ciências"]["media"] == pytest.approx(8.0) + assert boletim_turma["Camila Dias"]["Ciências"]["situacao"] == "Aprovado" + + +def test_professor_ministra_varias_disciplinas(): + prof = Professor("Juliana Ramos", "05006007080", "Português", SENHA) + Disciplina("Português", 80, prof) + Disciplina("Literatura", 40, prof) + assert "Português" in prof.disciplinas_ministradas + assert "Literatura" in prof.disciplinas_ministradas + + +def test_professor_autenticacao(): + prof = Professor("Rodrigo Alves", "12345678901", "Matemática", SENHA) + assert prof.autenticar(SENHA) is True + assert prof.autenticar("senhaerrada") is False + + +def test_pessoa_nao_pode_ser_instanciada_diretamente(): + from src.pessoa import Pessoa + with pytest.raises(TypeError): + Pessoa("Teste Silva", "00000000000") # type: ignore + + +def test_aluno_apresentar(): + a = Aluno("Eduardo Lima", "12312312312") + apresentacao = a.apresentar() + assert "Eduardo Lima" in apresentacao + assert str(a.matricula) in apresentacao diff --git a/Guia5/tests/test_turma.py b/Guia5/tests/test_turma.py new file mode 100644 index 0000000..d62b31e --- /dev/null +++ b/Guia5/tests/test_turma.py @@ -0,0 +1,147 @@ +import pytest +from src.aluno import Aluno +from src.professor import Professor +from src.disciplina import Disciplina +from src.turma import Turma + + +@pytest.fixture(autouse=True) +def resetar_matricula(): + Aluno.resetar_contador(1000) + yield + + +@pytest.fixture +def professor(): + return Professor("Letícia Nunes", "00011122233", "Matemática", "senha123") + + +@pytest.fixture +def disciplina(professor): + return Disciplina("Matemática", 80, professor) + + +@pytest.fixture +def turma(): + return Turma("9A", "9º Ano", capacidade=3) + + +@pytest.fixture +def alunos(): + return [Aluno(f"Aluno {i}", f"00{i}.000.000-00") for i in range(1, 4)] + + +class TestTurmaCriacao: + def test_criar_turma_valida(self, turma): + assert turma.codigo == "9A" + assert turma.serie == "9º Ano" + assert turma.capacidade == 3 + + def test_codigo_vazio_levanta_erro(self): + with pytest.raises(ValueError): + Turma("", "9º Ano") + + def test_capacidade_invalida(self): + with pytest.raises(ValueError): + Turma("9A", "9º Ano", capacidade=0) + + +class TestTurmaMatricula: + def test_matricular_aluno(self, turma, alunos): + turma.matricular_aluno(alunos[0]) + assert alunos[0] in turma.alunos + + def test_turma_cheia_levanta_erro(self, turma, alunos): + for a in alunos: + turma.matricular_aluno(a) + extra = Aluno("Extra", "999.999.999-99") + with pytest.raises(OverflowError): + turma.matricular_aluno(extra) + + def test_aluno_duplicado_levanta_erro(self, turma, alunos): + turma.matricular_aluno(alunos[0]) + with pytest.raises(ValueError): + turma.matricular_aluno(alunos[0]) + + def test_remover_aluno(self, turma, alunos): + turma.matricular_aluno(alunos[0]) + turma.remover_aluno(alunos[0]) + assert alunos[0] not in turma.alunos + + def test_remover_aluno_ausente_levanta_erro(self, turma, alunos): + with pytest.raises(ValueError): + turma.remover_aluno(alunos[0]) + + def test_vagas_disponiveis(self, turma, alunos): + turma.matricular_aluno(alunos[0]) + assert turma.vagas_disponiveis == 2 + + def test_total_alunos(self, turma, alunos): + turma.matricular_aluno(alunos[0]) + turma.matricular_aluno(alunos[1]) + assert turma.total_alunos() == 2 + + +class TestTurmaDisciplinas: + def test_adicionar_disciplina(self, turma, disciplina): + turma.adicionar_disciplina(disciplina) + assert disciplina in turma.disciplinas + + def test_disciplina_duplicada_levanta_erro(self, turma, disciplina): + turma.adicionar_disciplina(disciplina) + with pytest.raises(ValueError): + turma.adicionar_disciplina(disciplina) + + +class TestTurmaBoletim: + def test_boletim_com_notas(self, turma, alunos, disciplina): + turma.matricular_aluno(alunos[0]) + turma.adicionar_disciplina(disciplina) + alunos[0].adicionar_nota("Matemática", 8.0) + alunos[0].adicionar_nota("Matemática", 6.0) + boletim = turma.gerar_boletim(alunos[0]) + assert boletim["Matemática"]["media"] == pytest.approx(7.0) + assert boletim["Matemática"]["situacao"] == "Aprovado" + assert boletim["Matemática"]["notas"] == [8.0, 6.0] + + def test_boletim_sem_notas(self, turma, alunos, disciplina): + turma.matricular_aluno(alunos[0]) + turma.adicionar_disciplina(disciplina) + boletim = turma.gerar_boletim(alunos[0]) + assert boletim["Matemática"]["situacao"] == "Sem notas" + assert boletim["Matemática"]["media"] is None + + def test_boletim_reprovado(self, turma, alunos, disciplina): + turma.matricular_aluno(alunos[0]) + turma.adicionar_disciplina(disciplina) + alunos[0].adicionar_nota("Matemática", 3.0) + boletim = turma.gerar_boletim(alunos[0]) + assert boletim["Matemática"]["situacao"] == "Reprovado" + + def test_boletim_aluno_nao_matriculado_levanta_erro(self, turma, alunos): + with pytest.raises(ValueError): + turma.gerar_boletim(alunos[0]) + + def test_boletim_turma_inteira(self, turma, alunos, disciplina): + turma.adicionar_disciplina(disciplina) + turma.matricular_aluno(alunos[0]) + turma.matricular_aluno(alunos[1]) + alunos[0].adicionar_nota("Matemática", 9.0) + alunos[1].adicionar_nota("Matemática", 4.0) + + boletim_turma = turma.gerar_boletim_turma() + + assert alunos[0].nome in boletim_turma + assert alunos[1].nome in boletim_turma + assert boletim_turma[alunos[0].nome]["Matemática"]["situacao"] == "Aprovado" + assert boletim_turma[alunos[1].nome]["Matemática"]["situacao"] == "Reprovado" + + def test_boletim_turma_vazia(self, turma): + assert turma.gerar_boletim_turma() == {} + + +class TestTurmaStr: + def test_str_contem_codigo_e_serie(self, turma): + s = str(turma) + assert "9A" in s + assert "9º Ano" in s diff --git a/Guia5/tests/test_validador.py b/Guia5/tests/test_validador.py new file mode 100644 index 0000000..490a560 --- /dev/null +++ b/Guia5/tests/test_validador.py @@ -0,0 +1,132 @@ +import pytest +from utils import Validador + + +class TestValidadorCpf: + def test_cpf_valido_somente_digitos(self): + ok, resultado = Validador.cpf("12345678901") + assert ok is True + assert resultado == "12345678901" + + def test_cpf_valido_com_formatacao(self): + ok, resultado = Validador.cpf("123.456.789-01") + assert ok is True + assert resultado == "12345678901" + + def test_cpf_com_menos_de_11_digitos(self): + ok, msg = Validador.cpf("1234567890") + assert ok is False + assert "11 dígitos" in msg + + def test_cpf_com_mais_de_11_digitos(self): + ok, msg = Validador.cpf("123456789012") + assert ok is False + assert "11 dígitos" in msg + + def test_cpf_com_letras(self): + ok, msg = Validador.cpf("1234567890A") + assert ok is False + assert "apenas números" in msg + + def test_cpf_todos_iguais(self): + ok, msg = Validador.cpf("11111111111") + assert ok is False + assert "inválido" in msg.lower() + + def test_cpf_vazio(self): + ok, msg = Validador.cpf("") + assert ok is False + + +class TestValidadorNome: + def test_nome_valido(self): + ok, resultado = Validador.nome("João Silva") + assert ok is True + + def test_nome_sem_sobrenome(self): + ok, msg = Validador.nome("João") + assert ok is False + assert "sobrenome" in msg.lower() or "duas palavras" in msg.lower() + + def test_nome_com_numeros(self): + ok, msg = Validador.nome("João123 Silva") + assert ok is False + assert "letras" in msg.lower() + + def test_nome_vazio(self): + ok, msg = Validador.nome("") + assert ok is False + + def test_nome_com_acentos(self): + ok, resultado = Validador.nome("Lívia Gonçalves") + assert ok is True + + +class TestValidadorSenha: + def test_senha_valida(self): + ok, resultado = Validador.senha("abcd") + assert ok is True + + def test_senha_curta(self): + ok, msg = Validador.senha("abc") + assert ok is False + assert "4 caracteres" in msg + + def test_senha_vazia(self): + ok, msg = Validador.senha("") + assert ok is False + + +class TestValidadorCargaHoraria: + def test_carga_valida(self): + ok, resultado = Validador.carga_horaria("80") + assert ok is True + assert resultado == 80 + + def test_carga_zero(self): + ok, msg = Validador.carga_horaria("0") + assert ok is False + + def test_carga_negativa(self): + ok, msg = Validador.carga_horaria("-10") + assert ok is False + + def test_carga_texto(self): + ok, msg = Validador.carga_horaria("oitenta") + assert ok is False + + def test_carga_acima_limite(self): + ok, msg = Validador.carga_horaria("401") + assert ok is False + + +class TestValidadorNota: + def test_nota_valida(self): + ok, resultado = Validador.nota("7.5") + assert ok is True + assert resultado == pytest.approx(7.5) + + def test_nota_virgula(self): + ok, resultado = Validador.nota("8,5") + assert ok is True + assert resultado == pytest.approx(8.5) + + def test_nota_zero(self): + ok, resultado = Validador.nota("0") + assert ok is True + + def test_nota_dez(self): + ok, resultado = Validador.nota("10") + assert ok is True + + def test_nota_acima_de_dez(self): + ok, msg = Validador.nota("10.1") + assert ok is False + + def test_nota_negativa(self): + ok, msg = Validador.nota("-1") + assert ok is False + + def test_nota_texto(self): + ok, msg = Validador.nota("nota") + assert ok is False