From 65c9feb02a9cce7a613ee37a6b03cf800bd9ac51 Mon Sep 17 00:00:00 2001 From: hiurysousa Date: Fri, 22 May 2026 21:29:08 -0300 Subject: [PATCH 1/5] 22/05/2026 --- .../src/__pycache__/__init__.cpython-312.pyc | Bin 154 -> 164 bytes Guia1/src/__pycache__/main.cpython-312.pyc | Bin 1354 -> 1354 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 161 -> 171 bytes .../__pycache__/settings.cpython-312.pyc | Bin 702 -> 712 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 161 -> 171 bytes .../models/__pycache__/record.cpython-312.pyc | Bin 1395 -> 2067 bytes Guia1/src/models/record.py | 23 +++++++++-- .../__pycache__/__init__.cpython-312.pyc | Bin 167 -> 177 bytes .../abstract_repository.cpython-312.pyc | Bin 758 -> 768 bytes .../record_repository.cpython-312.pyc | Bin 1711 -> 2388 bytes Guia1/src/repositories/record_repository.py | 38 +++++++++++++----- .../__pycache__/__init__.cpython-312.pyc | Bin 163 -> 173 bytes .../record_service.cpython-312.pyc | Bin 1228 -> 1238 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 160 -> 170 bytes .../__pycache__/file_loader.cpython-312.pyc | Bin 1137 -> 1138 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 156 -> 166 bytes .../__pycache__/test_runner.cpython-312.pyc | Bin 5184 -> 5178 bytes Guia1/tests/test_runner.py | 4 +- .../src/__pycache__/__init__.cpython-314.pyc | Bin 0 -> 286 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 370 bytes .../__pycache__/desenvolvedor.cpython-314.pyc | Bin 0 -> 1935 bytes .../__pycache__/estagiario.cpython-314.pyc | Bin 0 -> 2009 bytes .../__pycache__/funcionario.cpython-314.pyc | Bin 0 -> 5003 bytes .../__pycache__/gerente.cpython-314.pyc | Bin 0 -> 1822 bytes Guia2/src/folha_pagamento/desenvolvedor.py | 28 +++++++++++-- Guia2/src/folha_pagamento/estagiario.py | 21 ++++++++-- Guia2/src/folha_pagamento/gerente.py | 26 ++++++++++-- .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 168 bytes ...desenvolvedor.cpython-314-pytest-9.0.3.pyc | Bin 0 -> 6235 bytes ...st_estagiario.cpython-314-pytest-9.0.3.pyc | Bin 0 -> 6122 bytes .../test_gerente.cpython-314-pytest-9.0.3.pyc | Bin 0 -> 6215 bytes 31 files changed, 116 insertions(+), 24 deletions(-) create mode 100644 Guia2/src/__pycache__/__init__.cpython-314.pyc create mode 100644 Guia2/src/folha_pagamento/__pycache__/__init__.cpython-314.pyc create mode 100644 Guia2/src/folha_pagamento/__pycache__/desenvolvedor.cpython-314.pyc create mode 100644 Guia2/src/folha_pagamento/__pycache__/estagiario.cpython-314.pyc create mode 100644 Guia2/src/folha_pagamento/__pycache__/funcionario.cpython-314.pyc create mode 100644 Guia2/src/folha_pagamento/__pycache__/gerente.cpython-314.pyc create mode 100644 Guia2/tests/__pycache__/__init__.cpython-314.pyc create mode 100644 Guia2/tests/__pycache__/test_desenvolvedor.cpython-314-pytest-9.0.3.pyc create mode 100644 Guia2/tests/__pycache__/test_estagiario.cpython-314-pytest-9.0.3.pyc create mode 100644 Guia2/tests/__pycache__/test_gerente.cpython-314-pytest-9.0.3.pyc diff --git a/Guia1/src/__pycache__/__init__.cpython-312.pyc b/Guia1/src/__pycache__/__init__.cpython-312.pyc index 508044149c0e1da12c8d749bf240946e280aa495..c2e89147ca55755e8ec030436a94b82b2db5f319 100644 GIT binary patch delta 92 zcmbQmxP+1WG%qg~0}yLNsVzyEzT~>Qfss?R!|@J}-~`29kB`hi-sF4CI$}3OWq?w=k`oy3 Z2#8KdoXP=|;+32%&GL@%*JLhM2LP7eCl&wz diff --git a/Guia1/src/config/__pycache__/__init__.cpython-312.pyc b/Guia1/src/config/__pycache__/__init__.cpython-312.pyc index 48f29792447512d685c5fac36cb55d460f1efd16..88833c4a255ed437a9a5da1f825b008a7b6d6985 100644 GIT binary patch delta 99 zcmZ3;xSEmsG%qg~0}yY)Ws2X+v delta 66 zcmX@Xx{sCnG%qg~0}y-S-yekI Si&6{nCo#=r{55$NlO6!M3>Jt0 diff --git a/Guia1/src/models/__pycache__/__init__.cpython-312.pyc b/Guia1/src/models/__pycache__/__init__.cpython-312.pyc index f2285cc1d7873a863d22504288650b51d1c3561a..3d35f9b1a490d4aae1936006a8ef10a57da10883 100644 GIT binary patch delta 99 zcmZ3;xSEmsG%qg~0}yt<8 diff --git a/Guia1/src/models/__pycache__/record.cpython-312.pyc b/Guia1/src/models/__pycache__/record.cpython-312.pyc index 42aff45eed484b119ac8efadfd58c87324087051..5a010ae68835d3bfe99727a5c1154c468c4e43f6 100644 GIT binary patch literal 2067 zcmbtVO-vg{6rTODz5ZhZGzq`O3Q@pOLX@HAi$wc6jDO1Racg0Q5S5lJG&-$ zWh8_+fYhpuasWgts8XpQ4!uS_rRC5=FDtm9RSt=IsCskbO09D0o87f>6BMcKNPfTb z=6mz@z46b{Xb8ay{CaGvl}G3os#F)~&Z@KkTR|)uMi?z3%iZaF|ti%qje&AM=ZR(x>>bZxQWy0m%U9{yJR%Jy53yv9O4ep<*x| zvi-M}v_`NUBo3EVZ1cHPR1yuubSiW3t0WnkQxF-z-Bn_CJb8{U@tB>4 zn~|_VcRLGnRsRB0Vo7s}oeETahrD_R%un~e6z{8x=OCW@m+}6(c)o<>+Pz+%3GDP$ z^Lsre?5ut>R7Bm#@V+fVMh$C{J&j0&L3MSh)op>fhi17C(LE*}NECVibaXI=l^G?b zD2`vSo6(zJ-c2vc3TFcES#DJ^l)DffW#mM!3=>xd9E`6*P zNKqP8^l3xOOQScg4@vsl(yf~}rT1=Kk`tF4ak!wSE=oE{OIf&9Mwi@v?#mZ#L6+5= zYRIxFy3O8KHP8>Fe*Tan>1 zM=dY(FeJJVz9ym{#rA7wxpZyxz)odnFxx;_K_xT={N*)vakFSz6c1Ol=MjGP2ghwC(h8TNo_*U&cA zBS2hebJ?_nRzj`+2m2Vrzk*GB&}{Iqy;ki6TOIt|@|-1fyb4cWFQ?@0`;3{Ri@uI9 z3!d6r08!oo4+kE;svmOj@LvVSP~4X=LvC`tDjqO}b1_<_Ok{N2>{)Ry$9<1zjz`$P7z zY~2~R#JiSo*C|IL9;T<#lw{cs$uf*yAw%VeEPql+Wn2%T|BE&Uj{|vwNw)jtBV2!q<_>)q@wBVb!&4tw-gg=*z9$lvfg%lKL< z97ts5#Dw0c$u6Ao0AqDBN$PJ P`I9}xOfb(8rB2A-+Rn0m delta 614 zcmZWnJx?1!5Z(3O7bZSioDd&E;3yRoE&x$LL<2%pUN3J7Pc!5hrS-Ago5Sn)rVz>4@u?6D?aX^L3*)gU(gt;(j19lsI;Ae9<7 z^jpziPzF--7|-(yv*mRqL6vta8<8w8O7%GkYvtnVDlH#mEz}2MEZL)P*}j*PEP ztA7Ynx-~d6sm!(?+wp{ z9R4Bk@^5i3etZbh+B5db;GP7T7EglG>u(o*|C z#{$i<$&yCF)kS-FSeW_CcWs90YAs3EQyr?>Y5m2Xo3yxD;xBx94=Xrc$}mbm!%JrS a?7ZI$gPjdI4`cd=?PWb);y)Q-Liz<53W+ZO diff --git a/Guia1/src/models/record.py b/Guia1/src/models/record.py index 5c5bc4c..8f9a1cf 100644 --- a/Guia1/src/models/record.py +++ b/Guia1/src/models/record.py @@ -1,8 +1,22 @@ class Record: def __init__(self, record_id: int, name: str, address: str): - self._id = record_id - self._name = name - self._address = address + try: + id_convertido = int(record_id) + except (ValueError, TypeError): + raise ValueError("ID deve ser um número inteiro válido.") + + if id_convertido < 0: + raise ValueError(f'ID deve ser inteiro E positivo.') + + if not name or name.strip() == '': + raise ValueError(f'Nome não pode ser vazio.') + + if not address or address.strip() == '': + raise ValueError(f'Endereço não pode ser vazio.') + + self._id = id_convertido + self._name = name.strip() + self._address = address.strip() @property def id(self): @@ -15,6 +29,7 @@ def name(self): @property def address(self): return self._address - + + def __repr__(self): return f"Record(id={self._id}, name='{self._name}', address='{self._address}')" \ No newline at end of file diff --git a/Guia1/src/repositories/__pycache__/__init__.cpython-312.pyc b/Guia1/src/repositories/__pycache__/__init__.cpython-312.pyc index 219943d13c57a83f70ca1d7d47146793bb279d63..bfbeb3468b59be3a011b39dec465c57a210cdf15 100644 GIT binary patch delta 74 zcmZ3^xRH_jG%qg~0}yO`MTTQAz#DkiizwWv5IBeS%qGR7sfIJ+djAjZ?p*)^uv dBqqS$KgKb@DBjNq#N4dKIGd62*5qSM1_1C88X5oq delta 67 zcmZo*`^L(BnwOW00SGn*uutUvtrF;L6%$&VT2vfUmY7qL8sm~$oL!P%5EJ0<55n<9 TsRj9)O&Di0GX9!;fyn>>()t$W diff --git a/Guia1/src/repositories/__pycache__/record_repository.cpython-312.pyc b/Guia1/src/repositories/__pycache__/record_repository.cpython-312.pyc index f76d38a1756ade4d7422d23b0b37569b6c91650a..47e921e1ba175b3efee3235f87a21220136168d9 100644 GIT binary patch delta 1409 zcmZ9M-%s0C6vyxN4-x_)4#a~5SOZDPveI?~LGWAp-bFGGIdhbkLCzlKmai?DlSfUUrNjdIh@C(SIA|RG6mKj!|Jzz3CPOxpREQ)i-QJG_F*(rf4cw>OF)se_Sg9)UA zR>C7BA=8>rBsnsz&J%hzgnkghfiugbR4SU3P20GXn2Qb&q7ai$kyug-a)j>O~H0K@7s~zhX*De+u?OCo^=gD$Q z_9r#9+5Qs4+ub|fmR0rJjeM5l zy<|bIx{`nd*O%$f(uTF+pZ#*=di5D%Kt~Xbx*k~GvALMpxb}O4F+O2TzMmhLjIpc6 z)$0bCGv+gU2&uT2DZ!p`9}RuX01dT+%sxWSx0%O6U5N*||5PXJ;m0?7TbW;51AVXX zTOtN;a$1zkPBU=t*yz>|z|?@E6{O=h&h4=tCeA{rcQQJoGBbeB45ARmxtx=8S*Jb$ zRymymCf_)m^gtE2=B{(+j_S+}5Ki;j3u1NtE;_+S#%W*+sE1QbTXc)g&tnxYS!E_& zhEWm&$7N7!Ivckizv@ZUxf7+OPhplFML~A>y}_Wx425_d0ewIiANR><*usfH~08rJ$#B`;kgfveiH6ot?V^&TpNi8}X(G)sn zIi9hC8083zr7-Pr{!AV8)bGLFLPgQNGI@J47v2^Fc`;BBgIU{O4tKFe%nm&jYqPee zPI0C2PGh#W=<~04u63@5)#m{%ma(B&43Y7I5F8gFCRZExyHB!8evX zF5dH1`#yp|9Jzn_)ZiI9OxF{7aHcghtH^VSXb(9LCH+&WRIbEgjCWE0F7of9)_pF3 NJw|QtZv^pR@&C!Wa5Vq` delta 778 zcmYjP&rcIU6rP!#ZMO?83vE&h<%fc36Mv>&6oP^z#+U%naI4ug-3NHj`M{0CmVK-7e6Jn#p2XeyH6$v4|zc*(r?y>Gvno%ij2Z}}XJeT_y{ z1o3k$JoSrIViz#pQfXy;Idj`(EK@aar`+VsPD!(gUmWkO7x;VZ>b-p~t{!y2Gv zRB+PdjLjMWWh53A1U2%umiHxAnzO_xU0q!)#@p#doa6UsYNi@Or_zVAlv4M#_<@9? z@mJYZYyDdJDIeI#Za8oIwxq4QmDrEQn>7hki*z|!Rgjig@s_+DC4E$j0Bea0z3Uz9 zpgy%tHeDdlA4dp4;NlMAx8yS+55weyugZzUe>3}qnJ#w86y=}fONxW6v}pN9`O19< zG2#+1aiDJENsE}UFhhO0Sei3gFgl9^#S((Ao4{HD`t9d(BNo{vlI=@{V+RY5r zz_u8;qp4LSMbx7j0%(b`-wfixZaR_)`--lMkErXus_WAw<4KWUSCYvl5HazyuntI@ zh-m`H_=M8i9Fp6snk=j9*#iWHY5cV^+uAJzbrqn$N`G*eIl#{G^O3X`$LdZp^9Ml< GRQ~|jNxPu{ diff --git a/Guia1/src/repositories/record_repository.py b/Guia1/src/repositories/record_repository.py index bded279..afaba5b 100644 --- a/Guia1/src/repositories/record_repository.py +++ b/Guia1/src/repositories/record_repository.py @@ -10,15 +10,35 @@ def __init__(self, file_path: str): def load_all(self): data = FileLoader.load_csv(self._file_path) - self._records = [ - Record(int(row["id"]), row["name"], row["address"]) - for row in data - ] + self._records = [] + for row in data: + try: + novo_registro = Record(row["id"], row["name"], row["address"]) + self._records.append(novo_registro) + except ValueError: + print(f"Registro inválido ignorado: {{'id': '{row['id']}', 'name': '{row['name']}', 'address': '{row['address']}'}}") + continue + return self._records def search(self, term: str): - term = term.lower() - return [ - r for r in self._records - if term in r.name.lower() or term in r.address.lower() - ] \ No newline at end of file + term_lista = term.lower().split() + + if not term_lista: + return [] + + resultados = [] + + for r in self._records: + palavras_do_registro = r.name.lower().split() + r.address.lower().split() + print(f'{palavras_do_registro}') + contem_todos = True + for palavra in term_lista: + if palavra not in palavras_do_registro: + contem_todos = False + break + + if contem_todos: + resultados.append(r) + + return resultados \ No newline at end of file diff --git a/Guia1/src/services/__pycache__/__init__.cpython-312.pyc b/Guia1/src/services/__pycache__/__init__.cpython-312.pyc index 7156b3400d03c95edf785d73c6374451cf8d4037..123b4f61af38d737cb13f9b60b707980c53fc4c6 100644 GIT binary patch delta 131 zcmZ3?xR#OoG%qg~0}yM6@iPA!g!kI&4@EQycTE2zB1VUwGmQks)$ XSHudmf)R*|L5z>gjEsy$%s>_Z8)qlY delta 64 zcmZ3>xR{aqG%qg~0}y-+W1q-vtm5Qs6%$&VT2vfUmY7qL8sm~$oL!P%5EJ0<55n<9 QsRj8HbL1I+P3%zw0Bv~{X8-^I diff --git a/Guia1/src/services/__pycache__/record_service.cpython-312.pyc b/Guia1/src/services/__pycache__/record_service.cpython-312.pyc index 29288bfcc275c671ade748d6ba8a812eb45dfc94..88163aafea21ab93f13773ff9e55bfff960ffbc6 100644 GIT binary patch delta 78 zcmX@Zd5x3%G%qg~0}ycx&=J7Civi6&XYT delta 68 zcmcb{d4`kwG%qg~0}#9pVBg5C!KmWlY!wq)oLW>IQyUl0@E?+?Q9 UMX3e(n-dw`m>GXfUcsUV0LI&j){-Y%*!l^kJl@xyfv|25dbP8B02y7 delta 64 zcmZ3*xPX!SG%qg~0}y-)W1q-vtYYtM6%$&VT2vfUmY7qL8sm~$oL!P%5EJ0<55n<9 QsRj8Hb7UBQP3%+z0Be>NSpWb4 diff --git a/Guia1/src/utils/__pycache__/file_loader.cpython-312.pyc b/Guia1/src/utils/__pycache__/file_loader.cpython-312.pyc index 6b5ca6a21d9cb8e3b88c17bad2a4a743ae0d4df1..73497f34d12be7f0278c50c10b7b2232513fcb5b 100644 GIT binary patch delta 140 zcmey!@ri@`G%qg~0}yIQyUl0@E?+?Q9 zMX3e(lh-pjJ2e9}H86Z&Vdu2FBPco{b3yV3_bY-n9~hW;RSvM+k(8M)H&bo}%VkO3 j32b-7rDwQJuKCUil=}`+@Cih0)@GJqWc)SRg+&hlRF5o9 diff --git a/Guia1/tests/__pycache__/__init__.cpython-312.pyc b/Guia1/tests/__pycache__/__init__.cpython-312.pyc index d4029fa49e430451318e97857bdcbdee513b4004..36a45629da1b59331351c64437505d6316591d78 100644 GIT binary patch delta 94 zcmbQkxQvneG%qg~0}y9*J0BL6xNB{r; diff --git a/Guia1/tests/__pycache__/test_runner.cpython-312.pyc b/Guia1/tests/__pycache__/test_runner.cpython-312.pyc index 5cc7ad8300e869efaa348ebcb42884e54da251ca..683638b17c9723a1f879fb602c30425c7315592d 100644 GIT binary patch delta 366 zcmX@0u}g#ZG%qg~0}x1k5y<+rk#`#td3K}>ct7q>SP zXDK(({H*-Lizi3%8t~j=R48SI3T&Rl%fZNaWb$l2Ll)i3ViuDx@JWI7f8tYCky@Z~ zQCMw->V}ewTGl&?PpIswzat`fLCXG$h{H`0sSlhW3oQBDK`uThXe!7HH1tOU!-B-+ zX$#XX zuwE5V`^d~7W%-?%flv7Z2ZMmbb$-Q*{ECUf@-lEGE3n*cxb05gUjQ k1`&oJLKZ}rfCvc?AqFIFaoFVMrXE7Qj4=o z@(W@D{QW^Vz9_XIe{(&vEjwe=6^urOphbH}i9QGqF~2W#uPM zPUbb>_{FGjiv`HpJfD|?k@4i@6?}#)I+w-FC*S3hf@o#|YW~HitRlHU@`9B86%mJ=l+9}Nr(5|^hfOj{AM zIH!SOGQUs*qt)aILWOPvl>~dR0K}BQsFwJ2Oz_2Mz`SiR=7|7x@)0 z^DAHAQ2x#d5(ODDSwMK1u?^6^A~p~q3?hs`ge-_K1rZV;LJUa!;;_lhPbtkwwJY+N I{8Bg&0D;zd;Q#;t diff --git a/Guia1/tests/test_runner.py b/Guia1/tests/test_runner.py index f8004a8..acbadee 100644 --- a/Guia1/tests/test_runner.py +++ b/Guia1/tests/test_runner.py @@ -53,7 +53,7 @@ def test_search_multiple_terms(self): try: self.service.get_all_records() - results = self.service.search("joao rua a") + results = self.service.search("joão rua a") if len(results) == 0: print("FALHA: Nenhum resultado encontrado") @@ -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/Guia2/src/__pycache__/__init__.cpython-314.pyc b/Guia2/src/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..822db3fe356ae1b8ca85a44fe1506bd276fa149a GIT binary patch literal 286 zcmdPqoiA9IlaX0kR2c&_IlCmkAjZ?p*)^uvBqqS$KgKb@DBjNq%yci!Of-rqE=rDxkI&4@ tEQycTE2zB1VUwGmQks)$SHul8lM#rEL5vT~jEs!;8N9DDco%U3`2a{cOhW(w literal 0 HcmV?d00001 diff --git a/Guia2/src/folha_pagamento/__pycache__/__init__.cpython-314.pyc b/Guia2/src/folha_pagamento/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2c675478c86f0c24b88b10bb80fe9206e842aa83 GIT binary patch literal 370 zcmdPq@OqG}&G<0yS$g-r{yE%}dVA&r2-I%m;G0T#HK*(=(ww zUYFG3)V#9%oU+uE{34(3puO28`2{haZqBYT#U?QU{{AtJ z0Y>qDMqs9UX=b8POmR_iOj>?UMq+$HVtQgO&|Ud4@$s2?nI-Y@dIgoYIBatBQ%ZAE l?TYw-c7Xg_EC?h%Ff%eT-epj^%b;|ZLE}DyOc4)I6ads!W|sf} literal 0 HcmV?d00001 diff --git a/Guia2/src/folha_pagamento/__pycache__/desenvolvedor.cpython-314.pyc b/Guia2/src/folha_pagamento/__pycache__/desenvolvedor.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7aba32beb419f61b688b720c8e8deceb95f4188d GIT binary patch literal 1935 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 literal 0 HcmV?d00001 diff --git a/Guia2/src/folha_pagamento/__pycache__/funcionario.cpython-314.pyc b/Guia2/src/folha_pagamento/__pycache__/funcionario.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ed28de1af342255bbca10bbabbd450999c2e0086 GIT binary patch literal 5003 zcmcgw&2JmW6`$pj6iF>VM2VKfTB1H|M@$^aRg~5(jMT2>#O{Zp(zSdDt;K4&lD1v# z%CpM=_9Z$LC;|kieQBg#3ZuwDhyD?T{{ay!N?}kSE_w)ZqoFzqdg^<#P5ht`PI^ro}ktOnmP!a}-5yNj887F)v!et_tg;6%w8qtZibP=wLcIbLq zx*o2Jb?9Pz#5mU^P~*h8q?b!1E=h1nl1oxt(tAhZvNV@vQ094(Z0Y*AF596?wRHVl zH_)L=mvT;Ki8y_wQ-I?j>wUXws=8$=RJT-sh~N)P6n+RE56&fF3xHYyAC1n;r1X>DjMzM(i&SF47?BwI1?GxCaJYZyBS8jGwN zmg2xubk(9@%aXFJ8j5XKbk&h%8;6?MVoO(lT)t&%)Gn{Y4v7?b^(j_6H=Ee(y3OArTzCLhM8y}{1yO`w?BGw(0UU)_ zTADQpku0|nCnYl1>aPn+q32o&0V{EtT+BjDx1qySzl0b*{woI2(=?B^#xO7D*p{}Id6ei*1@X&{6M-N6`CU$r!5gh{d+ zqLB9pi&D+0Qgc_J5GzPsGzASs#9JKaGoyVdg@}?J zM^}!SkH!Zdey|rC^}YD!Xh1xY=+cSA7)56T#F#)JCU!L5_@00jFE5D45WI8*4_twr z$uYQOF_t#6nG>Xu9WK_pS;X&kl^Hz9guC(_i>L}+vx<>! zdAzkPLdt{WXTokK%iVEe$%<03Y}{XU)w0=ekii`)j@CtR2tp=hAIyJzfbs z&!an|`%->;zH$2X+UeK#rTz!M{&@M}awB)XmOI~!M>2^fiNq5=qTf8YbP60eIkM-$ z>S$zl&ps!zn3#EFxB2vrk1I&guB*(op7D$A0+#J#YFh;Db1ZuTCatb#$&bOVaFV#$ zC(O4tmQoc1_f{&eSZ38`{obtZ24s!BNIO68<62roB83{caU@adsxi zgy>ps#_8NQz8D8mUPm`>AXrHC{`N*He?vL?L9*O<{dq2bqGY$O_R$V(S%dTk@h>wJZ3*5SPx2mzx zAX$&V>zOeR0fr@&9Yio5wj0K-D_@C2@04K7tztG)Ecj?eVzvAfsCuX7>o- zJSZ$5MA%CQJn&axgagcYTjXCc$^X=Mp60K^H;w#qpEA^-?B{OOF+f4Yj|xf8H_cqUauo@q$3{D7a9}@)gYzvb(@;nIA!su=Degr#M9Rj0ON=%!{`a4NwAJBw{nYg41ny+D4zghyww;@`_c;gmez=kAs;oHh=fO(V(QNo^U*MksRWl4(hVwhWKp{CVyZ0wt0kr+!h5ZRU zeffLf@?i)r>io9eoFLKEVF>P02O{*E*n>UHl@&Z$!IMpRvWY_p&Xi3&SRQ8l9_%$u z+;EzsBszd2C-IE|965=joFMxG?f63De&dZ@fkC;T%(NIsbSf?&%>{~twFnN|P* literal 0 HcmV?d00001 diff --git a/Guia2/src/folha_pagamento/__pycache__/gerente.cpython-314.pyc b/Guia2/src/folha_pagamento/__pycache__/gerente.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d8dcb0175d380dacf7a20e1380d99261db3be6b GIT binary patch literal 1822 zcmah~%~Kpz5bw9Mvn&e>3oL9Tm=zH!V+aRGm8m40pD2~Xe&S}AkuQAprKGPb3u6&|TZuK_ zRVKAmRAbhL`yLY3f*&q(IK&Fb!#2xwWp6M#>w5tYdy>)|*2`Yu7P1=DTiWkcRw}FJ zW=pwVJh5FI+$at{P_~L^)?0_``L<7sUz9&9Kgti?e{(xObg(16RUBS#JzLLr+@mXW zm2Kwx4~+5g-xO$jnPG|#*ICvqs&iuy9G^iQ4(CTB3u~u7_~o6oZz>vz;InRt;u-Lr z`yl6RZu!`%JnTJ^fPGtsJl=&2lTavb7y35}{r4xf3a8hz+J6MCM<%-|b z@cb5*&C%GE8o;TC;~@Q1zFlV>U##;pnw&jEhDfFM2-2>7q~@Fx4Mm5CCyziqFyeCo zJ`hEo6kNxGf8SL~3b?$Pe+idQx_Tn)NuSA1(OTqy^6>TJfXY$2=+6bNlv^oX4oo+; zv{O3oFfLrjvpt{EXgIJQ8UG8Cy@)LA#-Suk;aJoZILBrKVdI<%JK~XrXoO|EE~@ge zstGm*-GQ%QSUSPbRve?1AYqG-ydRae@`ju;7%^1p4}-B_0>N!8MU+n z$ReE4fk{mXPyTDljm#hXZkY5K?R*H0Q_*9~UTS3Gb*7 float: + return (self.salario_base * 0.03) + + def calcular_descontos(self) -> float: + return (self.salario_base * 0.02) + + def calcular_adicionais(self) -> float: + if self.carga_horaria <= 20: + return 150 + elif self.carga_horaria <= 30: + return 250 + else: + return 350 \ No newline at end of file diff --git a/Guia2/src/folha_pagamento/gerente.py b/Guia2/src/folha_pagamento/gerente.py index 31819a1..7f4bce9 100644 --- a/Guia2/src/folha_pagamento/gerente.py +++ b/Guia2/src/folha_pagamento/gerente.py @@ -1,6 +1,26 @@ from folha_pagamento.funcionario import Funcionario -# Desenvolva a classe Gerente aqui. +class Gerente(Funcionario): + def __init__(self, nome, matricula, salario_base, setor, qtd_equipe): + super().__init__(nome, matricula, salario_base) + self.setor = setor + self.qtd_equipe = qtd_equipe -class Gerente: - pass \ No newline at end of file + def calcular_bonus(self): + if self.qtd_equipe <= 5: + return (self.salario_base * 0.1) + elif 5 < 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 \ No newline at end of file diff --git a/Guia2/tests/__pycache__/__init__.cpython-314.pyc b/Guia2/tests/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4de7bd1bd3e620a808f4df0932a5de24747d0908 GIT binary patch literal 168 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 BR;>U4 literal 0 HcmV?d00001 diff --git a/Guia2/tests/__pycache__/test_estagiario.cpython-314-pytest-9.0.3.pyc b/Guia2/tests/__pycache__/test_estagiario.cpython-314-pytest-9.0.3.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a287e6691195acea5befad252938101d38fd756d GIT binary patch literal 6122 zcmeHL&2JmW6`x%$m(;Gloy77-B`S7QQFbU%qGUU^63Mn2x{z&kfzUv~da7fOLUKA@QpZebH?rF!bpT+jZ|`@>k6kQ^HR1vb0({tQKp+$NfEjo1P!#dN4D4w1BE zhlku2*_NS2ycS7E?dTBsfxOi3#Rm#4bd;n#+g3E`D6v&746+maj|Ut*b?I9N>ohq* zw2qrXN_5+_NFZmA7KLoRnsAE5l^t}r@lbG-1YET-}c-Vgu*-_CrMWF?i+rgZ;1QK|HI=GdEs+N{&S4WX9Mp&jeUYd?)l%+{#+CcA+1c1AMxO)MG51d3>t;-X z=Ea|fc*}s`qN(38th{nR9#@?1)|5_X(8W>aD(eU?xj9b8K~%~xp7T&);1?{q@5 zey3V2*~PNu$R!gzEN4KsR?KQ?nYrqPl6l7{>1AUz?{w&vaW{_{obGuxFh#SxK&eTc zBl=yFt{S$U|EN+j%7zUs4?Eo9Rq;0NKqj4lF%>jsg2n~6YhFxC(6rm+-Yq%VVx=s^ zzmb@CQ3MgEhYbK5aQ$xaBa@CnErO}l08t~KtFLdXryJ_&x|FV~r#ICRP&UxoPNo~l zG*&0mpN=tQEZtDk+t9>*TNp9IO|Jav9j-o+(!TYS`B=Idw zl5F;XySNHCezOF?tTwN2mVf|&zRD%?|7i*M__aU1|F?U;d5?C%i^4F9;;K%OEYV&R zxcpFDZ|D&euYtHhUk3@8|KKGte>rdE%(89%S8%_d<33MI2e7NhP#~+OCs3S3frOe4 zq8LIELop1Z9bEP^xb%~{i1{}7;;;k}bF`t3)}_U|I=ZQjfwHj-VtabAF};Y@(~F-b zn37m*sEgas#C}^CF~&`;ZRi z6>wyTc`ueTXu;4|Op2WTtg&3o!4d{5U*4+Zsj+P4kVg9#t{;q6*MKP3$gSS8-+~mo z9eJK=($}@C8Icj^4}4Mz#2H?$7Frr0Nq%Y|&PpZ>#96&{`D;U*n*{g8#F>*wEv(6} zV6(u9xj!$5+$``o*euB7QVW%pHK4c|c^|=zdSOzq_zV{Z6|#L-jQ}_ zhFo^W=|A{3Kkp7OTcDN<@i@e3F9~t}eTo5$!&EbI3%F`m%O~a>;SkmgJ1l&~qb_Vu z;&V@+coW4b6gYVKm$4hF^K-wA3)T| zZa;~=y(OLe45?$hp~madd|i!is%KCcpKqx1+miR&!lpCa*cJsTqlF z`;%WA^x|LsA-4TZqM=ihf&?Dwm3D)#9?*+>Ud|qKGff00*jl z5r;;rh>KQuZnY1nD4vYcX%JQfgtvEfacO|z(!P7@QxhxnBDf;pS$_mkBcBZo*S-sQ z^m)&zTAG1UqM;`0(xtkZ*iW@! zWvuUWqW<{>V z0*Kv^AP9dYwPI@C#8C=7qfw5qo!;`b-gpYXZva!mff|Cmx*G n76ux^K>gUoXC(5jxJM#l=!;%axGe1Tfx64oFBM6cWgYn!igGb_ literal 0 HcmV?d00001 diff --git a/Guia2/tests/__pycache__/test_gerente.cpython-314-pytest-9.0.3.pyc b/Guia2/tests/__pycache__/test_gerente.cpython-314-pytest-9.0.3.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1271208a11e9db8e7292c1cbb544c88df0c280d9 GIT binary patch literal 6215 zcmds*OKjW78Gwfr^|0u7+KrQJVmpZ)tM>XKD}E$vC)vcW!zL=bkuPcl*=8JdWj%%z z8m9=Lpszjl*zPHZ9*cEqPlxPBBfDb5;3%&%2D_*TF z7jJco0aCo1@J-AUQoMi3F`SWBzfZ`!BpKkI@er5Xb2ki?L||0X)Wsi-$}#6E_(qa! zu%l(X(Y5E2d*w-JT0L8HrDksDp1Vnc#GJRz?{Nj5@V+~2b?v$2_4|;G6i<)p+bjG! znsTJAU)cJ!z_-w6ACLcreHMPu=fIFMAl@R3hoBbJLW$szw68UkJi(utr`O-(ZSEPK zXo%kxKR=MPhO1IRxhm0w)_~Lb8+mHV@)g#H*?d4LibV{IF)O4SFyPZG`EQ!j2FyRR ztEaE9J`picJpmM;O>n`8^youuk422|&m5s^ikU`v69`VzaKso~PG^@(*)+B1Q223Q zB>cE*%61ux7!lP9yK~NJG}2(3$)}dmhHf?QcecXU3+p)5Q)z0or9hcYQN2Ky^J|4P z)vZQQr1AA~3scp5^(t#rIEfsIUetsj~%BZ(;*{;}E z8245t4Wv+SZ*LbnD9%{xEbB5uP^yrpDKov6&OOLGn4&lgkw(U!peU2wc5?dSrt;3q zmd@?-W7YFxU$l&s=U=Mr+iG`J?XD<86}5Xqy#T_~hgEfGTXBAysJS4jLil7{h%b~O zUfW`EY)xHHFnIdV>*=Y9W)Q%Rp?6uVAJB~!_%pi7P?mu+acHPLift~l3bICu!}YjZ zyqQqE>@8{u{{gPvVAK*sj(g@NzbbH5Wn67s?5W|Zpm1CemU|K6-lK=B?v?72_)xC8 zen;@w;p$QL5~l@l^(b3e*!qvO6|B<&u9bUKb_wI>Y zClNe^*J0qb1b-R+0r>lX-7{Jk*xj`H`MF>I=ZByF8oZ*vGI3B~mi2|Z46U9gnG z`pEF377xKkMgig2JbIf#`hWy~@|FZ&(T(MN&deJw#rrVSH~{|#Yz-Iz;@j&2O3o?( zYsjD%>*2?dNZ4u@Y!_iEre_orV0s#hGg!Qf1!NVD>7o<<=K-mFoDqD;O-#P(Fx zo{BPCQF}JjOCUUpL9rd3twv|DIXe4AKNI?AtLp4FsMv24JuZo=5D>PG$qQwc*S1*v zgPsuOSxh+C0@yxx149sA!yr-j4r<)M(9Va?x@+@5dL^?A%OI?tJXNTl5tk2S#K7-5 z8F6vcWn=MHLS|IH%d;wx?Ydm5PvvmJ&$8VQzB5#I>3KAj1)(&|cK`RJ71ZbN#mVs9 zw^Dy45`J~E-2lsW12x%hFnN4>2KidV&rY^$6O(1TEM2WfONy7V+7FIqirF^9s34{G zmZaptGagi8C`@?6b^>`q@Xs|qHv2+uCQ9*(0u>ZwbfZ1k)(eG!BZB9XHp;r~3|dU) zIw|ECI3aC~KS1G0DUoo!RkgRGOjp$24Yd!2f$6F`y{$OEO;q)Xst^!fj>!vUn%A~i zAX`({6AcF3=-}$DijW|H<-t5s?9zY5m~=0cAUWugeQ4o=bO@Qwd&o4JkQkY6@fmPz zGN}m#$kezv@|_`*nAOL~7hQ^dJ7f~MD}OJ?B$M!~Lnc2XlfQ;cfj1yifD?(I9Wp^q zM`LUs-n4^r@=!9deWV9PnU0HK?PP9?Z^JU(>@i;b_f;O2j z={+1uLD1IO;?`W#+2E!o=oPT?1*_E**ZiZWSI`#040 zKzQC!RVTL<=eLQP_e50)NFm4Mg)+%&TP%*Psp|;_&pW)Ho|M$zqnRi}88IkD} zYGzO)(`jt~H(V3s>)1YPx5GY{>{Q_KgD=}Y%wc<*72;n_Sk7&oO4;982JM^~lDBp7|A8O#!i%Bz&boS+f zWziHD9QKwen;ZBM#9pjI@2}-oN?CoHYS4z=H9mo2*C$ERKMDB-`FYbn$jRLRkwd>e z_vGByvMi15`H0-Qi_&fw+s`~Xvn_R0rH;y}u~#HGDesY>?EAXaCk^klfwYU#t43Lx alMXmcfWuYpAWPRA2asNe73m6lzyAfJMn81` literal 0 HcmV?d00001 From a750038c1b8ff986fdd267c95eb187951f451c26 Mon Sep 17 00:00:00 2001 From: hiurysousa Date: Thu, 28 May 2026 20:11:53 -0300 Subject: [PATCH 2/5] =?UTF-8?q?Solu=C3=A7=C3=A3o=20do=20Guia3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Guia3/.gitignore | 13 + Guia3/README.md | 252 ++++++++++++++++++++ Guia3/main.py | 8 + Guia3/pytest.ini | 3 + Guia3/requirements.txt | 2 + Guia3/src/__init__.py | 9 + Guia3/src/alternativa.py | 9 + Guia3/src/pergunta.py | 19 ++ Guia3/src/perguntadiscursiva.py | 13 + Guia3/src/perguntamultiplaescolha.py | 16 ++ Guia3/src/questionario.py | 13 + Guia3/src/resposta.py | 12 + Guia3/src/respostadiscursiva.py | 6 + Guia3/src/respostaobjetiva.py | 8 + Guia3/src/tentativaquestionario.py | 21 ++ Guia3/tests/__init__.py | 0 Guia3/tests/test_alternativa.py | 21 ++ Guia3/tests/test_pergunta.py | 8 + Guia3/tests/test_perguntadiscursiva.py | 39 +++ Guia3/tests/test_perguntamultiplaescolha.py | 51 ++++ Guia3/tests/test_questionario.py | 27 +++ Guia3/tests/test_resposta.py | 8 + Guia3/tests/test_respostadiscursiva.py | 42 ++++ Guia3/tests/test_respostaobjetiva.py | 59 +++++ Guia3/tests/test_tentativaquestionario.py | 90 +++++++ 25 files changed, 749 insertions(+) create mode 100644 Guia3/.gitignore create mode 100644 Guia3/README.md create mode 100644 Guia3/main.py create mode 100644 Guia3/pytest.ini create mode 100644 Guia3/requirements.txt create mode 100644 Guia3/src/__init__.py create mode 100644 Guia3/src/alternativa.py create mode 100644 Guia3/src/pergunta.py create mode 100644 Guia3/src/perguntadiscursiva.py create mode 100644 Guia3/src/perguntamultiplaescolha.py create mode 100644 Guia3/src/questionario.py create mode 100644 Guia3/src/resposta.py create mode 100644 Guia3/src/respostadiscursiva.py create mode 100644 Guia3/src/respostaobjetiva.py create mode 100644 Guia3/src/tentativaquestionario.py create mode 100644 Guia3/tests/__init__.py create mode 100644 Guia3/tests/test_alternativa.py create mode 100644 Guia3/tests/test_pergunta.py create mode 100644 Guia3/tests/test_perguntadiscursiva.py create mode 100644 Guia3/tests/test_perguntamultiplaescolha.py create mode 100644 Guia3/tests/test_questionario.py create mode 100644 Guia3/tests/test_resposta.py create mode 100644 Guia3/tests/test_respostadiscursiva.py create mode 100644 Guia3/tests/test_respostaobjetiva.py create mode 100644 Guia3/tests/test_tentativaquestionario.py diff --git a/Guia3/.gitignore b/Guia3/.gitignore new file mode 100644 index 0000000..4b3bf0c --- /dev/null +++ b/Guia3/.gitignore @@ -0,0 +1,13 @@ +__pycache__/ +*.pyc +*.pyo +*.pyd +venv/ +.venv/ +.venv +env/ +.env +.pytest_cache/ +.pytest_cache +.coverage +htmlcov/ diff --git a/Guia3/README.md b/Guia3/README.md new file mode 100644 index 0000000..c6877eb --- /dev/null +++ b/Guia3/README.md @@ -0,0 +1,252 @@ +# Guia 3 — Sistema de Quiz + +## Contexto + +Você faz parte da equipe responsável por desenvolver um **Sistema de Quiz** educativo. + +O sistema deve permitir a criação de quizzes (questionários) com perguntas de múltipla escolha ou discursivas, validação de respostas e cálculo de pontuação. + +O projeto já possui a estrutura de pastas definidas. Sua missão é **implementar/completar** as classes seguindo o **diagrama UML** e as regras abaixo, passando em todos os testes. + +--- + +## Diagrama UML + +```mermaid +classDiagram + direction TB + + class Pergunta { + <> + -String texto + -String? explicacao_geral //Pode ser None + +validar_resposta(resposta) boolean + +get_explicacao() String + +get_tipo() String + } + + class PerguntaMultiplaEscolha { + -List~Alternativa~ alternativas + +validar_resposta(int indice) boolean + +get_alternativa_correta() Alternativa + } + + class PerguntaDiscursiva { + -String? resposta_esperada //Pode ser None + +validar_resposta(String texto) boolean + } + + class Alternativa { + +String texto + +boolean correta + +String? explicacao //Pode ser None + } + + class Resposta { + <> + -Pergunta pergunta + -boolean esta_correta + -float pontuacao_obtida + +calcular_pontuacao() float + } + + class RespostaObjetiva { + -int indice_escolhido + -Alternativa? alternativa_selecionada //Pode ser None + } + + class RespostaDiscursiva { + -String texto_resposta + } + + class Questionario { + -String titulo + -List~Pergunta~ perguntas + +adicionar_pergunta(Pergunta p) + +criar_attempt(String usuario) QuizAttempt + } + + class TentativaQuestionario { + -Questionario questionario + -String usuario + -DateTime? data_inicio //Pode ser None + -DateTime? data_fim //Pode ser None + -List~Resposta~ respostas + +registrar_resposta(int indice_pergunta, Object valor) + +finalizar() Tuple~float, String~ + +calcular_pontuacao() float + +is_finalizado() boolean + } + + %% Relacionamentos + Pergunta <|-- PerguntaMultiplaEscolha + Pergunta <|-- PerguntaDiscursiva + Resposta <|-- RespostaObjetiva + Resposta <|-- RespostaDiscursiva + + PerguntaMultiplaEscolha "1" *-- "2..*" Alternativa + Questionario "1" *-- "0..*" Pergunta + Questionario "1" --> "0..*" TentativaQuestionario + TentativaQuestionario "1" *-- "0..*" Resposta + Resposta "1" --> "1" Pergunta +``` + +--- + +## Descrição das Classes + +### Pergunta (Classe Abstrata) + +Classe base para todas as perguntas do sistema. + +Atributos: +- texto: String — Enunciado da pergunta. +- explicacao_geral: String? (opcional) — Texto explicativo mostrado após a correção. + +Métodos: +- validar_resposta(resposta) → boolean — Valida a resposta (implementado nas subclasses). +- get_explicacao() → String — Retorna a explicação geral. +- get_tipo() → String — Retorna o tipo ("multipla_escolha" ou "discursiva"). + +### PerguntaMultiplaEscolha + +Herda de Pergunta. Perguntas com alternativas. + +Atributos: +- alternativas: List[Alternativa] — Lista de alternativas. + +Métodos: +- validar_resposta(int indice) → boolean — Valida o índice escolhido. +- get_alternativa_correta() → Alternativa — Retorna a alternativa correta. + +### PerguntaDiscursiva + +Herda de Pergunta. Perguntas com resposta em texto livre. + +Atributos: +- resposta_esperada: String? (opcional) — Resposta considerada correta. +- case_sensitive: boolean — Diferencia maiúsculas/minúsculas. + +Métodos: +- validar_resposta(String texto) → boolean — Compara texto do usuário. + +### Alternativa + +Representa uma opção em perguntas de múltipla escolha. + +Atributos: +- texto: String — Texto da alternativa. +- correta: boolean — Indica se é correta. +- explicacao: String? (opcional) — Explicação da alternativa. + +### Resposta (Classe Abstrata) + +Classe base para respostas dadas pelo usuário. + +Atributos: +- pergunta: Pergunta — Pergunta respondida. +- esta_correta: boolean — Se a resposta está correta. +- pontuacao_obtida: float — Pontuação obtida. + +Métodos: +- calcular_pontuacao() → float — Calcula pontuação. + +### RespostaObjetiva + +Herda de Resposta. Para perguntas de múltipla escolha. + +Atributos: +- indice_escolhido: int — Índice escolhido. +- alternativa_selecionada: Alternativa? (opcional) + +### RespostaDiscursiva + +Herda de Resposta. Para perguntas discursivas. + +Atributos: +- texto_resposta: String — Texto digitado pelo usuário. + +### Quiz + +Modelo/template do quiz (criado uma vez). + +Atributos: +- titulo: String — Título do quiz. +- perguntas: List[Pergunta] — Lista de perguntas. + +Métodos: +- adicionar_pergunta(Pergunta p) +- criar_attempt(String usuario) → QuizAttempt + +### QuizAttempt + +Representa uma tentativa de responder o quiz. + +Atributos: +- quiz: Quiz — Quiz original. +- usuario: String — Usuário. +- data_inicio: DateTime? +- data_fim: DateTime? +- respostas: List[Resposta] + +Métodos: +- registrar_resposta(int indice_pergunta, Object valor) +- finalizar() → (float, String) +- calcular_pontuacao() → float +- is_finalizado() → boolean + +--- + +## Como prepara o ambiente e rodar os testes? + +#### 1. Criar ambiente virutal + +Na pasta do projeto ..\Guia3> executar o comando: + +```bash +python -m venv .venv +``` + +#### 2. Ativar Ambiente Virtual + +Isso garante que qualquer modificação precise ser feita, seja realizada em um Ambiente Virtual controlado e não produza conflitos entre pacotes de outros projetos. + +PowerShell do Windows: +```bash +.\.venv\Scripts\activate +``` +macOS / Linux: +```bash +source .venv/bin/activate +``` +#### 3. Instalar dependências + +Na pasta do projeto ..\Guia3> executar o comando: + +```bash +pip install -r requirements.txt +``` + +#### Rodar exemplo + +Na pasta do projeto ..\Guia3> executar o comando: + +```bash +python main.py +``` + +#### Rodar testes + +Na pasta do projeto ..\Guia3> executar o comando: + +bash +``` +pytest -v +``` + +ou + +bash +``` +python -m pytest -v +``` \ No newline at end of file diff --git a/Guia3/main.py b/Guia3/main.py new file mode 100644 index 0000000..e9e667d --- /dev/null +++ b/Guia3/main.py @@ -0,0 +1,8 @@ +from Guia3.src import * + +def main(): + pass + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/Guia3/pytest.ini b/Guia3/pytest.ini new file mode 100644 index 0000000..97a0030 --- /dev/null +++ b/Guia3/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +pythonpath = src +testpaths = tests \ No newline at end of file diff --git a/Guia3/requirements.txt b/Guia3/requirements.txt new file mode 100644 index 0000000..b820918 --- /dev/null +++ b/Guia3/requirements.txt @@ -0,0 +1,2 @@ +pytest>=8.0.0 +pytest-cov>=4.0.0 diff --git a/Guia3/src/__init__.py b/Guia3/src/__init__.py new file mode 100644 index 0000000..9283af9 --- /dev/null +++ b/Guia3/src/__init__.py @@ -0,0 +1,9 @@ +from .alternativa import Alternativa +from .pergunta import Pergunta +from .perguntadiscursiva import PerguntaDiscursiva +from .perguntamultiplaescolha import PerguntaMultiplaEscolha +from .questionario import Questionario +from .resposta import Resposta +from .respostadiscursiva import RespostaDiscursiva +from .respostaobjetiva import RespostaObjetiva +from .tentativaquestionario import TentativaQuestionario \ No newline at end of file diff --git a/Guia3/src/alternativa.py b/Guia3/src/alternativa.py new file mode 100644 index 0000000..ffbfdf8 --- /dev/null +++ b/Guia3/src/alternativa.py @@ -0,0 +1,9 @@ +from typing import List, Tuple, Dict + +class Alternativa: + def __init__(self, texto, correta, explicacao = None): + self._texto = texto + self._correta = correta + self._explicacao = explicacao + + \ No newline at end of file diff --git a/Guia3/src/pergunta.py b/Guia3/src/pergunta.py new file mode 100644 index 0000000..80de6aa --- /dev/null +++ b/Guia3/src/pergunta.py @@ -0,0 +1,19 @@ +from typing import List, Tuple, Dict +from abc import ABC, abstractmethod + +class Pergunta(ABC): + def __init__(self, texto, explicacao_geral = None): + self._texto = texto + self._explicacao_geral = explicacao_geral + + @abstractmethod + def validar_resposta(resposta): + pass + + @property + def get_explicacao(self): + return self._explicacao_geral + + @property + def get_tipo(self): + return self.get_tipo \ No newline at end of file diff --git a/Guia3/src/perguntadiscursiva.py b/Guia3/src/perguntadiscursiva.py new file mode 100644 index 0000000..c9d83a9 --- /dev/null +++ b/Guia3/src/perguntadiscursiva.py @@ -0,0 +1,13 @@ +from typing import List, Tuple, Dict +from pergunta import Pergunta + + +class PerguntaDiscursiva(Pergunta): + def __init__(self, case_sensitive:bool, resposta_esperada:str = None): + self._case_sensitive = case_sensitive + self._resposta_esperada = resposta_esperada + + def validar_resposta(self, texto): + if texto in self._resposta_esperada: + return True + return False diff --git a/Guia3/src/perguntamultiplaescolha.py b/Guia3/src/perguntamultiplaescolha.py new file mode 100644 index 0000000..7a99d7a --- /dev/null +++ b/Guia3/src/perguntamultiplaescolha.py @@ -0,0 +1,16 @@ +from typing import List, Tuple, Dict +from pergunta import Pergunta + +class PerguntaMultiplaEscolha(Pergunta): + def __init__(self): + self._alternativas = [] + + def validar_resposta(self, indice): + if self._alternativas[indice]: + return True + return False + + @property + def get_alternativa_correta(self): + if self.validar_resposta: + return self._alternativas \ No newline at end of file diff --git a/Guia3/src/questionario.py b/Guia3/src/questionario.py new file mode 100644 index 0000000..85664ab --- /dev/null +++ b/Guia3/src/questionario.py @@ -0,0 +1,13 @@ +from typing import List, Tuple, Dict +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(usuario) \ No newline at end of file diff --git a/Guia3/src/resposta.py b/Guia3/src/resposta.py new file mode 100644 index 0000000..a23b4a6 --- /dev/null +++ b/Guia3/src/resposta.py @@ -0,0 +1,12 @@ +from typing import List, Tuple, Dict +from abc import ABC, abstractmethod + +class Resposta(ABC): + def __init__(self, pergunta, esta_correta, pontuacao_obtida): + self._pergunta = pergunta + self._esta_correta = esta_correta + self._pontuacao_obtida = pontuacao_obtida + + @abstractmethod + def calcular_pontuacao(self): + return self._pontuacao_obtida \ No newline at end of file diff --git a/Guia3/src/respostadiscursiva.py b/Guia3/src/respostadiscursiva.py new file mode 100644 index 0000000..eeaa293 --- /dev/null +++ b/Guia3/src/respostadiscursiva.py @@ -0,0 +1,6 @@ +from typing import List, Tuple, Dict +from resposta import Resposta + +class RespostaDiscursiva(Resposta): + def __init__(self, texto_resposta): + self._texto_resposta = texto_resposta \ No newline at end of file diff --git a/Guia3/src/respostaobjetiva.py b/Guia3/src/respostaobjetiva.py new file mode 100644 index 0000000..38168a8 --- /dev/null +++ b/Guia3/src/respostaobjetiva.py @@ -0,0 +1,8 @@ +from typing import List, Tuple, Dict +from resposta import Resposta + +class RespostaObjetiva(Resposta): + def __init__(self, indice_escolhido, alternativa_selecionada = None): + self._indice_escolhido = indice_escolhido + self._alternativa_selecionada = alternativa_selecionada + \ No newline at end of file diff --git a/Guia3/src/tentativaquestionario.py b/Guia3/src/tentativaquestionario.py new file mode 100644 index 0000000..29427e0 --- /dev/null +++ b/Guia3/src/tentativaquestionario.py @@ -0,0 +1,21 @@ +from typing import List, Tuple, Dict + +class TentativaQuestionario: + def __init__(self, questionario, usuario, data_inicio = None, data_fim = None): + self._questionario = questionario + self._usuario = usuario + self._data_inicio = data_inicio + self._data_fim = data_fim + self._respostas = [] + + def registrar_resposta(self, indice_pergunta, valor): + self._respostas.append(valor) + + def finalizar(self): + pass + + def calcular_pontuacao(self): + pass + + def is_finalizado(self): + pass \ No newline at end of file diff --git a/Guia3/tests/__init__.py b/Guia3/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Guia3/tests/test_alternativa.py b/Guia3/tests/test_alternativa.py new file mode 100644 index 0000000..41b5c6d --- /dev/null +++ b/Guia3/tests/test_alternativa.py @@ -0,0 +1,21 @@ +import pytest + +from src.alternativa import Alternativa + + +def test_criar_alternativa(): + alt = Alternativa( + texto="Python", + correta=True, + explicacao="Linguagem interpretada" + ) + + assert alt.texto == "Python" + assert alt.correta is True + assert alt.explicacao == "Linguagem interpretada" + + +def test_alternativa_sem_explicacao(): + alt = Alternativa("Java", False) + + assert alt.explicacao is None \ No newline at end of file diff --git a/Guia3/tests/test_pergunta.py b/Guia3/tests/test_pergunta.py new file mode 100644 index 0000000..160c4db --- /dev/null +++ b/Guia3/tests/test_pergunta.py @@ -0,0 +1,8 @@ +import pytest + +from src.pergunta import Pergunta + + +def test_nao_instanciar_pergunta_abstract(): + with pytest.raises(TypeError): + Pergunta("Pergunta abstrata") \ No newline at end of file diff --git a/Guia3/tests/test_perguntadiscursiva.py b/Guia3/tests/test_perguntadiscursiva.py new file mode 100644 index 0000000..04505a7 --- /dev/null +++ b/Guia3/tests/test_perguntadiscursiva.py @@ -0,0 +1,39 @@ +from src.perguntadiscursiva import PerguntaDiscursiva + + +def test_validar_resposta_correta(): + pergunta = PerguntaDiscursiva( + texto="O que é POO?", + resposta_esperada="Programação Orientada a Objetos" + ) + + resposta = "Programação Orientada a Objetos" + + assert pergunta.validar_resposta(resposta) is True + + +def test_validar_resposta_errada(): + pergunta = PerguntaDiscursiva( + texto="O que é POO?", + resposta_esperada="Programação Orientada a Objetos" + ) + + resposta = "Banco de dados" + + assert pergunta.validar_resposta(resposta) is False + + +def test_pergunta_sem_resposta_esperada(): + pergunta = PerguntaDiscursiva( + texto="Explique encapsulamento." + ) + + assert pergunta.resposta_esperada is None + + +def test_get_tipo(): + pergunta = PerguntaDiscursiva( + texto="Explique herança." + ) + + assert pergunta.get_tipo() == "discursiva" \ No newline at end of file diff --git a/Guia3/tests/test_perguntamultiplaescolha.py b/Guia3/tests/test_perguntamultiplaescolha.py new file mode 100644 index 0000000..45f3d32 --- /dev/null +++ b/Guia3/tests/test_perguntamultiplaescolha.py @@ -0,0 +1,51 @@ +from src.alternativa import Alternativa +from src.perguntamultiplaescolha import PerguntaMultiplaEscolha + + +def criar_pergunta(): + alternativas = [ + Alternativa("Java", False), + Alternativa("Python", True), + Alternativa("C", False), + ] + + return PerguntaMultiplaEscolha( + texto="Qual linguagem é interpretada?", + alternativas=alternativas, + explicacao_geral="Python normalmente é interpretada." + ) + + +def test_validar_resposta_correta(): + pergunta = criar_pergunta() + + assert pergunta.validar_resposta(1) is True + + +def test_validar_resposta_errada(): + pergunta = criar_pergunta() + + assert pergunta.validar_resposta(0) is False + + +def test_get_alternativa_correta(): + pergunta = criar_pergunta() + + correta = pergunta.get_alternativa_correta() + + assert correta.texto == "Python" + assert correta.correta is True + + +def test_get_tipo(): + pergunta = criar_pergunta() + + assert pergunta.get_tipo() == "multipla_escolha" + + +def test_get_explicacao(): + pergunta = criar_pergunta() + + assert pergunta.get_explicacao() == ( + "Python normalmente é interpretada." + ) \ No newline at end of file diff --git a/Guia3/tests/test_questionario.py b/Guia3/tests/test_questionario.py new file mode 100644 index 0000000..c592025 --- /dev/null +++ b/Guia3/tests/test_questionario.py @@ -0,0 +1,27 @@ +import pytest + +from src.questionario import Questionario +from src.perguntadiscursiva import PerguntaDiscursiva +from src.tentativaquestionario import TentativaQuestionario + + +def test_adicionar_pergunta(): + questionario = Questionario("Quiz POO") + + pergunta = PerguntaDiscursiva( + texto="O que é encapsulamento?" + ) + + questionario.adicionar_pergunta(pergunta) + + assert len(questionario.perguntas) == 1 + + +def test_criar_attempt(): + questionario = Questionario("Quiz Redes") + + tentativa = questionario.criar_attempt("valter") + + assert isinstance(tentativa, TentativaQuestionario) + assert tentativa.usuario == "valter" + diff --git a/Guia3/tests/test_resposta.py b/Guia3/tests/test_resposta.py new file mode 100644 index 0000000..fd9a6d6 --- /dev/null +++ b/Guia3/tests/test_resposta.py @@ -0,0 +1,8 @@ +import pytest + +from src.resposta import Resposta + + +def test_nao_instanciar_resposta_abstract(): + with pytest.raises(TypeError): + Resposta(None) \ No newline at end of file diff --git a/Guia3/tests/test_respostadiscursiva.py b/Guia3/tests/test_respostadiscursiva.py new file mode 100644 index 0000000..b1201d4 --- /dev/null +++ b/Guia3/tests/test_respostadiscursiva.py @@ -0,0 +1,42 @@ +from src.perguntadiscursiva import PerguntaDiscursiva +from src.respostadiscursiva import RespostaDiscursiva + + +def criar_pergunta(): + return PerguntaDiscursiva( + texto="O que significa CPU?", + resposta_esperada="Central Processing Unit" + ) + + +def test_resposta_discursiva_correta(): + pergunta = criar_pergunta() + + resposta = RespostaDiscursiva( + pergunta=pergunta, + texto_resposta="Central Processing Unit" + ) + + assert resposta.esta_correta is True + + +def test_resposta_discursiva_errada(): + pergunta = criar_pergunta() + + resposta = RespostaDiscursiva( + pergunta=pergunta, + texto_resposta="Memória RAM" + ) + + assert resposta.esta_correta is False + + +def test_calcular_pontuacao(): + pergunta = criar_pergunta() + + resposta = RespostaDiscursiva( + pergunta=pergunta, + texto_resposta="Central Processing Unit" + ) + + assert resposta.calcular_pontuacao() == 1.0 \ No newline at end of file diff --git a/Guia3/tests/test_respostaobjetiva.py b/Guia3/tests/test_respostaobjetiva.py new file mode 100644 index 0000000..e2a0739 --- /dev/null +++ b/Guia3/tests/test_respostaobjetiva.py @@ -0,0 +1,59 @@ +from src.alternativa import Alternativa +from src.perguntamultiplaescolha import PerguntaMultiplaEscolha +from src.respostaobjetiva import RespostaObjetiva + + +def criar_pergunta(): + alternativas = [ + Alternativa("HTTP", False), + Alternativa("TCP/IP", True), + ] + + return PerguntaMultiplaEscolha( + texto="Qual protocolo é base da internet?", + alternativas=alternativas + ) + + +def test_resposta_objetiva_correta(): + pergunta = criar_pergunta() + + resposta = RespostaObjetiva( + pergunta=pergunta, + indice_escolhido=1 + ) + + assert resposta.esta_correta is True + + +def test_resposta_objetiva_errada(): + pergunta = criar_pergunta() + + resposta = RespostaObjetiva( + pergunta=pergunta, + indice_escolhido=0 + ) + + assert resposta.esta_correta is False + + +def test_calcular_pontuacao_correta(): + pergunta = criar_pergunta() + + resposta = RespostaObjetiva( + pergunta=pergunta, + indice_escolhido=1 + ) + + assert resposta.calcular_pontuacao() == 1.0 + + +def test_calcular_pontuacao_errada(): + pergunta = criar_pergunta() + + resposta = RespostaObjetiva( + pergunta=pergunta, + indice_escolhido=0 + ) + + assert resposta.calcular_pontuacao() == 0.0 \ No newline at end of file diff --git a/Guia3/tests/test_tentativaquestionario.py b/Guia3/tests/test_tentativaquestionario.py new file mode 100644 index 0000000..e0f4ede --- /dev/null +++ b/Guia3/tests/test_tentativaquestionario.py @@ -0,0 +1,90 @@ +from src.alternativa import Alternativa +from src.perguntamultiplaescolha import PerguntaMultiplaEscolha +from src.perguntadiscursiva import PerguntaDiscursiva +from src.questionario import Questionario +from src.tentativaquestionario import TentativaQuestionario + + +def criar_questionario(): + q = Questionario("Quiz") + + p1 = PerguntaMultiplaEscolha( + texto="2 + 2?", + alternativas=[ + Alternativa("3", False), + Alternativa("4", True), + ] + ) + + p2 = PerguntaDiscursiva( + texto="Sigla CPU", + resposta_esperada="Central Processing Unit" + ) + + q.adicionar_pergunta(p1) + q.adicionar_pergunta(p2) + + return q + + +def test_registrar_resposta_objetiva(): + q = criar_questionario() + + tentativa = TentativaQuestionario( + questionario=q, + usuario="valter" + ) + + tentativa.registrar_resposta(0, 1) + + assert len(tentativa.respostas) == 1 + + +def test_registrar_resposta_discursiva(): + q = criar_questionario() + + tentativa = TentativaQuestionario( + questionario=q, + usuario="valter" + ) + + tentativa.registrar_resposta( + 1, + "Central Processing Unit" + ) + + assert len(tentativa.respostas) == 1 + + +def test_calcular_pontuacao(): + q = criar_questionario() + + tentativa = TentativaQuestionario( + questionario=q, + usuario="valter" + ) + + tentativa.registrar_resposta(0, 1) + tentativa.registrar_resposta( + 1, + "Central Processing Unit" + ) + + assert tentativa.calcular_pontuacao() == 2.0 + + +def test_finalizar(): + q = criar_questionario() + + tentativa = TentativaQuestionario( + questionario=q, + usuario="valter" + ) + + tentativa.registrar_resposta(0, 1) + + pontuacao, feedback = tentativa.finalizar() + + assert pontuacao >= 0 + assert isinstance(feedback, str) + assert tentativa.is_finalizado() is True \ No newline at end of file From 488c3e5072db90895645268f68ad033323a97411 Mon Sep 17 00:00:00 2001 From: hiurysousa Date: Fri, 29 May 2026 21:25:47 -0300 Subject: [PATCH 3/5] Guia 3 - 29/05/2026 --- Guia3/src/alternativa.py | 8 +++--- Guia3/src/pergunta.py | 15 +++++------ Guia3/src/perguntadiscursiva.py | 23 +++++++++++----- Guia3/src/perguntamultiplaescolha.py | 19 +++++++------ Guia3/src/questionario.py | 10 +++---- Guia3/src/resposta.py | 13 +++++---- Guia3/src/respostadiscursiva.py | 14 ++++++++-- Guia3/src/respostaobjetiva.py | 19 ++++++++++--- Guia3/src/tentativaquestionario.py | 40 +++++++++++++++++++--------- 9 files changed, 106 insertions(+), 55 deletions(-) diff --git a/Guia3/src/alternativa.py b/Guia3/src/alternativa.py index ffbfdf8..4a0caa5 100644 --- a/Guia3/src/alternativa.py +++ b/Guia3/src/alternativa.py @@ -2,8 +2,6 @@ class Alternativa: def __init__(self, texto, correta, explicacao = None): - self._texto = texto - self._correta = correta - self._explicacao = explicacao - - \ No newline at end of file + self.texto = texto + self.correta = correta + self.explicacao = explicacao diff --git a/Guia3/src/pergunta.py b/Guia3/src/pergunta.py index 80de6aa..3914a15 100644 --- a/Guia3/src/pergunta.py +++ b/Guia3/src/pergunta.py @@ -3,17 +3,16 @@ class Pergunta(ABC): def __init__(self, texto, explicacao_geral = None): - self._texto = texto - self._explicacao_geral = explicacao_geral + self.texto = texto + self.explicacao_geral = explicacao_geral + + def get_explicacao(self): + return self.explicacao_geral @abstractmethod - def validar_resposta(resposta): + def validar_resposta(self, resposta): pass - - @property - def get_explicacao(self): - return self._explicacao_geral - @property + @abstractmethod def get_tipo(self): return self.get_tipo \ No newline at end of file diff --git a/Guia3/src/perguntadiscursiva.py b/Guia3/src/perguntadiscursiva.py index c9d83a9..c348ac5 100644 --- a/Guia3/src/perguntadiscursiva.py +++ b/Guia3/src/perguntadiscursiva.py @@ -3,11 +3,22 @@ class PerguntaDiscursiva(Pergunta): - def __init__(self, case_sensitive:bool, resposta_esperada:str = None): - self._case_sensitive = case_sensitive - self._resposta_esperada = resposta_esperada + def __init__(self, texto, resposta_esperada = None, case_sensitive = False): + super().__init__(texto) + self.resposta_esperada = resposta_esperada + self.case_sensitive = case_sensitive def validar_resposta(self, texto): - if texto in self._resposta_esperada: - return True - return False + if self.resposta_esperada is None: + return False + + resp_usuario = texto.strip() + resp_esperada = self.resposta_esperada.strip() + + if not self.case_sensitive: + return resp_usuario.lower() == resp_esperada.lower() + + return resp_usuario == resp_esperada + + def get_tipo(self): + return "discursiva" diff --git a/Guia3/src/perguntamultiplaescolha.py b/Guia3/src/perguntamultiplaescolha.py index 7a99d7a..5df295a 100644 --- a/Guia3/src/perguntamultiplaescolha.py +++ b/Guia3/src/perguntamultiplaescolha.py @@ -2,15 +2,18 @@ from pergunta import Pergunta class PerguntaMultiplaEscolha(Pergunta): - def __init__(self): - self._alternativas = [] + def __init__(self, texto, alternativas, explicacao_geral = None): + super().__init__(texto, explicacao_geral) + self.alternativas = alternativas def validar_resposta(self, indice): - if self._alternativas[indice]: - return True - return False + return self.alternativas[indice].correta - @property def get_alternativa_correta(self): - if self.validar_resposta: - return self._alternativas \ No newline at end of file + for alternativa in self.alternativas: + if alternativa.correta: + return alternativa + return None + + def get_tipo(self): + return "multipla_escolha" \ No newline at end of file diff --git a/Guia3/src/questionario.py b/Guia3/src/questionario.py index 85664ab..12b71ab 100644 --- a/Guia3/src/questionario.py +++ b/Guia3/src/questionario.py @@ -1,13 +1,13 @@ from typing import List, Tuple, Dict -from tentativaquestionario import TentativaQuestionario +from src.tentativaquestionario import TentativaQuestionario class Questionario: def __init__(self, titulo): - self._titulo = titulo - self._perguntas = [] + self.titulo = titulo + self.perguntas = [] def adicionar_pergunta(self, pergunta): - self._perguntas.append(pergunta) + self.perguntas.append(pergunta) def criar_attempt(self, usuario): - return TentativaQuestionario(usuario) \ No newline at end of file + return TentativaQuestionario(self, usuario) \ No newline at end of file diff --git a/Guia3/src/resposta.py b/Guia3/src/resposta.py index a23b4a6..be525ef 100644 --- a/Guia3/src/resposta.py +++ b/Guia3/src/resposta.py @@ -2,11 +2,14 @@ from abc import ABC, abstractmethod class Resposta(ABC): - def __init__(self, pergunta, esta_correta, pontuacao_obtida): - self._pergunta = pergunta - self._esta_correta = esta_correta - self._pontuacao_obtida = pontuacao_obtida + def __init__(self, pergunta): + if type(self) is Resposta: + raise TypeError(f'Não é permitido instanciar a classe abstrata Resposta diretamente.') + + self.pergunta = pergunta + self.esta_correta = False + self.pontuacao_obtida = 0.0 @abstractmethod def calcular_pontuacao(self): - return self._pontuacao_obtida \ No newline at end of file + pass \ No newline at end of file diff --git a/Guia3/src/respostadiscursiva.py b/Guia3/src/respostadiscursiva.py index eeaa293..db680c3 100644 --- a/Guia3/src/respostadiscursiva.py +++ b/Guia3/src/respostadiscursiva.py @@ -2,5 +2,15 @@ from resposta import Resposta class RespostaDiscursiva(Resposta): - def __init__(self, texto_resposta): - self._texto_resposta = texto_resposta \ No newline at end of file + def __init__(self, pergunta, texto_resposta): + super().__init__(pergunta) + self.texto_resposta = texto_resposta + self.esta_correta = pergunta.validar_resposta(texto_resposta) + + def calcular_pontuacao(self): + if self.esta_correta: + self.pontuacao_obtida = 1.0 + else: + self.pontuacao_obtida = 0.0 + + return self.pontuacao_obtida \ No newline at end of file diff --git a/Guia3/src/respostaobjetiva.py b/Guia3/src/respostaobjetiva.py index 38168a8..78ac518 100644 --- a/Guia3/src/respostaobjetiva.py +++ b/Guia3/src/respostaobjetiva.py @@ -2,7 +2,18 @@ from resposta import Resposta class RespostaObjetiva(Resposta): - def __init__(self, indice_escolhido, alternativa_selecionada = None): - self._indice_escolhido = indice_escolhido - self._alternativa_selecionada = alternativa_selecionada - \ No newline at end of file + def __init__(self, pergunta, indice_escolhido): + super().__init__(pergunta) + self.indice_escolhido = indice_escolhido + self.esta_correta = pergunta.validar_resposta(indice_escolhido) + self.alternativa_selecionada = pergunta.alternativas[indice_escolhido] + + def calcular_pontuacao(self): + if self.esta_correta: + self.pontuacao_obtida = 1.0 + else: + self.pontuacao_obtida = 0.0 + + return self.pontuacao_obtida + + \ No newline at end of file diff --git a/Guia3/src/tentativaquestionario.py b/Guia3/src/tentativaquestionario.py index 29427e0..4ae0c67 100644 --- a/Guia3/src/tentativaquestionario.py +++ b/Guia3/src/tentativaquestionario.py @@ -1,21 +1,37 @@ -from typing import List, Tuple, Dict +from datetime import datetime +from src.respostadiscursiva import RespostaDiscursiva +from src.respostaobjetiva import RespostaObjetiva class TentativaQuestionario: - def __init__(self, questionario, usuario, data_inicio = None, data_fim = None): - self._questionario = questionario - self._usuario = usuario - self._data_inicio = data_inicio - self._data_fim = data_fim - self._respostas = [] + def __init__(self, questionario, usuario): + self.questionario = questionario + self.usuario = usuario + self.respostas = [] + self.data_inicio = datetime.now() + self.data_fim = None + self.finalizado = False def registrar_resposta(self, indice_pergunta, valor): - self._respostas.append(valor) + pergunta = self.questionario.perguntas[indice_pergunta] + if pergunta.get_tipo() == "multipla_escolha": + nova_resposta = RespostaObjetiva(pergunta, valor) + else: + nova_resposta = RespostaDiscursiva(pergunta, valor) - def finalizar(self): - pass + self.respostas.append(nova_resposta) def calcular_pontuacao(self): - pass + total = 0.0 + for resp in self.respostas: + total += resp.calcular_pontuacao() + return total + + def finalizar(self): + self.data_fim = datetime.now() + self.finalizado = True + nota_final = self.calcular_pontuacao() + + return (nota_final, "Finalizado") def is_finalizado(self): - pass \ No newline at end of file + return self.finalizado \ No newline at end of file From f21a2bb1ae16eda4b48c4a567b9015521967902e Mon Sep 17 00:00:00 2001 From: hiurysousa Date: Fri, 12 Jun 2026 20:35:23 -0300 Subject: [PATCH 4/5] Adiciona Guia4 --- Guia4/.gitignore | 13 ++ Guia4/README.md | 173 +++++++++++++++++++++++++++ Guia4/main.py | 8 ++ Guia4/requirements.txt | 4 + Guia4/src/__init__.py | 9 ++ Guia4/src/alternativa.py | 4 + Guia4/src/correcao.py | 4 + Guia4/src/llmservice.py | 4 + Guia4/src/pergunta.py | 4 + Guia4/src/perguntadiscursiva.py | 4 + Guia4/src/perguntamultiplaescolha.py | 4 + Guia4/src/questionario.py | 4 + Guia4/src/resposta.py | 4 + Guia4/src/respostadiscursiva.py | 4 + Guia4/src/respostaobjetiva.py | 4 + Guia4/src/tentativaquestionario.py | 4 + Guia4/tests/__init__.py | 0 17 files changed, 251 insertions(+) create mode 100644 Guia4/.gitignore create mode 100644 Guia4/README.md create mode 100644 Guia4/main.py create mode 100644 Guia4/requirements.txt create mode 100644 Guia4/src/__init__.py create mode 100644 Guia4/src/alternativa.py create mode 100644 Guia4/src/correcao.py create mode 100644 Guia4/src/llmservice.py create mode 100644 Guia4/src/pergunta.py create mode 100644 Guia4/src/perguntadiscursiva.py create mode 100644 Guia4/src/perguntamultiplaescolha.py create mode 100644 Guia4/src/questionario.py create mode 100644 Guia4/src/resposta.py create mode 100644 Guia4/src/respostadiscursiva.py create mode 100644 Guia4/src/respostaobjetiva.py create mode 100644 Guia4/src/tentativaquestionario.py create mode 100644 Guia4/tests/__init__.py diff --git a/Guia4/.gitignore b/Guia4/.gitignore new file mode 100644 index 0000000..4b3bf0c --- /dev/null +++ b/Guia4/.gitignore @@ -0,0 +1,13 @@ +__pycache__/ +*.pyc +*.pyo +*.pyd +venv/ +.venv/ +.venv +env/ +.env +.pytest_cache/ +.pytest_cache +.coverage +htmlcov/ diff --git a/Guia4/README.md b/Guia4/README.md new file mode 100644 index 0000000..ca95454 --- /dev/null +++ b/Guia4/README.md @@ -0,0 +1,173 @@ +# Guia 4 — Serviço LLM + +## Contexto +Você faz parte da equipe responsável por desenvolver um **Sistema de Quiz** educativo. +O sistema deve permitir a criação de quizzes com perguntas de múltipla escolha ou **discursivas**, validação de respostas e cálculo de pontuação. + +Agora, **perguntas discursivas** deverão ser corrigidas por um serviço de LLM (Groq) via API. +Sua missão é implementar/completar as classes seguindo o **diagrama UML** complementar e as regras abaixo. + +--- + +## Diagrama UML + +### 1. Diagrama Principal (mantido exatamente como estava) + +```mermaid +classDiagram + direction TB + class Pergunta { + <> + -String texto + -String? explicacao_geral + +validar_resposta(resposta) boolean + +get_explicacao() String + +get_tipo() String + } + class PerguntaMultiplaEscolha { + -List~Alternativa~ alternativas + +validar_resposta(int indice) boolean + +get_alternativa_correta() Alternativa + } + class PerguntaDiscursiva { + -String? resposta_esperada + +validar_resposta(String texto) boolean + } + class Alternativa { + +String texto + +boolean correta + +String? explicacao + } + class Resposta { + <> + -Pergunta pergunta + -boolean esta_correta + -float pontuacao_obtida + +calcular_pontuacao() float + } + class RespostaObjetiva { + -int indice_escolhido + -Alternativa? alternativa_selecionada + } + class RespostaDiscursiva { + -String texto_resposta + } + class Questionario { + -String titulo + -List~Pergunta~ perguntas + +adicionar_pergunta(Pergunta p) + +criar_attempt(String usuario) QuizAttempt + } + class TentativaQuestionario { + -Questionario questionario + -String usuario + -DateTime? data_inicio + -DateTime? data_fim + -List~Resposta~ respostas + +registrar_resposta(int indice_pergunta, Object valor) + +finalizar() Tuple~float, String~ + +calcular_pontuacao() float + +is_finalizado() boolean + } + + %% Relacionamentos + Pergunta <|-- PerguntaMultiplaEscolha + Pergunta <|-- PerguntaDiscursiva + Resposta <|-- RespostaObjetiva + Resposta <|-- RespostaDiscursiva + + PerguntaMultiplaEscolha "1" *-- "2..*" Alternativa + Questionario "1" *-- "0..*" Pergunta + Questionario "1" --> "0..*" TentativaQuestionario + TentativaQuestionario "1" *-- "0..*" Resposta + Resposta "1" --> "1" Pergunta +``` + +### 2. Diagrama Complementar — Integração com LLM (Novo) + +```mermaid +classDiagram + direction TB + + class LLMService { + <> + -String api_key + -String model + -String base_url + +__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 { + <> + +corrigir_discursiva(pergunta: PerguntaDiscursiva, resposta_aluno: str, service: LLMService = None) -> Dict + +criar_prompt_correcao(pergunta: PerguntaDiscursiva, resposta_aluno: str) -> str + } + + LLMService o-- Correcao : "é usado por" +``` + +--- + +## Nova Descrição das Classes + +### LLMService (Classe de Serviço) +Responsável pela **conexão direta** com o serviço Groq. + +**Responsabilidades:** +- Guardar a API Key (deve ser carregada via variável de ambiente `GROQ_API_KEY`) +- Realizar as chamadas HTTP para a API do Groq +- **Tratar todos os erros** (timeout, rate limit, autenticação, JSON inválido, etc.) internamente +- Montar o prompt adequado para correção de questões discursivas +- Retornar apenas o resultado limpo (nunca expor detalhes da API para o resto da aplicação) + +**Métodos principais:** +- `corrigir_resposta(pergunta: PerguntaDiscursiva, resposta_aluno: str) → Dict` + - Retorna dicionário com: `{"correta": bool, "pontuacao": float, "feedback": str, "explicacao": str}` + +### CorrecaoUtil (Classe Utilitária) +Classe de alto nível que a aplicação deve usar. + +**Responsabilidades:** +- Abstrair o uso do `LLMService` +- Criar o prompt de forma inteligente +- Fornecer interface simples e limpa para o resto do sistema +- Possibilitar uso de mock em testes + +**Método principal:** +- `corrigir_discursiva(...)` + +--- + +## Regras de Implementação Importantes + +1. **Não modificar** as classes existentes do diagrama principal. +2. A classe `PerguntaDiscursiva` **não deve** conhecer o `LLMService` diretamente. +3. Toda correção de discursiva deve passar pela `Correcao`. +4. O `LLMService` deve: + - Usar a biblioteca `groq` (ou `requests`) + - Tratar erros internamente + - Ter fallback ou mensagem clara em caso de falha na API +5. A API Key **nunca** deve ficar hard-coded. + +--- + +## Como preparar o ambiente + +```bash +# 1. Criar venv +python -m venv .venv + +# 2. Ativar +# Windows +.\.venv\Scripts\activate +# Linux/macOS +source .venv/bin/activate + +# 3. Instalar dependências +pip install -r requirements.txt +``` + +--- \ No newline at end of file diff --git a/Guia4/main.py b/Guia4/main.py new file mode 100644 index 0000000..e9e667d --- /dev/null +++ b/Guia4/main.py @@ -0,0 +1,8 @@ +from Guia3.src import * + +def main(): + pass + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/Guia4/requirements.txt b/Guia4/requirements.txt new file mode 100644 index 0000000..a0ca34d --- /dev/null +++ b/Guia4/requirements.txt @@ -0,0 +1,4 @@ +pytest>=8.0.0 +pytest-cov>=4.0.0 +groq +python-dotenv \ No newline at end of file diff --git a/Guia4/src/__init__.py b/Guia4/src/__init__.py new file mode 100644 index 0000000..9283af9 --- /dev/null +++ b/Guia4/src/__init__.py @@ -0,0 +1,9 @@ +from .alternativa import Alternativa +from .pergunta import Pergunta +from .perguntadiscursiva import PerguntaDiscursiva +from .perguntamultiplaescolha import PerguntaMultiplaEscolha +from .questionario import Questionario +from .resposta import Resposta +from .respostadiscursiva import RespostaDiscursiva +from .respostaobjetiva import RespostaObjetiva +from .tentativaquestionario import TentativaQuestionario \ No newline at end of file diff --git a/Guia4/src/alternativa.py b/Guia4/src/alternativa.py new file mode 100644 index 0000000..4dde61f --- /dev/null +++ b/Guia4/src/alternativa.py @@ -0,0 +1,4 @@ +from typing import List, Tuple, Dict + +class Alternativa: + pass \ No newline at end of file diff --git a/Guia4/src/correcao.py b/Guia4/src/correcao.py new file mode 100644 index 0000000..bdf2fa4 --- /dev/null +++ b/Guia4/src/correcao.py @@ -0,0 +1,4 @@ +from typing import List, Tuple, Dict + +class Correcao: + pass \ No newline at end of file diff --git a/Guia4/src/llmservice.py b/Guia4/src/llmservice.py new file mode 100644 index 0000000..e6e91b5 --- /dev/null +++ b/Guia4/src/llmservice.py @@ -0,0 +1,4 @@ +from typing import List, Tuple, Dict + +class LLMService: + pass \ No newline at end of file diff --git a/Guia4/src/pergunta.py b/Guia4/src/pergunta.py new file mode 100644 index 0000000..5b3763d --- /dev/null +++ b/Guia4/src/pergunta.py @@ -0,0 +1,4 @@ +from typing import List, Tuple, Dict + +class Pergunta: + pass \ No newline at end of file diff --git a/Guia4/src/perguntadiscursiva.py b/Guia4/src/perguntadiscursiva.py new file mode 100644 index 0000000..f4c26af --- /dev/null +++ b/Guia4/src/perguntadiscursiva.py @@ -0,0 +1,4 @@ +from typing import List, Tuple, Dict + +class PerguntaDiscursiva: + pass \ No newline at end of file diff --git a/Guia4/src/perguntamultiplaescolha.py b/Guia4/src/perguntamultiplaescolha.py new file mode 100644 index 0000000..bcbe94d --- /dev/null +++ b/Guia4/src/perguntamultiplaescolha.py @@ -0,0 +1,4 @@ +from typing import List, Tuple, Dict + +class PerguntaMultiplaEscolha: + pass \ No newline at end of file diff --git a/Guia4/src/questionario.py b/Guia4/src/questionario.py new file mode 100644 index 0000000..7525582 --- /dev/null +++ b/Guia4/src/questionario.py @@ -0,0 +1,4 @@ +from typing import List, Tuple, Dict + +class Questionario: + pass diff --git a/Guia4/src/resposta.py b/Guia4/src/resposta.py new file mode 100644 index 0000000..846d771 --- /dev/null +++ b/Guia4/src/resposta.py @@ -0,0 +1,4 @@ +from typing import List, Tuple, Dict + +class Resposta: + pass \ No newline at end of file diff --git a/Guia4/src/respostadiscursiva.py b/Guia4/src/respostadiscursiva.py new file mode 100644 index 0000000..4ea6dbb --- /dev/null +++ b/Guia4/src/respostadiscursiva.py @@ -0,0 +1,4 @@ +from typing import List, Tuple, Dict + +class RespostaDiscursiva: + pass \ No newline at end of file diff --git a/Guia4/src/respostaobjetiva.py b/Guia4/src/respostaobjetiva.py new file mode 100644 index 0000000..72ed2d0 --- /dev/null +++ b/Guia4/src/respostaobjetiva.py @@ -0,0 +1,4 @@ +from typing import List, Tuple, Dict + +class RespostaObjetiva: + pass \ No newline at end of file diff --git a/Guia4/src/tentativaquestionario.py b/Guia4/src/tentativaquestionario.py new file mode 100644 index 0000000..9947dd1 --- /dev/null +++ b/Guia4/src/tentativaquestionario.py @@ -0,0 +1,4 @@ +from typing import List, Tuple, Dict + +class TentativaQuestionario: + pass \ No newline at end of file diff --git a/Guia4/tests/__init__.py b/Guia4/tests/__init__.py new file mode 100644 index 0000000..e69de29 From b256c80fe32e0eea5cd1f6cd739f7d69161d4688 Mon Sep 17 00:00:00 2001 From: hiurysousa Date: Fri, 12 Jun 2026 23:06:12 -0300 Subject: [PATCH 5/5] fix: atualiza modelo do Groq e ajusta visibilidade do metodo de chamada da API --- Guia4/src/correcao.py | 37 ++++++++++++++++++- Guia4/src/llmservice.py | 30 ++++++++++++++- Guia4/tests/teste_correcao.py | 69 +++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 4 deletions(-) create mode 100644 Guia4/tests/teste_correcao.py diff --git a/Guia4/src/correcao.py b/Guia4/src/correcao.py index bdf2fa4..995d283 100644 --- a/Guia4/src/correcao.py +++ b/Guia4/src/correcao.py @@ -1,4 +1,37 @@ -from typing import List, Tuple, Dict +import json +from typing import Dict +from perguntadiscursiva import PerguntaDiscursiva +from llmservice import LLMService class Correcao: - pass \ No newline at end of file + + @staticmethod + def corrigir_discursiva(pergunta: PerguntaDiscursiva, resposta_aluno: str, service: LLMService = None) -> Dict: + if service is None: + service = LLMService() + + prompt = Correcao.criar_prompt_correcao(pergunta, resposta_aluno) + + try: + resposta_texto = service.fazer_chamada_api(prompt) + return json.loads(resposta_texto) + # No arquivo src/correcao.py, mude temporariamente para: + except Exception as e: + print(f"\n🚨 ERRO REAL DA API: {e}\n") # <-- Adicione essa linha para diagnosticar + return { + "correta": False, + "pontuacao": 0.0, + "feedback": "Erro na comunicação com o serviço de correção.", + "explicacao": "Não foi possível validar a resposta no momento." + } + + @staticmethod + def criar_prompt_correcao(pergunta: PerguntaDiscursiva, resposta_aluno: str) -> str: + return ( + "Avalie a resposta do aluno com base nos critérios fornecidos.\n" + f"Enunciado da Questão: {pergunta.texto}\n" + f"Resposta Esperada: {pergunta.resposta_esperada}\n" + f"Resposta enviada pelo Aluno: {resposta_aluno}\n\n" + "Retorne obrigatoriamente um formato JSON com as chaves exatas:\n" + '{"correta": bool, "pontuacao": float, "feedback": "str", "explicacao": "str"}' + ) \ No newline at end of file diff --git a/Guia4/src/llmservice.py b/Guia4/src/llmservice.py index e6e91b5..7b78f3d 100644 --- a/Guia4/src/llmservice.py +++ b/Guia4/src/llmservice.py @@ -1,4 +1,30 @@ -from typing import List, Tuple, Dict +import os +import json +from typing import Dict +from groq import Groq +from perguntadiscursiva import PerguntaDiscursiva class LLMService: - pass \ No newline at end of file + def __init__(self, api_key: str = None, model: str = "llama-3.3-70b-versatile", groq_obj: Groq = None): + self.api_key = api_key or os.environ.get("GROQ_API_KEY") + self.model = model + self.groq_obj = groq_obj or (Groq(api_key=self.api_key) if self.api_key else None) + + def fazer_chamada_api(self, prompt: str) -> str: + if not isinstance(prompt, str): + raise TypeError("Erro na entrada de dados. O prompt deve ser uma string.") + + if not self.groq_obj: + raise ValueError("Cliente Groq não foi inicializado. Verifique a API Key.") + + response = self.groq_obj.chat.completions.create( + model=self.model, + temperature=0.0, + messages=[ + {"role": "system", "content": "Você é um professor avaliador. Responda APENAS com um JSON estruturado."}, + {"role": "user", "content": prompt}, + ] + ) + return response.choices[0].message.content + + \ No newline at end of file diff --git a/Guia4/tests/teste_correcao.py b/Guia4/tests/teste_correcao.py new file mode 100644 index 0000000..bd28356 --- /dev/null +++ b/Guia4/tests/teste_correcao.py @@ -0,0 +1,69 @@ +import pytest +import json +from unittest.mock import MagicMock +from src.perguntadiscursiva import PerguntaDiscursiva +from src.llmservice import LLMService +from src.correcao import Correcao + +def test_criar_prompt_correcao(): + """Garante que o prompt está sendo montado com os dados corretos da questão.""" + pergunta = PerguntaDiscursiva( + texto="O que é encapsulamento em POO?", + resposta_esperada="Proteger os atributos internos de uma classe usando métodos públicos." + ) + resposta_aluno = "É esconder as variáveis e usar getters e setters." + + prompt = Correcao.criar_prompt_correcao(pergunta, resposta_aluno) + + # Valida se as informações cruciais foram injetadas na string do prompt + assert "O que é encapsulamento em POO?" in prompt + assert "Proteger os atributos internos" in prompt + assert "É esconder as variáveis" in prompt + + +def test_corrigir_discursiva_com_sucesso_via_mock(): + """Testa o fluxo feliz: a API responde um JSON válido e a classe Correcao o decodifica.""" + pergunta = PerguntaDiscursiva(texto="O que é uma classe?", resposta_esperada="Um molde para objetos.") + resposta_aluno = "É a estrutura que define um objeto." + + # 1. Cria um mock do LLMService + mock_service = MagicMock(spec=LLMService) + + # Simula a string JSON que o modelo retornaria na vida real + json_retorno_llm = json.dumps({ + "correta": True, + "pontuacao": 1.0, + "feedback": "Resposta muito boa e direta.", + "explicacao": "O aluno compreendeu que a classe funciona como a definição estrutural." + }) + + # Configura o método público para retornar essa string + mock_service.fazer_chamada_api.return_value = json_retorno_llm + + # 2. Executa a correção injetando o nosso mock + resultado = Correcao.corrigir_discursiva(pergunta, resposta_aluno, service=mock_service) + + # 3. Asserts + assert resultado["correta"] is True + assert resultado["pontuacao"] == 1.0 + assert resultado["feedback"] == "Resposta muito boa e direta." + + # Garante que a classe Correcao chamou o método exato que sobrou no diagrama do professor + mock_service.fazer_chamada_api.assert_called_once() + + +def test_fallback_quando_api_retorna_erro(): + """Testa se o sistema lida com falhas de rede/API sem quebrar o programa.""" + pergunta = PerguntaDiscursiva(texto="O que é polimorfismo?", resposta_esperada="Métodos com mesma assinatura e comportamentos diferentes.") + + mock_service = MagicMock(spec=LLMService) + # Simula o método disparando um erro de conexão/timeout + mock_service.fazer_chamada_api.side_effect = Exception("Erro de conexão com o servidor do Groq") + + # Executa a correção que vai falhar internamente na chamada da API + resultado = Correcao.corrigir_discursiva(pergunta, "Não lembro", service=mock_service) + + # Verifica se o bloco 'except' capturou a falha e retornou o dicionário padrão de erro + assert resultado["correta"] is False + assert resultado["pontuacao"] == 0.0 + assert "Erro na comunicação" in resultado["feedback"] \ No newline at end of file