From 8df619bb3873343e6a6587ecb76f6e741b3ddbfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Pedro?= Date: Thu, 28 May 2026 20:14:36 -0300 Subject: [PATCH 1/4] ATV3_1ENVIO_JOAOPEDRO --- Guia3/src/alternativa.py | 5 +++- Guia3/src/pergunta.py | 19 +++++++++++-- Guia3/src/perguntadiscursiva.py | 15 +++++++++- Guia3/src/perguntamultiplaescolha.py | 41 +++++++++++++++++++++++++++- Guia3/src/questionario.py | 15 +++++++++- Guia3/src/resposta.py | 11 ++++++-- Guia3/src/respostadiscursiva.py | 3 +- Guia3/src/respostaobjetiva.py | 4 ++- 8 files changed, 103 insertions(+), 10 deletions(-) diff --git a/Guia3/src/alternativa.py b/Guia3/src/alternativa.py index 4dde61f..cdae2b5 100644 --- a/Guia3/src/alternativa.py +++ b/Guia3/src/alternativa.py @@ -1,4 +1,7 @@ from typing import List, Tuple, Dict class Alternativa: - pass \ No newline at end of file + def __init__(self, texto: str, correta: bool, explicacao: str = 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 index 5b3763d..0a6aee1 100644 --- a/Guia3/src/pergunta.py +++ b/Guia3/src/pergunta.py @@ -1,4 +1,19 @@ from typing import List, Tuple, Dict +from abc import ABC, abstractmethod -class Pergunta: - pass \ No newline at end of file +class Pergunta(ABC): + def __init__(self, texto: str, explicacao_geral: str = None): + self._texto = texto + self._explicacao_geral = explicacao_geral + + @abstractmethod + def validar_resposta(self, resposta) -> bool: + pass + + @abstractmethod + def get_explicacao(self) -> str: + pass + + @abstractmethod + def get_tipo(self) -> str: + pass \ No newline at end of file diff --git a/Guia3/src/perguntadiscursiva.py b/Guia3/src/perguntadiscursiva.py index f4c26af..e381509 100644 --- a/Guia3/src/perguntadiscursiva.py +++ b/Guia3/src/perguntadiscursiva.py @@ -1,4 +1,17 @@ from typing import List, Tuple, Dict class PerguntaDiscursiva: - pass \ No newline at end of file + def __init__(self, texto, resposta_esperada=None, case_sensitive: bool = False): + self.texto = texto + self.resposta_esperada = resposta_esperada + self.case_sensitive = case_sensitive + + def get_tipo(self): + return "discursiva" + + def validar_resposta(self, texto: str): + if self.resposta_esperada is None: + return False + if self.case_sensitive: + return texto == self.resposta_esperada + return texto.strip().lower() == self.resposta_esperada.strip().lower() \ No newline at end of file diff --git a/Guia3/src/perguntamultiplaescolha.py b/Guia3/src/perguntamultiplaescolha.py index bcbe94d..d60d625 100644 --- a/Guia3/src/perguntamultiplaescolha.py +++ b/Guia3/src/perguntamultiplaescolha.py @@ -1,4 +1,43 @@ from typing import List, Tuple, Dict class PerguntaMultiplaEscolha: - pass \ No newline at end of file + def __init__(self, texto: str, alternativas: List[Tuple[str, bool]], explicacao_geral: str = ""): + self.texto = texto + self.alternativas = alternativas + self.explicacao_geral = explicacao_geral + + def validar_resposta(self, resposta) -> bool: + if isinstance(resposta, int): + if 0 <= resposta < len(self.alternativas): + return self.alternativas[resposta][1] + return False + + if isinstance(resposta, str): + for texto, correta in self.alternativas: + if texto == resposta: + return correta + return False + + if isinstance(resposta, (list, tuple, set)): + selecionadas = set() + for item in resposta: + if isinstance(item, int): + if 0 <= item < len(self.alternativas): + selecionadas.add(self.alternativas[item][0]) + else: + return False + elif isinstance(item, str): + selecionadas.add(item) + else: + return False + + corretas = {texto for texto, correta in self.alternativas if correta} + return selecionadas == corretas + + return False + + def get_explicacao(self) -> str: + return self.explicacao_geral + + def get_tipo(self) -> str: + return "multipla_escolha" diff --git a/Guia3/src/questionario.py b/Guia3/src/questionario.py index 7525582..b96937d 100644 --- a/Guia3/src/questionario.py +++ b/Guia3/src/questionario.py @@ -1,4 +1,17 @@ from typing import List, Tuple, Dict class Questionario: - pass + def __init__(self, resposta_esperada=None, case_sensitive: bool = False): + self.resposta_esperada = resposta_esperada + self.case_sensitive = case_sensitive + self.perguntas = [] + + def adicionar_pergunta(self, pergunta): + self.perguntas.append(pergunta) + + def validar_resposta(self, texto: str) -> bool: + if self.resposta_esperada is None: + return True + if self.case_sensitive: + return texto == self.resposta_esperada + return texto.lower() == self.resposta_esperada.lower() \ No newline at end of file diff --git a/Guia3/src/resposta.py b/Guia3/src/resposta.py index 846d771..7599202 100644 --- a/Guia3/src/resposta.py +++ b/Guia3/src/resposta.py @@ -1,4 +1,11 @@ +from abc import ABC from typing import List, Tuple, Dict -class Resposta: - pass \ No newline at end of file +class Resposta(ABC): + def __init__(self,pergunta, esta_correta: bool, pontuacao_obtida: float): + self.pergunta = pergunta + self.esta_correta = esta_correta + self.pontuacao_obtida = pontuacao_obtida + + def calcular_pontuacao(self): + pass #VER AMANHÃ!!!! \ No newline at end of file diff --git a/Guia3/src/respostadiscursiva.py b/Guia3/src/respostadiscursiva.py index 4ea6dbb..7361a4d 100644 --- a/Guia3/src/respostadiscursiva.py +++ b/Guia3/src/respostadiscursiva.py @@ -1,4 +1,5 @@ from typing import List, Tuple, Dict class RespostaDiscursiva: - pass \ No newline at end of file + 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 index 72ed2d0..ad946d3 100644 --- a/Guia3/src/respostaobjetiva.py +++ b/Guia3/src/respostaobjetiva.py @@ -1,4 +1,6 @@ from typing import List, Tuple, Dict class RespostaObjetiva: - pass \ No newline at end of file + def __init__(self, indice_escolhido, alternativa_selecionada): + self.indice_escolhido = indice_escolhido + self.alternativa_selecionada = alternativa_selecionada \ No newline at end of file From 382cffe5a4d027257a3dd326bd687fbce1794d69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Pedro?= Date: Fri, 29 May 2026 21:41:00 -0300 Subject: [PATCH 2/4] ATV3_29/05/2026_JOAOPEDRO_OK --- .../src/__pycache__/__init__.cpython-314.pyc | Bin 0 -> 175 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 182 bytes .../__pycache__/settings.cpython-314.pyc | Bin 0 -> 778 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 182 bytes .../models/__pycache__/record.cpython-314.pyc | Bin 0 -> 1694 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 188 bytes .../abstract_repository.cpython-314.pyc | Bin 0 -> 1057 bytes .../record_repository.cpython-314.pyc | Bin 0 -> 2206 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 184 bytes .../record_service.cpython-314.pyc | Bin 0 -> 1684 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 181 bytes .../__pycache__/file_loader.cpython-314.pyc | Bin 0 -> 1474 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 177 bytes .../test_runner.cpython-314-pytest-9.0.3.pyc | Bin 0 -> 5567 bytes Guia3/src/pergunta.py | 2 +- Guia3/src/perguntadiscursiva.py | 21 +++++----- Guia3/src/perguntamultiplaescolha.py | 34 ++++++++++++---- Guia3/src/questionario.py | 20 ++++----- Guia3/src/resposta.py | 2 +- Guia3/src/respostadiscursiva.py | 10 ++++- Guia3/src/respostaobjetiva.py | 9 ++++- Guia3/src/tentativaquestionario.py | 38 +++++++++++++++++- 22 files changed, 99 insertions(+), 37 deletions(-) create mode 100644 Guia1/src/__pycache__/__init__.cpython-314.pyc create mode 100644 Guia1/src/config/__pycache__/__init__.cpython-314.pyc create mode 100644 Guia1/src/config/__pycache__/settings.cpython-314.pyc create mode 100644 Guia1/src/models/__pycache__/__init__.cpython-314.pyc create mode 100644 Guia1/src/models/__pycache__/record.cpython-314.pyc create mode 100644 Guia1/src/repositories/__pycache__/__init__.cpython-314.pyc create mode 100644 Guia1/src/repositories/__pycache__/abstract_repository.cpython-314.pyc create mode 100644 Guia1/src/repositories/__pycache__/record_repository.cpython-314.pyc create mode 100644 Guia1/src/services/__pycache__/__init__.cpython-314.pyc create mode 100644 Guia1/src/services/__pycache__/record_service.cpython-314.pyc create mode 100644 Guia1/src/utils/__pycache__/__init__.cpython-314.pyc create mode 100644 Guia1/src/utils/__pycache__/file_loader.cpython-314.pyc create mode 100644 Guia1/tests/__pycache__/__init__.cpython-314.pyc create mode 100644 Guia1/tests/__pycache__/test_runner.cpython-314-pytest-9.0.3.pyc diff --git a/Guia1/src/__pycache__/__init__.cpython-314.pyc b/Guia1/src/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..999c12765b1ab70ae3fadea3e08726a90c3e7a44 GIT binary patch literal 175 zcmdPq5CH>>P{wCAAftgHh(Vb_lhJP_LlF~@{~09t#S=(4Tg8MH zrxq2*csk`|X6B{DxTF?mm*f}3IF@9VWu_#iq#DNur55MslpbDwcyWGAfWLo?due8( xVN7vRa!h=DW?p7Ve7s&kx)5*Pt1&rj77{q763%yEAjvU literal 0 HcmV?d00001 diff --git a/Guia1/src/config/__pycache__/__init__.cpython-314.pyc b/Guia1/src/config/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d1423d83c9ce11a07455fe63adb78e9ec186505 GIT binary patch literal 182 zcmdPq5CH>>P{wCAAftgHh(Vb_lhJP_LlF~@{~09tB>+e`Tg8MH zrxq2*csk`|X6B{DxTF?mm*f}3IF@9VWu_#iq#DNur55MslpbDwcyWGAfWLo?due8( zVN7vRa!hi5URq{)OniK1US>&ryk0@&Ee@O9{FKt1RJ$Tppz$DA6oVL_m>C%viRpncWmr^ynO(d7gPc=bgE7YP^8NdjH}4>Yms? zOL4sP5gA?*q=%LfRxAXlf-WMgoo~#4QYNHD8Ha#| zei#X}8i|B$2cD#cN161Jd0I4!HD~IJv!r3+R9}r~n2t5-59;KOb$>Rk5W>L$>JkTl z(Z?O|IU($%=zJ)<|K1dE`-c6H`;6PxOh9Sq*$tmRg{W=Ufp)3qc>Z;QeI5m!!@a|I zk=apKu(A^1iV2<<#N?K-X>64?OWTj$cK2TmCT_j9n0$#WZB=E( qW1ih5H=Pw{sd+P>=`^bdDIc3p3izfeit-7K{m@lqdWZyMF+Ty7ld6UQ literal 0 HcmV?d00001 diff --git a/Guia1/src/models/__pycache__/__init__.cpython-314.pyc b/Guia1/src/models/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..96ae7fb6ec84d5aafe7d42e07b5f97e5836f77e8 GIT binary patch literal 182 zcmdPq5CH>>P{wCAAftgHh(Vb_lhJP_LlF~@{~09tB>+e`Tg8MH zrxq2*csk`|X6B{DxTF?mm*f}3IF@9VWu_#iq#DNur55MslpbDwcyWGAfWLo?due8( zVN7vRa!hW1N@`AVOniK1US>&ryk0@&Ee;!?U};XOT@fqLc#tcKL5xq#jEsy$%s>_Z DzacK! literal 0 HcmV?d00001 diff --git a/Guia1/src/models/__pycache__/record.cpython-314.pyc b/Guia1/src/models/__pycache__/record.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4814a5ec77bd2a6a910aac26298533720656b5a3 GIT binary patch literal 1694 zcmbtUO>7%Q6n?YopN-?h2_|)#x+2sP!4S$%dO%f+_^Cv>q-6_sB;1Y`??&CQUYnUQ zYAsdr0SO{ia4jc5NKl_RA;GOx4_tkW_JrWX&6SbTBk#?wjaz!_ljhC)nR(xvH*e;} z#f2Oo{rJo3-k%KMFH$CyN`&zm3Vm3CXTgRN*zA-MdlcA41*&FqvV-Qt2ji=#IJoaJ zC}x8)t}taDfi~1&i`lFMfn^)WmsIL<+BQpYDv6>psHbN1Eb8eQ-Lf+!P;+)xX*JaNZ0YDP6y*IF}UT zME{>{oZdOz_)vO6Hr{(X@cqzjob%)-s@-Y4rTou)*KxfqZhNvFbnpFi?}v8d;)M&1 zce}pxYD0=?dc9ge^}Xd(ekNFw^VziG5!g z(KNWEr{A`;ag3{=Ik|39Xfu|`8oz)-AL>gXSvA$y!%2Ce{z^zIHQ7*s0oxv!kxAuB zb6qcsPYRZbQql`{1>K3r60?v=LVXTNET9lsocp1VrHcyFBgF=NoMl|ohl67IrggA# z^k(j0^_j00#1dNdwd1Ub^3&PJja!3@iDP!EfOrb^DicrRG+?5HlccRlE(V@rF3G}o zDPuX0tPg(_ieF#bzxHL}iIG<@o;XI4|HIX_iRKevDk7oE{1m#k-y2#-pBVed~ z?B(Lzp7-6eJ!^BF%5!$3m*3c2PqIbL*rHqdez_xi!|v0LZ9 zJ^#)5Tc^Ls0|DCp;gxvsG368ky=R5ZEjcd1Vect zOpbGWx!r(de6gQ)ogncPIN$Y!RDmCQVH@*W#m$k`5$%pA)OAs&fYVnZD&$?k9|a%0 z-{g)`!vDCdJc)sqXa1a~7X+MRPgA?-rg~F9EP*!zno*<4IS4T2p&Y>OZGnYbWth?mf% Vlc7gFG8tosMv?8XF%Z{V{smE%TTB1| literal 0 HcmV?d00001 diff --git a/Guia1/src/repositories/__pycache__/__init__.cpython-314.pyc b/Guia1/src/repositories/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ab190b0c7c4889975a7f783a5ba85a0a67d566ba GIT binary patch literal 188 zcmdPq5CH>>P{wCAAftgHh(Vb_lhJP_LlF~@{~09tB@9S7Tg8MH zrxq2*csk`|X6B{DxTF?mm*f}3IF@9VWu_#iq#DNur55MslpbDwcyWGAfWLo?due8( zVN7vRa!gTbL4I*&Nq$jgYH>__d}dx|NqoFsLFFwDo80`A(wtPgB37U!Ah#5Q7@wFK K85xV1fh+(W9x;Oe literal 0 HcmV?d00001 diff --git a/Guia1/src/repositories/__pycache__/abstract_repository.cpython-314.pyc b/Guia1/src/repositories/__pycache__/abstract_repository.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dccf22f864d9e61fd14f7fe2dea8808b5be38412 GIT binary patch literal 1057 zcmZuvO;6N77=EYit_4|8K18Ak3JFPr!5BCgLyUllkQmS!x!7egv_q(Nw>UFv!fg*G z{sldoX!rsC0&f0$}9r)OpM!s1faFE+JNA~X$Y zw$msqO-tN!HVShcA5*D2>oChGZ{qqk;_T$lnBUE-NZR1TBC;!Qol3lPOxC&4{;;i)1Tqc}92Ycic^ ztE=SE$GK=WQzMMzJR85H;EM+I!}Ku1SsPA)Rcc2BUuKI-4WJ^V;N5e{T9k`Q)q~(! zio&TugrcOWsj{T1mO_Qw+1Mq;lqzyp{REVqWWG?4T(V#whId7y)>lL> nx-J?nHs{WlEk0HgXh)+iHgjk*#(u%%vE#DZ=b;n8tBCy%?6~@n literal 0 HcmV?d00001 diff --git a/Guia1/src/repositories/__pycache__/record_repository.cpython-314.pyc b/Guia1/src/repositories/__pycache__/record_repository.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9269d19e7af2e05b689bcd427e690046e9c3afc1 GIT binary patch literal 2206 zcmbtV-ES0C6hHH|-LAB|P|CKnQ2Jq$5nLs0j2{FjC{i({j-fobFd23S8J+H|_uefj zp=KXUERhI^1bq@>5c>uXglEG)fMOITl3?_O#D|t`D7<;jo!w=FjXrpjIrrRi&zXD9 zxxaJn#@3bu;P~!Z#|M9@0DsV;HjFA$*^W#Z4uYkO!fr(TXSFlGjG{-G(rF zpdEU_($ioW2b5tgvKKKc)(>XWeOj7IxN77O7tF~C%Pj`n69Jn>Y0PqS0m~zo*yZ`| z+kumJSuXlRMA1DtDsSQBphE{hC>99ypdqw15WoZsDVCB3KW?c=Q$n}2Gzde=(J7Q0 zQjQLZrU?*ns|g*8hpFi7C;sFVJc2$NbS{Nb*??q=QpR%*sxN;6kVk1ZyrUe!Q{PqI zgBKM1n|h!0!{);ngk5yRkvS#QFkVv)V}}Da>4>n!wsXG2dEUzj+vcR-S6;Mn+WBgB zA9oqgj_&Y1uaM8a?(z>sP|S`9Z_3MKN``F9<$+(iarwr#L3Vua-t3zt&)J;iESF`q ze0na&1*Oxj%ce8M>2QT@JB30Z9O2q_xbEq&GmYC#=nr3wq!dZ=qWkOF;6;o{lkoKDRVP8 z$dbsTitQCV%vjjDjN{Rkbh;I_%W!*b|H7U}~Z2I*UG=&l%AXX5vC*J*80gO;{0 zhfedahR;o(-Fji@!qlbLu4q?AueJWNV%wc`*8-`|>L**~~aQQ^bt!EYyX|CK>r|s;mVkM$oDjtPGx|NV*b98|~D~#V`^JfaL2TIYszu zL{<#dCI0RqgDpiFuNMdks$;l1Bfgf!Wdr7R66W@=S`MZE_UBjdg!g-?jiD|1^wjuI zTa2L__KW~bsEE!to}jZ4dyMj8>Y?EWM_ndC#nz#SttWW|FE)sLsIUlIgH>$Bxa+Xo zkud#_ijOvA*7u_&)_bz;)UgxCK07|E-%EF$pZQ{@qG_qbo%F!zUferBb!O`P@iWKI ze*AOt+3#0h@4VW1z2|Drt<5`rG}^X8lB-LlU-Q z^T$@~YwT&qb#GG~jcQajI`}!-K)_ZXsMrsu-Z_bF`oq9kqvc zl9z;sV%f#1((!J|9eK5CH>>P{wCAAftgHh(Vb_lhJP_LlF~@{~09tB?w43Tg8MH zrxq2*csk`|X6B{DxTF?mm*f}3IF@9VWu_#iq#DNur55MslpbDwcyWGAfWLo?due8( zVN7vRatu&sS!Qx-aZG%CW?p7Ve7s&kK FEC4a$FS`H$ literal 0 HcmV?d00001 diff --git a/Guia1/src/services/__pycache__/record_service.cpython-314.pyc b/Guia1/src/services/__pycache__/record_service.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5a2c760572704bdb8108f1f8ab2213879c6f6b79 GIT binary patch literal 1684 zcma(R%WfP+uzGiPJ$_}39X}urc4En7NzBQOWYOkjn?;MQcOjR(d%OLwoO zL>%^jM2;X1h+85gz#qUJ$vKbsp_igntIpViNQX= z^W!fQpZ>}K{v@J(to+iN#Y+>`z-7zu4%2K3M&S&&g>i6;i-qi!F1XeVR7wwttgv** z6+t3HSJV>~=|tWLvUeHGm7w)5(mwwDW6;b6U05sX;us`Qg$3raafod)6Ax+2EsldO z8NOxsCBrYfW!+N+ZM!{a-cAQIyVr!=iUJW}Oh6wUGFrs1HI3j3Yod|0!qS%jA#x|- zJ+^@+ud$Edd4|6#Jf!z&?|KvqUibCJHcN|2%Ss_FuP1WT*XbbVLF_9PMuFyB(fhN_ zzduCu#!KD@N=W4`zaB?XE%e?L>NB0xz1MWK6@`8%7Pu=^5;t~!+W8^zRzgA1MuLbAnGe?4*@0OL&3w08~lO`MhD_wp};LeOe zE#yPcE&>sh7yRtJ)?PbjP-Tnxy$ZDs*M%9_W-FC~B*nC!%TAp!Kb8{ly0lSZaxyzB z2kDi~c~pxqQ|Z`ozcaq^*}J_ZEeFBp{h^PjzN3-vI$>^a1A!ZDhYDqC%Pp0N#VC)R}}9Dop= znFOy>0Jqac8w`tObt&f8Rf_7^IqW@}#}pb9_~3nvJDCo}^!0`c{OPE+wR0zq!lYuQ zB^=MFp7vG!xFOz>GLczz%S!k%*uX?lZRI3_?#7g7kddA|TGdR&WbQ>2H{pRZ^0of% z<~KLL{o=O#ZM<@Ccyjmj<^AEM-GQa9LeK6wv$yrWb79xMU{Z@6MJ={c>7@av`I|Jj zeVlJ5VFNb{r;+}=;m5he;e0)kO2<)6)Dq6+C@RaR2^cp=ana!PM*;UWMti-Xg~Fda z)wPNOfz8U%uIUJ>)vH-U(0oY(R2TIuf+t^LWCs>*hDWp>{wlN5+(8+e;~(k}5CH>>P{wCAAftgHh(Vb_lhJP_LlF~@{~09t#UDsGTg8MH zrxq2*csk`|X6B{DxTF?mm*f}3IF@9VWu_#iq#DNur55MslpbDwcyWGAfWLo?due8( zVN7vRa!hGSW=?TTe0*kJW=VX!UP0w84x8Nkl+v73yCPPg=^#fGgBYKf85tRin1L(+ Dj4LjM literal 0 HcmV?d00001 diff --git a/Guia1/src/utils/__pycache__/file_loader.cpython-314.pyc b/Guia1/src/utils/__pycache__/file_loader.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..44e2c0324810fb0a5a426e9e6504b90201813453 GIT binary patch literal 1474 zcmY*ZO>Ep$5T0jyH@lmp$u4X+Q8tOwNJXovMUY4h$tIBBsuD_zDY@Y0$=FU~aO~~# za|DIM3L$Z=#HDZn35lOuBtR-RLN6R6MF`2{fW)b{qK!yJFU)v%A$?wt=goX?X5Rel zg){vH!1dJ+LvR1W0RE&+cWE7_bq<*(Y=XhAf`l?mfT2{tP^XnPLNbF*LQQ)}+jYAw zY*V$fY1NUj(8^6fG)QGrmFigt;ik3$Q+U1tevYjAgVUWOGGnMRy8*Jt(9n7&DXe+E z`)X)8uCOsypqCa!L4H~~k~(X`25c%b9|1Tht-@<;8|}Z&-h}5VO64Kpll&GM;|)u0 z*I1%PQq+_ryA_HZOD6rC+rAY=j%Q2GBf_6+9vk>so;BZyToIX9mwe9)9CO8u-jQL$ zT$J9f=U`}4+;F4Nj~{&f;LFgwe(johCHAZr%t+W~EImImQ^$UKjZ2O9l0MF@APA); zUCxur|98A}T9HNP(X&V%H{k?wnT5ZIe7c-|S}2D!CB)F`!51%|YfWR4nlJ)pH(8h* z0}E*=mpi4IW1PiGta9ikzQN2+VF9jdslBJj1Z*&}ORJ%n>`Iv0|w7On1DD z!zAc%0iNq%Z=r`Oc*;18!g%@^>Lkpmqu?2FKIn_>GVFn>l6G;;uX+8Ocqhk54_l3Q8<+oz}*JAxA?P$3A`PgS; z-)M)!7w=||DkJySKKbAz1EqOH;P0?le{!6K!HHvF`BipQ8aWudd_TYU{y}y5u(Wc} zw{kQvM7o7kHTC`SefHhj-poO@eps46=$n7?NJZs8(MuS_hi4au;NHZdmie)O+`iVk zSkCVcXvCLQqC*o)6?K1>5uK|nmDHb08RWl(^;#~;aUNJZG&u#%u}CovHK&=n9b0}! zGr;+lCnD*4fg6ORj&RyMd&iaAp(CmoMN-B&Pleo)Qh0C0(v1*VJoQgtyS~qPjR|VD zi2DDPG~I%dpAzczsDkA2$C%tdGQ|@GhqR?aw)8h~;!GD=Qv?lj`{)>Psj6MQdix#= b;yfzIPgFvJIb-a1D4b}$Y@r26(lh@8dihh2 literal 0 HcmV?d00001 diff --git a/Guia1/tests/__pycache__/__init__.cpython-314.pyc b/Guia1/tests/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..edb1c8f3fa925e5e55f32c51d14090966404943e GIT binary patch literal 177 zcmdPq5CH>>P{wCAAftgHh(Vb_lhJP_LlF~@{~09t#T!UCTg8MH zrxq2*csk`|X6B{DxTF?mm*f}3IF@9VWu_#iq#DNur55MslpbDwcyWGAfWLo?due8( zVN6MCaY=DZe0*kJW=VX!UP0w84x8Nkl+v73yCPPg$sh+5gBYKf85tRin1L(+vw1Av literal 0 HcmV?d00001 diff --git a/Guia1/tests/__pycache__/test_runner.cpython-314-pytest-9.0.3.pyc b/Guia1/tests/__pycache__/test_runner.cpython-314-pytest-9.0.3.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a80cdccfba4423a8dda9aa650f73d88553118758 GIT binary patch literal 5567 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 str: + return self.explicacao_geral + + 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 d60d625..75a6368 100644 --- a/Guia3/src/perguntamultiplaescolha.py +++ b/Guia3/src/perguntamultiplaescolha.py @@ -1,19 +1,31 @@ from typing import List, Tuple, Dict +from pergunta import Pergunta -class PerguntaMultiplaEscolha: - def __init__(self, texto: str, alternativas: List[Tuple[str, bool]], explicacao_geral: str = ""): - self.texto = texto +class PerguntaMultiplaEscolha(Pergunta): + def __init__(self, texto: str, alternativas: List[Tuple[str, bool]], explicacao_geral = None): + super().__init__(texto, explicacao_geral) self.alternativas = alternativas self.explicacao_geral = explicacao_geral + def _obter_texto_correta(self, alternativa): + if isinstance(alternativa, tuple): + return alternativa[0], alternativa[1] + if isinstance(alternativa, dict): + return alternativa.get("texto"), alternativa.get("correta", False) + if hasattr(alternativa, "texto") and hasattr(alternativa, "correta"): + return alternativa.texto, alternativa.correta + return None, False + def validar_resposta(self, resposta) -> bool: if isinstance(resposta, int): if 0 <= resposta < len(self.alternativas): - return self.alternativas[resposta][1] + _, correta = self._obter_texto_correta(self.alternativas[resposta]) + return correta return False if isinstance(resposta, str): - for texto, correta in self.alternativas: + for alternativa in self.alternativas: + texto, correta = self._obter_texto_correta(alternativa) if texto == resposta: return correta return False @@ -23,7 +35,8 @@ def validar_resposta(self, resposta) -> bool: for item in resposta: if isinstance(item, int): if 0 <= item < len(self.alternativas): - selecionadas.add(self.alternativas[item][0]) + texto, _ = self._obter_texto_correta(self.alternativas[item]) + selecionadas.add(texto) else: return False elif isinstance(item, str): @@ -31,11 +44,18 @@ def validar_resposta(self, resposta) -> bool: else: return False - corretas = {texto for texto, correta in self.alternativas if correta} + corretas = {texto for alternativa in self.alternativas if (texto := self._obter_texto_correta(alternativa)[0]) is not None and self._obter_texto_correta(alternativa)[1]} return selecionadas == corretas return False + def get_alternativa_correta(self): + for alt in self.alternativas: + _, correta = self._obter_texto_correta(alt) + if correta: + return alt + return None + def get_explicacao(self) -> str: return self.explicacao_geral diff --git a/Guia3/src/questionario.py b/Guia3/src/questionario.py index b96937d..36b8f79 100644 --- a/Guia3/src/questionario.py +++ b/Guia3/src/questionario.py @@ -1,17 +1,13 @@ -from typing import List, Tuple, Dict +from typing import List +from src.tentativaquestionario import TentativaQuestionario class Questionario: - def __init__(self, resposta_esperada=None, case_sensitive: bool = False): - self.resposta_esperada = resposta_esperada - self.case_sensitive = case_sensitive - self.perguntas = [] + def __init__(self, titulo: str): + self.titulo = titulo + self.perguntas: List = [] - def adicionar_pergunta(self, pergunta): + def adicionar_pergunta(self, pergunta) -> None: self.perguntas.append(pergunta) - def validar_resposta(self, texto: str) -> bool: - if self.resposta_esperada is None: - return True - if self.case_sensitive: - return texto == self.resposta_esperada - return texto.lower() == self.resposta_esperada.lower() \ No newline at end of file + def criar_attempt(self, usuario: str): + return TentativaQuestionario(questionario = self, usuario = usuario) \ No newline at end of file diff --git a/Guia3/src/resposta.py b/Guia3/src/resposta.py index 7599202..a6c5a3b 100644 --- a/Guia3/src/resposta.py +++ b/Guia3/src/resposta.py @@ -8,4 +8,4 @@ def __init__(self,pergunta, esta_correta: bool, pontuacao_obtida: float): self.pontuacao_obtida = pontuacao_obtida def calcular_pontuacao(self): - pass #VER AMANHÃ!!!! \ 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 7361a4d..7ee4d84 100644 --- a/Guia3/src/respostadiscursiva.py +++ b/Guia3/src/respostadiscursiva.py @@ -1,5 +1,11 @@ from typing import List, Tuple, Dict +from src.pergunta import Pergunta class RespostaDiscursiva: - def __init__(self, texto_resposta): - self.texto_resposta = texto_resposta \ No newline at end of file + def __init__(self, pergunta, texto_resposta: str): + self.pergunta = pergunta + self.texto_resposta = texto_resposta + self.esta_correta = pergunta.validar_resposta(texto_resposta) + + def calcular_pontuacao(self) -> float: + 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 ad946d3..c78ae52 100644 --- a/Guia3/src/respostaobjetiva.py +++ b/Guia3/src/respostaobjetiva.py @@ -1,6 +1,11 @@ from typing import List, Tuple, Dict class RespostaObjetiva: - def __init__(self, indice_escolhido, alternativa_selecionada): + def __init__(self, pergunta, indice_escolhido: int): + self.pergunta = pergunta self.indice_escolhido = indice_escolhido - self.alternativa_selecionada = alternativa_selecionada \ No newline at end of file + self.alternativa_selecionada = pergunta.alternativas[indice_escolhido] + self.esta_correta = pergunta.validar_resposta(indice_escolhido) + + def calcular_pontuacao(self) -> float: + 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 9947dd1..047ad40 100644 --- a/Guia3/src/tentativaquestionario.py +++ b/Guia3/src/tentativaquestionario.py @@ -1,4 +1,38 @@ -from typing import List, Tuple, Dict +from typing import List, Tuple +from datetime import datetime +from src.perguntamultiplaescolha import PerguntaMultiplaEscolha +from src.respostaobjetiva import RespostaObjetiva +from src.respostadiscursiva import RespostaDiscursiva class TentativaQuestionario: - pass \ No newline at end of file + def __init__(self, questionario, usuario: str): + self.questionario = questionario + self.usuario = usuario + self.data_inicio = datetime.now() + self.data_fim = None + self.respostas: List = [] + self._finalizado: bool = False + + def registrar_resposta(self, indice_pergunta: int, valor) -> None: + pergunta = self.questionario.perguntas[indice_pergunta] + + if isinstance(pergunta, PerguntaMultiplaEscolha): + resposta = RespostaObjetiva(pergunta = pergunta, indice_escolhido=valor) + else: + resposta = RespostaDiscursiva(pergunta = pergunta, texto_resposta=valor) + + self.respostas.append(resposta) + + def calcular_pontuacao(self) -> float: + return sum(r.calcular_pontuacao() for r in self.respostas) + + def finalizar(self) -> Tuple[float, str]: + self._finalizado = True + self.data_fim = datetime.now() + pontuacao = self.calcular_pontuacao() + total = len(self.questionario.perguntas) + feedback = f"Você acertou {pontuacao:.0f} de {total} perguntas." + return pontuacao, feedback + + def is_finalizado(self) -> bool: + return self._finalizado \ No newline at end of file From be8e7d2e5bacef3504f8ab5350580122632c6443 Mon Sep 17 00:00:00 2001 From: RaimundoValter Date: Fri, 12 Jun 2026 15:10:19 -0300 Subject: [PATCH 3/4] Guia 4 --- 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 f6712dbc23d156c6a5587ca8b830e4dd42848de2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Pedro?= Date: Fri, 12 Jun 2026 21:47:36 -0300 Subject: [PATCH 4/4] JOAO PEDRO_GUIA4_OK_12062026 --- Guia4/main.py | 25 ++++++++++- Guia4/src/__init__.py | 4 +- Guia4/src/alternativa.py | 5 ++- Guia4/src/correcao.py | 26 ++++++++++- Guia4/src/llmservice.py | 54 ++++++++++++++++++++++- Guia4/src/pergunta.py | 19 +++++++- Guia4/src/perguntadiscursiva.py | 22 +++++++++- Guia4/src/perguntamultiplaescolha.py | 65 +++++++++++++++++++++++++++- Guia4/src/questionario.py | 13 +++++- Guia4/src/resposta.py | 11 ++++- Guia4/src/respostadiscursiva.py | 14 +++++- Guia4/src/respostaobjetiva.py | 12 ++++- Guia4/src/tentativaquestionario.py | 38 +++++++++++++++- 13 files changed, 285 insertions(+), 23 deletions(-) diff --git a/Guia4/main.py b/Guia4/main.py index e9e667d..0185df4 100644 --- a/Guia4/main.py +++ b/Guia4/main.py @@ -1,8 +1,29 @@ -from Guia3.src import * +from src.perguntadiscursiva import PerguntaDiscursiva +from src.correcao import Correcao def main(): - pass + perguntas_e_respostas = [ + (PerguntaDiscursiva("O que é POO?", resposta_esperada="Programação Orientada a Objetos"), "Programação Orientada a Objetos"), + (PerguntaDiscursiva("O que é POO?", resposta_esperada="Programação Orientada a Objetos"), "Banco de dados"), + (PerguntaDiscursiva("O que significa CPU?", resposta_esperada="Central Processing Unit"), "Central Processing Unit"), + (PerguntaDiscursiva("O que significa CPU?", resposta_esperada="Central Processing Unit"), "Memória RAM"), + (PerguntaDiscursiva("Explique encapsulamento."), "É quando escondemos os detalhes internos de uma classe."), + (PerguntaDiscursiva("Explique herança."), "É quando uma classe filha herda atributos e métodos da classe mãe."), + (PerguntaDiscursiva("Sigla CPU", resposta_esperada="Central Processing Unit"), "Central Processing Unit"), + ] + for i, (pergunta, resposta_aluno) in enumerate(perguntas_e_respostas, 1): + print(f"=== Pergunta {i} ===") + print(f"Pergunta: {pergunta.texto}") + print(f"Resposta: {resposta_aluno}") + + resultado = Correcao.corrigir_discursiva(pergunta, resposta_aluno) + + print(f"Correta: {resultado['correta']}") + print(f"Pontuação: {resultado['pontuacao']}") + print(f"Feedback: {resultado['feedback']}") + print(f"Explicação:{resultado['explicacao']}") + print() if __name__ == "__main__": main() \ No newline at end of file diff --git a/Guia4/src/__init__.py b/Guia4/src/__init__.py index 9283af9..5435a81 100644 --- a/Guia4/src/__init__.py +++ b/Guia4/src/__init__.py @@ -6,4 +6,6 @@ from .resposta import Resposta from .respostadiscursiva import RespostaDiscursiva from .respostaobjetiva import RespostaObjetiva -from .tentativaquestionario import TentativaQuestionario \ No newline at end of file +from .tentativaquestionario import TentativaQuestionario +from .llmservice import LLMService +from .correcao import Correcao \ No newline at end of file diff --git a/Guia4/src/alternativa.py b/Guia4/src/alternativa.py index 4dde61f..cdae2b5 100644 --- a/Guia4/src/alternativa.py +++ b/Guia4/src/alternativa.py @@ -1,4 +1,7 @@ from typing import List, Tuple, Dict class Alternativa: - pass \ No newline at end of file + def __init__(self, texto: str, correta: bool, explicacao: str = None): + self.texto = texto + self.correta = correta + self.explicacao = explicacao \ No newline at end of file diff --git a/Guia4/src/correcao.py b/Guia4/src/correcao.py index bdf2fa4..9a1ee3f 100644 --- a/Guia4/src/correcao.py +++ b/Guia4/src/correcao.py @@ -1,4 +1,26 @@ -from typing import List, Tuple, Dict +from src.llmservice import LLMService class Correcao: - pass \ No newline at end of file + + @staticmethod + def corrigir_discursiva(pergunta, resposta_aluno: str, service: LLMService = None) -> dict: + if service is None: + service = LLMService() + return service.corrigir_resposta(pergunta, resposta_aluno) + + @staticmethod + def criar_prompt_correcao(pergunta, resposta_aluno: str) -> str: + esperada = getattr(pergunta, 'resposta_esperada', None) + partes = [ + "Você é um corretor de questões discursivas.", + f"Pergunta: {pergunta.texto}", + f"Resposta do aluno: {resposta_aluno}", + ] + if esperada: + partes.append(f"Resposta esperada: {esperada}") + partes.append( + 'Avalie a resposta e retorne APENAS um JSON válido com as chaves: ' + '"correta" (bool), "pontuacao" (float entre 0.0 e 1.0), ' + '"feedback" (str) e "explicacao" (str). Sem texto adicional.' + ) + return "\n".join(partes) \ No newline at end of file diff --git a/Guia4/src/llmservice.py b/Guia4/src/llmservice.py index e6e91b5..f8758a7 100644 --- a/Guia4/src/llmservice.py +++ b/Guia4/src/llmservice.py @@ -1,4 +1,56 @@ from typing import List, Tuple, Dict +import os +from groq import Groq + class LLMService: - pass \ No newline at end of file + def __init__(self, api_key: str = None, model: str = "llama-3.3-70b-versatile"): + self.api_key = api_key or os.environ.get("O PRIMEIRO MODO DE TESTE É CONFIGURAR SUA CHAVE API PARA VARIÁVEL LOCAL. A SEGUNDA FORMA É SUBSTITURI TODO ESSE TEXTO ESCRITO AQUI POR SUA CHAVE API") + self.model = model + self._client = Groq(api_key=self.api_key) + + def corrigir_resposta(self, pergunta, resposta_aluno: str) -> dict: + prompt = self._montar_prompt(pergunta, resposta_aluno) + resposta_bruta = self._fazer_chamada_api(prompt) + try: + import json + return json.loads(resposta_bruta) + except Exception as e: + self._tratar_erro(e) + return { + "correta": False, + "pontuacao": 0.0, + "feedback": "Não foi possível avaliar a resposta.", + "explicacao": str(e) + } + + def _montar_prompt(self, pergunta, resposta_aluno: str) -> str: + esperada = getattr(pergunta, 'resposta_esperada', None) + partes = [ + "Você é um corretor de questões discursivas.", + f"Pergunta: {pergunta.texto}", + f"Resposta do aluno: {resposta_aluno}", + ] + if esperada: + partes.append(f"Resposta esperada: {esperada}") + partes.append( + 'Avalie a resposta e retorne APENAS um JSON válido com as chaves: ' + '"correta" (bool), "pontuacao" (float entre 0.0 e 1.0), ' + '"feedback" (str) e "explicacao" (str). Sem texto adicional.' + ) + return "\n".join(partes) + + def _fazer_chamada_api(self, prompt: str) -> str: + try: + completion = self._client.chat.completions.create( + model=self.model, + messages=[{"role": "user", "content": prompt}], + temperature=0.2, + ) + return completion.choices[0].message.content + except Exception as e: + self._tratar_erro(e) + return '{"correta": false, "pontuacao": 0.0, "feedback": "Erro na chamada à API.", "explicacao": ""}' + + def _tratar_erro(self, e: Exception) -> None: + print(f"[LLMService] Erro: {type(e).__name__}: {e}") \ No newline at end of file diff --git a/Guia4/src/pergunta.py b/Guia4/src/pergunta.py index 5b3763d..34e4b74 100644 --- a/Guia4/src/pergunta.py +++ b/Guia4/src/pergunta.py @@ -1,4 +1,19 @@ from typing import List, Tuple, Dict +from abc import ABC, abstractmethod -class Pergunta: - pass \ No newline at end of file +class Pergunta(ABC): + def __init__(self, texto: str, explicacao_geral = None): + self._texto = texto + self._explicacao_geral = explicacao_geral + + @abstractmethod + def validar_resposta(self, resposta) -> bool: + pass + + @abstractmethod + def get_explicacao(self) -> str: + pass + + @abstractmethod + def get_tipo(self) -> str: + pass \ No newline at end of file diff --git a/Guia4/src/perguntadiscursiva.py b/Guia4/src/perguntadiscursiva.py index f4c26af..127c1fb 100644 --- a/Guia4/src/perguntadiscursiva.py +++ b/Guia4/src/perguntadiscursiva.py @@ -1,4 +1,22 @@ from typing import List, Tuple, Dict +from src.pergunta import Pergunta -class PerguntaDiscursiva: - pass \ No newline at end of file +class PerguntaDiscursiva(Pergunta): + def __init__(self, texto, explicacao_geral=None, resposta_esperada: str = None): + super().__init__(texto, explicacao_geral) + self.resposta_esperada = resposta_esperada + + @property + def texto(self): + return self._texto + + def validar_resposta(self, texto: str): + if self.resposta_esperada is None: + return False + return texto.strip().lower() == self.resposta_esperada.strip().lower() + + def get_explicacao(self) -> str: + return self._explicacao_geral + + def get_tipo(self): + return "discursiva" \ No newline at end of file diff --git a/Guia4/src/perguntamultiplaescolha.py b/Guia4/src/perguntamultiplaescolha.py index bcbe94d..16300ca 100644 --- a/Guia4/src/perguntamultiplaescolha.py +++ b/Guia4/src/perguntamultiplaescolha.py @@ -1,4 +1,65 @@ from typing import List, Tuple, Dict +#from pergunta import Pergunta +from src.pergunta import Pergunta +from src.alternativa import Alternativa -class PerguntaMultiplaEscolha: - pass \ No newline at end of file +class PerguntaMultiplaEscolha(Pergunta): + def __init__(self, texto: str, alternativas: List[Tuple[str, bool]], explicacao_geral = None): + super().__init__(texto, explicacao_geral) + self.alternativas = alternativas + self.explicacao_geral = explicacao_geral + + def _obter_texto_correta(self, alternativa): + if isinstance(alternativa, tuple): + return alternativa[0], alternativa[1] + if isinstance(alternativa, dict): + return alternativa.get("texto"), alternativa.get("correta", False) + if hasattr(alternativa, "texto") and hasattr(alternativa, "correta"): + return alternativa.texto, alternativa.correta + return None, False + + def validar_resposta(self, resposta) -> bool: + if isinstance(resposta, int): + if 0 <= resposta < len(self.alternativas): + _, correta = self._obter_texto_correta(self.alternativas[resposta]) + return correta + return False + + if isinstance(resposta, str): + for alternativa in self.alternativas: + texto, correta = self._obter_texto_correta(alternativa) + if texto == resposta: + return correta + return False + + if isinstance(resposta, (list, tuple, set)): + selecionadas = set() + for item in resposta: + if isinstance(item, int): + if 0 <= item < len(self.alternativas): + texto, _ = self._obter_texto_correta(self.alternativas[item]) + selecionadas.add(texto) + else: + return False + elif isinstance(item, str): + selecionadas.add(item) + else: + return False + + corretas = {texto for alternativa in self.alternativas if (texto := self._obter_texto_correta(alternativa)[0]) is not None and self._obter_texto_correta(alternativa)[1]} + return selecionadas == corretas + + return False + + def get_alternativa_correta(self): + for alt in self.alternativas: + _, correta = self._obter_texto_correta(alt) + if correta: + return alt + return None + + def get_explicacao(self) -> str: + return self.explicacao_geral + + def get_tipo(self) -> str: + return "multipla_escolha" \ No newline at end of file diff --git a/Guia4/src/questionario.py b/Guia4/src/questionario.py index 7525582..36b8f79 100644 --- a/Guia4/src/questionario.py +++ b/Guia4/src/questionario.py @@ -1,4 +1,13 @@ -from typing import List, Tuple, Dict +from typing import List +from src.tentativaquestionario import TentativaQuestionario class Questionario: - pass + def __init__(self, titulo: str): + self.titulo = titulo + self.perguntas: List = [] + + def adicionar_pergunta(self, pergunta) -> None: + self.perguntas.append(pergunta) + + def criar_attempt(self, usuario: str): + return TentativaQuestionario(questionario = self, usuario = usuario) \ No newline at end of file diff --git a/Guia4/src/resposta.py b/Guia4/src/resposta.py index 846d771..a6c5a3b 100644 --- a/Guia4/src/resposta.py +++ b/Guia4/src/resposta.py @@ -1,4 +1,11 @@ +from abc import ABC from typing import List, Tuple, Dict -class Resposta: - pass \ No newline at end of file +class Resposta(ABC): + def __init__(self,pergunta, esta_correta: bool, pontuacao_obtida: float): + self.pergunta = pergunta + self.esta_correta = esta_correta + self.pontuacao_obtida = pontuacao_obtida + + def calcular_pontuacao(self): + pass \ No newline at end of file diff --git a/Guia4/src/respostadiscursiva.py b/Guia4/src/respostadiscursiva.py index 4ea6dbb..34f2715 100644 --- a/Guia4/src/respostadiscursiva.py +++ b/Guia4/src/respostadiscursiva.py @@ -1,4 +1,14 @@ from typing import List, Tuple, Dict +from src.pergunta import Pergunta +from src.resposta import Resposta -class RespostaDiscursiva: - pass \ No newline at end of file +from src.resposta import Resposta + +class RespostaDiscursiva(Resposta): + def __init__(self, pergunta, texto_resposta: str): + self.texto_resposta = texto_resposta + esta_correta = pergunta.validar_resposta(texto_resposta) + super().__init__(pergunta, esta_correta, 1.0 if esta_correta else 0.0) + + def calcular_pontuacao(self) -> float: + return 1.0 if self.esta_correta else 0.0 \ No newline at end of file diff --git a/Guia4/src/respostaobjetiva.py b/Guia4/src/respostaobjetiva.py index 72ed2d0..e916311 100644 --- a/Guia4/src/respostaobjetiva.py +++ b/Guia4/src/respostaobjetiva.py @@ -1,4 +1,12 @@ from typing import List, Tuple, Dict +from src.resposta import Resposta -class RespostaObjetiva: - pass \ No newline at end of file +class RespostaObjetiva(Resposta): + def __init__(self, pergunta, indice_escolhido: int): + self.indice_escolhido = indice_escolhido + self.alternativa_selecionada = pergunta.alternativas[indice_escolhido] + esta_correta = pergunta.validar_resposta(indice_escolhido) + super().__init__(pergunta, esta_correta, 1.0 if esta_correta else 0.0) + + def calcular_pontuacao(self) -> float: + return 1.0 if self.esta_correta else 0.0 \ No newline at end of file diff --git a/Guia4/src/tentativaquestionario.py b/Guia4/src/tentativaquestionario.py index 9947dd1..047ad40 100644 --- a/Guia4/src/tentativaquestionario.py +++ b/Guia4/src/tentativaquestionario.py @@ -1,4 +1,38 @@ -from typing import List, Tuple, Dict +from typing import List, Tuple +from datetime import datetime +from src.perguntamultiplaescolha import PerguntaMultiplaEscolha +from src.respostaobjetiva import RespostaObjetiva +from src.respostadiscursiva import RespostaDiscursiva class TentativaQuestionario: - pass \ No newline at end of file + def __init__(self, questionario, usuario: str): + self.questionario = questionario + self.usuario = usuario + self.data_inicio = datetime.now() + self.data_fim = None + self.respostas: List = [] + self._finalizado: bool = False + + def registrar_resposta(self, indice_pergunta: int, valor) -> None: + pergunta = self.questionario.perguntas[indice_pergunta] + + if isinstance(pergunta, PerguntaMultiplaEscolha): + resposta = RespostaObjetiva(pergunta = pergunta, indice_escolhido=valor) + else: + resposta = RespostaDiscursiva(pergunta = pergunta, texto_resposta=valor) + + self.respostas.append(resposta) + + def calcular_pontuacao(self) -> float: + return sum(r.calcular_pontuacao() for r in self.respostas) + + def finalizar(self) -> Tuple[float, str]: + self._finalizado = True + self.data_fim = datetime.now() + pontuacao = self.calcular_pontuacao() + total = len(self.questionario.perguntas) + feedback = f"Você acertou {pontuacao:.0f} de {total} perguntas." + return pontuacao, feedback + + def is_finalizado(self) -> bool: + return self._finalizado \ No newline at end of file