From 4d06d6bf27f976ab91b4821993c788bbffaf0c71 Mon Sep 17 00:00:00 2001 From: AdsonEsteves Date: Thu, 11 Mar 2021 02:22:25 -0300 Subject: [PATCH] captura de dados --- UserAgentsServices.xml | 7 + board.bkp | Bin 34899 -> 135714 bytes src/main/java/sacip/rest/RestTutor.java | 13 + .../java/sacip/sti/agents/GrouperAgent.java | 53 +++- .../sacip/sti/agents/PedagogicalAgent.java | 98 ++++-- .../sacip/sti/agents/RecommenderAgent.java | 71 +++-- .../sacip/sti/components/DBConnection.java | 88 ++++-- .../java/sacip/sti/dataentities/Student.java | 4 + .../java/sacip/sti/evaluation/DataHolder.java | 298 ++++++++++++++++++ 9 files changed, 535 insertions(+), 97 deletions(-) create mode 100644 src/main/java/sacip/sti/evaluation/DataHolder.java diff --git a/UserAgentsServices.xml b/UserAgentsServices.xml index a7a60ac..742ebeb 100644 --- a/UserAgentsServices.xml +++ b/UserAgentsServices.xml @@ -45,6 +45,13 @@ mas SACIP + + atualizarTrilha + atualiza a trilha do usuario com um novo conteudo + PedagogicalAgent + mas + SACIP + diff --git a/board.bkp b/board.bkp index d7851fc04184997be743de8b87d3d83abfd9e367..7c4c5b33b6af4320d3fd1d1ecf0163e6b8215efd 100644 GIT binary patch literal 135714 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 literal 34899 zcmeHwd(7)-dROM0nRCvZx$JaynZ550bh_-K-E)%Ij&0hq#IbXA;@oeoc5!SccH&E7 zJ69@b3!+t#R){XGR%Iy^R!gg*N^FZth17-lqm}vxDpLOdq=E_(2wK5^1#uD2>wKS` zC*Rqf>~t#5rG8_IN|s1mZ?g_kGHp<&u4#z5AUQ>(M+$v z13tR!0k7xyWKRAY!=L=j_uol9Q%pUyJykENTIl&ZKlSM+&SZPqB6!p`Kpwv8hB6GN zy?^-G&;I88zrq}H>e*oLYGLZ}lSL7I`Z4hCH8kKpau>{{Y+ZF?8>f>||L&Lm=|6J3pTE{<>hpg-l?qm=zXUUr z|342{dGEDvBZDyY60AH!3k|&&7=8e#`mRJ3SF8KE`~T*jzxmo<`0cx?I|%fZ)bpX~ z8-eNB`ff=?5C1;2eE|P|9sYY?n0l7YWYRF_CjwQomXCtadZeqN3X8!CEhBi;3D-Id zzV>ThGrsiOzx5k;QZE4}EM3)&O6uOyb3-qvq+SjUXX&+#z_Zty>dC2lU^PP34^yum zSBsR9!m%cGIz*cIO zdOhod=rOwLEKI%clRpJRb$^Qf_|Lxb zm4E%(HQR}~49A>j!N9h3) zB{zUqU$4LU^SVVYGCXIr3`r^0;mc1vQNgoqfGj8=c%$b!o^DMoVj0N&{O|XDPv%#C z|4XkNjjIi>T+8&c$=!de36j@8ETn$;x4!U)pNOV;1`PCTCn`(cM(i+|pFH8+=n3zJ zsRvT9^!(5O7kAHD2bQM7LRcDMup@63;8{MH+=^_u3q!wJX!JK5KlqCM&Hn}8I!?Wy zYMK!Qimw}f^vTa%t*}(I!d@Xl-5rhHpNntePIMD@KzQCNbj_?S*nt0s4OZ4?TUlPF zqnRqlm6?q`mWFv=CzFwXfBZ1I7S}Z(;#%$vyJLS^)$Z!f-Mg8jjxU^DM} z!CLRIV8I-KFG*AHdcpqCJ1p2AJtCHl1^XwChJcuYAO53ylpMoQxf}y7gwH(AWV1BI zaLgAoIi6w=1N_~I0Zxp0@J0tB2(Xu)OFYS+`EQ?j<@>(=Gd~@< zfcu7R5ci2=4rMY2KLkzyjJ|FJhHHAk#rpYpWU4<7x5iRTU=`YePQ!qj7Mra@u}jM^ z5WVJpIGMJW4RU4Wtt#_@R4T(@*~yNm+K%hDVNfWH8v~&^6igVfn-y!BrOK8}P?Hzi zYI)|Gc^GWO)vPEn6Q>4)x~*&{X4PUX7*H#CtyvKdIT#qLykm5_yWAKCX2TsY+Z^LN zFqktHXSkjAnnf54y1kXUpJl}%3Is zPb)B}tFGo$X1h9g+J_x!K4ga$8}4B+H<`k6!O26w!NZQ6<|oC8qOD-y(etiCGwob6 z@`PVL+LiY_;Xk_=x0i(&|Key@-ZR+$@*NiJ?;H`!#0LBI#9;q%rvTrnIleTye_OHJ z@4dr<{m)0l=3>FV3p^na{FmNyUbobF{g0z5NMg5lsVR8o^>@}3JoBleDM)PWd!~Sp zdQYYt-iu@Z|HrYWhP?^aXrg?HE#=tsdOQNg=au&Z}iu-l_x`FA~H|LQv| z*q=BGmV4I=_NU)r!G0A;i@P|M0V;m4lQ03aF_L@{aUx}!w=zWc~Gco+X_KSb(<1c*q-~aXN63>z1KdCUp ziv82zlcRrO>M#FMsX8`Ir8%C;G3j*naTYuYo8^J3%VhXmCdYC#M`tKH_eGYXp=Oj# zXL+8Zp+v-d2Ta{_$~yE373WdKOm&Vp zkNo0_FE97=p$&uGcIy@LzSSheR1IoXvv!Z+^yr3uIaU&BSGZEr)0UnOn-yJK)wUr# zz!_WWl?(j3FzaTk(Stya_)v8%=g6jBEbNa)!u}|HxbNk6=|d*N)?v_9>#mnGoC1+A zA|=b-LZ>R6wjRJ0gWhmOepm{ie)cdl9n+cCg(guDQ&P72f@XSzhir`}%ic!x2880A zO$L<5v~<1?gTR@JhUozj34@H~t+>)|H?6{86H0|aW~&Ag3=sX=-MDFC%yuX;!L~1E z7oXo*%-7WayzU(bf~~xYzX5zX|)a+YS3B`SW!GD^)geN z5@9fFQki#dByciIu)elrH#%L(9a1%dxU7aC)Nz}T%-vZ|^&Z3&0|Y6-0H z87LPWYdQugaYwrL38vvCVrqT&syL5r#2>r`YzfBeTvI6a+{#{xD%a2a9LVyS=yOl9 zzb_?{ehQZMnH(qEv0onxssuQ^VPr+IRNfA>Ds?PhCz6corq4N0?*Lx?LBV?S7f2fe zL>1RH>}US(TZl55@72fxyb7YF7n{r`Yg8tr4iJd4x>%Ii8V7?8>yJE3uklc3$4j9t z2%TWJWk#ehk^mkCN3W2q^`cuz$~>5Hr0oQHvxtq%m*To1`?tL?nFb35qb{+j1p(%tYDGMaq5-3ixHHpS}4FpP(iN(!}y?Z$@4^aV4-srtk`o0HY5}eTZ$uPth&jEFyO}}L8T0SOw={m9>iXatJCCdD@&}50%6fz z!$9rr3&jB~EYmOuMze!hGSX9mt7fSLX^XU$c-y3@gK&fuN#Wd?h*4f z@8$DOX*3-X$DkMddS^DQ&56qpq?NcD>4O>Z1x0qrI#RuF5?pNx(xKkcZ?KxbjN?Fa(I!m5_xg+oUZdTx-)RuZUWYz)g?(2aS3HZ}7 zFh7t0^C_@h4~m9kxyP2Eg>KeOWj7mw;W~2w?Y=F}oUE;s&X5{ z*OX1J#f}R@qG7HG#m-ErqYzl3I<7A*0b=D{e*jkov-~2bF6BMJa$(i#Z=17hH4g(6 z*9g-R$MD{wPL@f&SdhBxmd_ItcbGY@U*ii+ayy$!+mt%}#)M3;ta%N2G3_r%<+Z;r z$>j}QwPum%8;Kvi6X}fj(Fc)~AH-OE{i(v@+v-nyI~A-um-h<9`2KcnU3ZQd6Y1yr zOd`?S<#xt)*qjfg{Q@w1BWl@ohE`dhQ*%3l4g_rX3trSE8I3Pl;4ekcdkKJAk`#yT znRJ&#)_$ijFdJ&vBYq>@?^)~8xIQ4O5c&L>oZY$gf)On|rRZfUQ)XfiL$ZQi{r}w2^ULa<_JoOsW>V7Yhu8nrHEU+tiJtN`K zGS|%IxnbV*qDA!M@k;q{qSxDy@t>M?+pl^Pv&I*pk>rMETh;WQwyTHou?IH^DdKqa zD(SygI0@}lA9`^k0sZXT(7&YE+|*uHT%wnCv2O43G({6XyAlc|ZJ>_~qKZRyncW8i z|3K2YE>#@1p&KV4SuAQ&F)FLfCOsLtB!dftRr-)#`3&*r?ex?a2f1zmm}Hnu_W04X z>X9!k3pRwX8LI&_$V11kIdrSKYLc+kZPIyxwQ}8R^mt#3_5aa0N9waqa$yHn~=B0#{IiV=cTwkyEB468QyngPCO)3sHap zB3Uv!YhNnZv`wr_(*FwsHn7XZZdQxjx>K;&t}62VE}88#E!*Z3qrcLZ3-hNUaR(PU3c3}K-V#m0=5^TCeqPlydub`_K68`UVMMj@}dDfq^L zt5PIiY998p%63`G_%J|->13_^o?Y}xWVUA6Z(DU=_ek`KkU5)gwRzJWYa|#(q}ZCy zj$56HxgnM}uh!J@y6&3?80^~4bljZrd-A38Vo#RLO^_pzu|1jPJGIh;BE}Z=J|ZG* z%`B z5ulzuv8*^zzjCN2JkQ-%FGlOj(c~|K)H$Zs+L{}uLDHjLePMP~ZfX+Chqx4!>Y(%} z*f_4-n(H-!a5J6NHXDT=k%41v9KYSlnFLan3Ovuq%eKh(pd218gmQ~7(N5bO6BMC@ zKBW4c-L76xcSP@ZKOF1*)mvuL5Hf7OK0Qz~m4amK%*Uo>i_L%`m+EK8h_j0mgO)^B zcaOUno@}%H)F+?ZN-a6dFxnt=_|*^o^7noH@BD65&WdIHdLrYefPOf03Y+YxG<3Ed z6~c!bp(@{PDMQYd7I9_dYT;bGRz4D~m5%@(QBh+h)vCE!ySXQTM)4%7M@c(~D6NUc ztl(|_$}98DvePBD7_B^;AC_AyzA-1kWgvEDW;sLmSr~W&cRlS)r7F3i6@<2?R@n*3 z_cud9Ud<%RCTr!uq)l<%3!}PMc3D$ubDoPbn%$^vWtOnTiI{9eae1?1b4;bTbHWjz zU|HSo9r;jKOcG3N+6~iYM;b%k#tuD^?yWV21Rz#vl&Mad>l|@g+}v&#h01h4f&nV* zAkHtTzC`P|kT4E+Bb5-w;mT#l@cWZb75q-qq)krXb2iJm@{RSIAC#A7Zq{qhqvuEh zlEktn9P9n4Tn994>i*tahigK+#~afkL0!H@=UxQZd!U6;x|ACz&9+IHr;3wVIon2c zP5@iyl{6d9EtP~7*&?-52F+ka!2tFAtFE8prG8@-IorF(@&jQkUVLgb(prs_&1}5w zL9}sZQB^SwTQh<@r9~R~(us(p7oKc0F<100gj<~!Cz4+43S?RA+`YFHbHzCcYqsee zH>_&%0YcV?1!Xb|Yo(@0mQs=?L<;^ZG2EYC;kHM$de1NFEg(d0nA)JA2$^xSnmIx> z0es8~JnvZHbyeXc^nSsCUFFW3e=mf+mysxb@?v!)3@|b>C>%!CFgwxq0xydhVzN+d zi~^Z3U)c*oSxS>lH4TBG`Q6N!!lJ}>a$Pm)mhDzIggRJH8qM0k$g!lB(6i^8PKOuU zI~XXHNxQM^PPW8Ss~w51tWCB}0D-FE2_WD~lET3tqcr5YH}^KgaUh}?F~G%YZQ9WU z*CcO4ImME$wVBmQ&^vG?F~Km{4`jb{ps93J)4BVV7=@pDs+M?M>FT8eM$FAR41}0x zv2?nm^t-FwbaQNeO8^*KTm-SOyFkM`fqtG2% zT}+|3$_gZA;Huq?Y+0&JW<&e7HoDu{w1nxNR02?(fr?*#gJZOAw;~g^tr7_1T~T2w zBsE0EjE!j(RGXbw3HW-_tlSzD>Lv0SM8YsrbKkD^Op^pec{-R)D@-~|$|(rSOu1rm z!=6|U;mTO98HPFvHZ2%%ZmS~?6;&q>mlS6rb-fv*_tQ@mde0(OQ9ZB@FK$?HxHrtG z8kPd1K5mZ-(ZeJtj?HLXqfK_MBhdbri_PcrQK;}d2yzurx$%}N&j@Rg>G7+HRGn=} zHBBwnp;KmJRft}ZVg1&1Hbh}8>dn@sGD`C#XcfgSH}{lkScw$&-;?m~ScQL5_;<@_ z^w?DC+ zjgFd&P$jCQjQhomJ(-X=4n=Ew2qEoGi;c=(sIE0I#<{~%ELMu}8Q#{srQ2hDLf0qw ziWEAE7xhOIg+2u&AX>9hf41IfTdKCav1ap!-a>W9t!(dX&Bg}qs-+Ej-{#(oWB^#B zkra-F?n?-Q*CM^X27(lYh146A^&;_)sP2I(f0Nc=UsyKGEXfD!-Svc-)HY3$HX4Pp z(CKsGi12`|d|g_mCj*7drnRgCJ=6r7Xz!4>g+_IpUlHQ1v8w6?+8MGWI-ZTZ85LN4 zkt|_tHZZrHLtP{hHDY-J1NI7RY{?<%soZGpW!fFcq~}2wvb9>k1>FSm>F&9{={FrrneF=%)a5XK_5$tV{~QwnvV{ zII|P_#BBDo<0D7_aA$qcC)~(`gd=>|H8l4~pG5Mze8Wt#i=sA5FAMoD zJ0-EyQcd%-P@WbDJo^3eXt~*P0U0=Ar@f?u<(hykEb*OTF)OACzEGl?04KVkV_yfc z{U1t9#clBar;P5%HLcQC=(WHWp1FUeH&g_+Vb|iI{X`{^nlCXBN#6rnk&XIVS+?v& zjh5I2C9H0o^(Da~K$(+o>eZbbGX&a37t>6Tc@_v`Ss zo;MQQ`a0btDPU52g8^zYL=2jfmFkl$x3q{K%2XtMR%^Bd()Zv>UA-bvb+HcvWaChE z0@V{xK^f5jsQ(*}}I7OM}fZ_#m|Q zwb8g3l!@0uJqD-}HLO*|0UR`>c%Vd-7W$3(P|Y5eEUEM&7*9wW-oAd|vMkwpi=|(7(6zCv7=!=yuxr4C26}`owsS{MA^0K>a zwE12k9UUi>lrZ3Jr~?Rj;cBVbG&AE-hukO%o2qlA!|laku^wUS-zHw`UL;=PwXUiH zG2A|Qd+lGynKiL()olFd9^aoe2Eem80=Jxf+$0i8LWS-}!Zxl(A#k$j(!b0$snR|)5H z@4HwPFGSJm3n1f)+uLdNT)s>ib5OfSQXzmVsNx}%8^d&`P&9~HM~8T!4L_*2{@@g* zjjYbk2Jjig6DAB$q}UI{tLgp8YPyZayVK9HqUMWjFfBLOUS**=&^$=No*G2D?ce&GzPY@@9OmoQ6$>QfWnehP+n?~kwBjT=#JIZ znyHIv83qe&VV5&LCz319$lZn^>}7zk_+evGD=41gu8CnqxdEaXr_=dxS1e{c5}2Z9 zwDCZiEumuPLvDw&Thtqe+H;XLM>P+W#GvEp(3Ybie{d?fjL;;}aA4Kk;?M}2>TAVGHN7E4Q?&IHtud)V$x8pQ96{22PoZK?`AfG(K-`j9EE^ z$&)-grq!SOskd19NvM?2>Zs1erE6PRa$SpfFcg=dvrZ8j5i?S3?YSXiP!{3J5MPd> zDU(|7)Ky*P2wR7CD4`=!QIkiq|2oq6-%hfQ`_W^QtmCS!AqK-Ik~rzM1;FyY+o5LV zX?QaLmL#n?lKdcl%sCRR#!xx!>bT}`W5*GR@1I-@m878vnHrRnqH}cyld$=gmVFs+=HZ)GQwIbmAbdf@TqPUsNgr(*~6vcD)*GcZ7% zo9H-U%%`00aY@VuOfcNFT6I@m9nYr7P8`AO_c#fzpmiVwa*5s#6p>gYwDT0z98gab z+JD2d^Mx(&-EA!r{jr0FYlCHJK`*pNM=|&8n*>kIDo%8q5LU}x)^ZA+K z&3qMp=ip-CC0iX4qGWMU?{}!pg2bK(9joom6N>EEaVmZ)m z5@fIt@Dk)k$~_+HN|bxjqI=tH&rWx1qtY^`L*{!!w=^_fGxKq0w~sWLSf4ncit?{} z*E@>hZ5h{wC^1fv#Qx{SfcQX^ntuRPxGK?N-R|lwQg20B2Fk&xak{nHJKF;(@a&m+ zxkgihPU^bojB&Kv4(%sHEN!+i3mhk_m53!m_69YfqZ1gA*HiQTRi%|HDkOzMWdqb7 ziySJ-&XU>@k=(uG!7c=ACuHtz!`jAXy}7-c=@2p8z?zmBE6%|@^v;8#xV<34T4G!x zxqR|sM13ei)Q3PFXicNtw1|XDnzhJhdYaT|FzgyCqz}=4$Lxwy{6SBSs3TNILS1Eu zRO-V9Y|hF_(bj=_D>yjZs3(#7bCuHJ^^T~Jpi0fv`nCFSMf&UUc5HMEl)9cwWSPpd z19%%^usLd!cId&zC&epgon^a%SX+_VkdYfqcV>Co=rg2rfjV??jqBbUNg#IYBYG13 zd(!%FD?DA?u~y*o<;-NehoggT!n0~wRM%-wDd)@-#(~&`i-eXts>Tq6G>)BV}@YjuA^jLp^4biD!$g4a&MswRm|{lkfm2Jwkm2 zd}GvR7d-8gh*LA!+NxhX)Lj^$okz&9pv|Hv^{3i(p%F;bnlJ?D=P(c^8ud|HJB(F{ z_(#o~^mr#fmw-^gn_p3ehvVeaT2#YX?RhAlPDwc3qr8DsG53(ooShnhB6ZZdg$y#< z1c^?zNivUUHBDoPo^Xe^0ivjRNB4bhF`RZ4Xg|5(6SJ!_D{X3)&Yc;ZIR3g0wC=-i zl+D&E7@s)zJK5la&CpL=Y=)A25N%0Lt5dq-RW}0?+#p&UA*NMa_BDZOde8y<5UI5f zUTz{(^ECoF)VyD8Xp@{jfo*^{2po%A3AL~k??ds%9(Q-g_T9glVyWJI> zoun?J76Cy^=b*#BM;Le1p2T)@fitI<2U4`%HU_;$p*O9O2oddiN6lqPhhc=-zZqlp z4KS_IzuR#h>KVCJuL)f$O%GXUwmc(Y{pM7x4@L?zI>T&iSrbQjjoL^yzj84w6GjX5 zKx>>}$YGrxlfE6~+)zOSMdS6L+U}aUU6Is&&<=29Bhlt06fU8|HG_q1j;3%Lh6A$| z%SvWZz~G6%N|`U zlp?Mbf3c#IZXvaAtf#pqpQAvSI-NVrryX}jB3ZOq4Q6h*x3}&7p_5PFuuCH)zpQr)&F(P@e>-rDEf{V9N~)ca zB>zv6Bt0Jql_W`5AB~0l-j8&=_ahxo^N-Ele>sNg$q^$@K@)PEt>?RdLz!bZP-h@1@yX?sm^Ii85QDQR&>M|YqWSQN>loD6jvE~K0sxO>^) zU^cX^BERj}4WBQ2(J3|exx^4Yj1A#c<`Sdp#H;B8{~=>@yu?einDllMm6W)Bux zOX@bEKmMvFa(%GH#Q;nyBIxXYl!Kt7h)_2UN{JB7%~rOXoWhS88nlPfYm}c?N0ok) z&yMpdybUFPa@Y|(VYKB5+n7m*zMSU@&X~Bq`(KRJ`jM;b27a0KHmDV)8#9 z2W*}tHyO$hYm>QFJ{G$Y+9+lL<6qH6Bw%uG^!?4n5O{yYSihf$`Sb*Yd8Mwq`CVl| zC}I?%4*TWmC=l6M4z8e|JVl|fAU3kO;d~U8S<&_>R7621m!TG8bg~L+f=_m&!yTYs zKEMt7r{}Fzilw>KvZV~kq!YUp0qXb*!i0wWu3HQ!%^rpGT=kHL^E7VQim@h7+jg+X z9rx5d0aDj#(`n!1$w}n*zj866lJi?AZ#R)MLuI}Op`#7dsO*ub1Kx%z0|}!UDJt3h za#EkV6$x!@(MxXAX$eNA3(V13Kv72`7mi4*zK0f-Twpi;5ym> diff --git a/src/main/java/sacip/rest/RestTutor.java b/src/main/java/sacip/rest/RestTutor.java index 2574f8e..e229559 100644 --- a/src/main/java/sacip/rest/RestTutor.java +++ b/src/main/java/sacip/rest/RestTutor.java @@ -87,5 +87,18 @@ public String getConteudos(@RequestBody JsonNode dados){ } } + @PostMapping("/atualizarTrilha/{agentPort}") + public String atualizarTrilha(@PathVariable String agentPort, @RequestBody JsonNode dados){ + try { + ServiceWrapper wrapper = AgentServer.require("SACIP"+agentPort, "atualizarTrilha"+agentPort); + wrapper.addParameter("nomeConteudo", dados.get("nomeConteudo").asText("")); + List run = wrapper.run(); + return run.get(0).toString(); + } catch (Exception e) { + LOG.error("Falhou ao atualizar trilha", e); + return "Falhou ao atualizar trilha: \n"+e.getLocalizedMessage(); + } + } + } diff --git a/src/main/java/sacip/sti/agents/GrouperAgent.java b/src/main/java/sacip/sti/agents/GrouperAgent.java index b5024be..065c869 100644 --- a/src/main/java/sacip/sti/agents/GrouperAgent.java +++ b/src/main/java/sacip/sti/agents/GrouperAgent.java @@ -41,13 +41,6 @@ public void provide(String service, Map in, List out) throws ServiceException { } catch (Exception e) { } - if (service.equals("getStudentGroups")) { - try { - - } catch (Exception e) { - LOG.error("ERRO NO AGENT AGRUPADOR", e); - } - } } @Override @@ -57,7 +50,7 @@ protected void lifeCycle() throws LifeCycleException, InterruptedException { { try { - HashMap> studentGroups = findStudentGroup(); + HashMap> studentGroups = findStudentGroup(); Board.setContextAttribute("StudentsGroups", studentGroups); } catch (Exception e) { @@ -73,34 +66,38 @@ public static void main(String[] args) { agent.findStudentGroup(); } - public HashMap> findStudentGroup() + public HashMap> findStudentGroup() { List estudantes = getUsers(); - HashMap> studentGroups = new HashMap<>(); + HashMap> studentGroups = new HashMap<>(); double score = 0.0; - int mean = 5; + int mean = 10; // List docs = Arrays.asList("carros animes youtube História", "comédia animes História Livros", "monstros cultura comédia Tecnologia", "Livros", "mitologia animes", "Livros matemática"); List docs = new ArrayList<>(); for (Student estudante : estudantes) { - docs.add(estudante.getPreferencias().toString().replaceAll("[,\\[\\]]", "")); + docs.add(estudante.getPreferencias().toString().replaceAll("[,\\[\\]]", "")+" "+estudante.getNivelEducacional()+" "+estudante.getGenero()+" "+grupoIdade(estudante.getIdade())); + //System.out.println(estudante.getPreferencias().toString().replaceAll("[,\\[\\]]", "")+" "+estudante.getNivelEducacional()); + //docs.add(estudante.getPreferencias().toString().replaceAll("[,\\[\\]]", "")); } - while(score<0.5) + //while(score<0.5) { + studentGroups.clear(); Lda method = new Lda(); method.setTopicCount((estudantes.size()/mean)+1); method.setMaxVocabularySize(20000); + method.setRemoveNumber(false); LdaResult result = method.fit(docs); for(Doc doc : result.documents()) { List> topTopics = doc.topTopics(1); - int key = topTopics.get(0)._1(); + String key = result.topicSummary(topTopics.get(0)._1()); int studentIndex = doc.getDocIndex(); - if(studentGroups.containsKey(topTopics.get(0)._1())) + if(studentGroups.containsKey(key)) { studentGroups.get(key).add(estudantes.get(studentIndex)); } @@ -111,15 +108,37 @@ public HashMap> findStudentGroup() studentGroups.put(key, grupo); } score+=topTopics.get(0)._2(); - //System.out.println("Doc: {"+doc.getDocIndex()+"}"+" TOP TOPIC: {"+topTopics.get(0)._1()+"}"+" SCORE: {"+topTopics.get(0)._2()+"}"); + //System.out.println("Doc: {"+doc.getDocIndex()+"}"+" TOP TOPIC: {"+result.topicSummary(topTopics.get(0)._1())+"}"+" SCORE: {"+topTopics.get(0)._2()+"}"); } score=score/docs.size(); mean++; } - //System.out.println("Finalizou: "+studentGroups); + System.out.println("Finalizou: "+score+ " Media:"+mean + " Grupos: " +studentGroups.size()); + return studentGroups; } + private String grupoIdade(int idade) + { + if(idade<13) + { + return "menor13"; + } + else if(idade<18) + { + return "13menor18"; + } + else if(idade < 24) + { + return "18menor24"; + } + else if(idade < 30) + { + return "24menor30"; + } + return "maior30"; + } + private List findStudentSimilars(Student alunoRequisitado) { List estudantes = getUsers(); diff --git a/src/main/java/sacip/sti/agents/PedagogicalAgent.java b/src/main/java/sacip/sti/agents/PedagogicalAgent.java index dc41100..2132fa8 100644 --- a/src/main/java/sacip/sti/agents/PedagogicalAgent.java +++ b/src/main/java/sacip/sti/agents/PedagogicalAgent.java @@ -6,7 +6,10 @@ import java.util.Map; import java.util.Map.Entry; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; import org.midas.as.AgentServer; import org.midas.as.agent.board.Board; @@ -23,6 +26,7 @@ import sacip.Launcher; import sacip.sti.dataentities.Content; import sacip.sti.dataentities.Student; +import sacip.sti.evaluation.DataHolder; public class PedagogicalAgent extends Agent implements MessageListener { @@ -63,24 +67,34 @@ private void montarAlunoExemplo() { } private void registrarConteudosDaTrilhaDoAluno() { - try { - ServiceWrapper buscarConteudos = require("SACIP", "findContents"); - buscarConteudos.addParameter("name",this.student.getTrilha().toArray(new String[this.student.getTrilha().size()])); - List resultado = buscarConteudos.run(); - if (resultado.get(0) instanceof List) { - List trilhaConteudos = (List) resultado.get(0); - for (String nome : this.student.getTrilha()) { - for (Content content : trilhaConteudos) { - if (content.getName().equals(nome)) { - trilha.add(content); - continue; + if(student.getTrilha()!=null) + { + List modifiedList = new ArrayList<>(); + try { + ServiceWrapper buscarConteudos = require("SACIP", "findContents"); + buscarConteudos.addParameter("name",this.student.getTrilha().toArray(new String[this.student.getTrilha().size()])); + List resultado = buscarConteudos.run(); + if (resultado.get(0) instanceof List) { + List trilhaConteudos = (List) resultado.get(0); + trilha.clear(); + for (String nome : this.student.getTrilha()) { + for (Content content : trilhaConteudos) { + if (content.getName().equals(nome)) { + trilha.add(content); + modifiedList.add(nome); + continue; + } } } } + student.setTrilha(modifiedList); + } catch (Exception e) { + e.printStackTrace(); + LOG.error("Não conseguiu gerar a trilha", e); } - } catch (Exception e) { - e.printStackTrace(); - LOG.error("Não conseguiu gerar a trilha", e); + } + else{ + student.setTrilha(new ArrayList()); } } @@ -95,6 +109,10 @@ public void provide(String service, Map in, List out) throws ServiceException { out.add(new ObjectMapper().valueToTree(trilha)); break; + case "atualizarTrilha": + out.add(atualizarTrilha((String)in.get("nomeConteudo"))); + break; + case "suggestContent": out.add(suggestContent()); break; @@ -132,15 +150,40 @@ public void boardChanged(Message msg) { } + private Object atualizarTrilha(String nomeConteudo) + { + if(nomeConteudo.equals("")) + { + return new ServiceException("nome de conteudo invalido"); + } + + getAluno().addNovoPassoTrilha(nomeConteudo); + registrarConteudosDaTrilhaDoAluno(); + + try { + ServiceWrapper wrapper = require("SACIP", "editStudentListAttr"); + wrapper.addParameter("name", getAluno().getName()); + wrapper.addParameter("attrName", "trilha"); + wrapper.addParameter("newValue", new TextNode(nomeConteudo)); + return wrapper.run().get(0); + } catch (Exception e) { + LOG.error("ERRO NO TRACKING AGENT AO ENVIAR DADOS de "+trilha, e); + e.printStackTrace(); + return e.getLocalizedMessage(); + } + } + private List getStudentGroup(Student aluno) throws BoardException { - HashMap> studentGroups = (HashMap>) Board.getContextAttribute("StudentsGroups"); + HashMap> studentGroups = (HashMap>) Board.getContextAttribute("StudentsGroups"); - for (List group : studentGroups.values()) { - for (Student student : group) { + for (Entry> group : studentGroups.entrySet()) { + for (Student student : group.getValue()) { if(student.getName().equals(aluno.getName())) { - return group; + System.out.println("TOPICOS: "+group.getKey()); + DataHolder.getInstance().setTopic(group.getKey()); + return group.getValue(); } } } @@ -152,21 +195,26 @@ private String suggestContent() { try { - List studentGroup = getStudentGroup(getAluno()); - + List studentGroup = getStudentGroup(getAluno()); //Pegar grupo de alunos ServiceWrapper servicoGetGroups = require("SACIP", "getStudentGroups"); servicoGetGroups.addParameter("estudante", getAluno()); - List grupo = (List) servicoGetGroups.run().get(0); - + // List grupo = (List) servicoGetGroups.run().get(0); + List grupo = new ArrayList<>(); + + // studentGroup.forEach(s -> { + // Student student = grupo.stream().filter(g -> g.getName().equals(s.getName())) + // .findAny().orElseGet(()->null); + // if(student==null && !s.getName().equals(getAluno().getName())) + // grupo.add(s); + // }); studentGroup.forEach(s -> { - Student student = grupo.stream().filter(g -> g.getName().equals(s.getName())) - .findAny().orElseGet(()->null); - if(student==null && !s.getName().equals(getAluno().getName())) + if(!s.getName().equals(getAluno().getName())) grupo.add(s); }); + //Pedir recomendação para o recomendador ServiceWrapper servicoGetContent = require("SACIP", "getRecommendedContent"); servicoGetContent.addParameter("estudante", getAluno()); diff --git a/src/main/java/sacip/sti/agents/RecommenderAgent.java b/src/main/java/sacip/sti/agents/RecommenderAgent.java index aba26b4..1d46eba 100644 --- a/src/main/java/sacip/sti/agents/RecommenderAgent.java +++ b/src/main/java/sacip/sti/agents/RecommenderAgent.java @@ -24,6 +24,7 @@ import sacip.sti.dataentities.Content; import sacip.sti.dataentities.Student; +import sacip.sti.evaluation.DataHolder; public class RecommenderAgent extends Agent { @@ -81,6 +82,7 @@ public int compare(Content o1, Content o2) { //Fazer chamada ao banco buscando os conteúdos desses níveis //Fazer chamada ao banco ServiceWrapper servicoPegarConteudosEmNiveis = require("SACIP", "findContents"); + if(!trilha.isEmpty()) servicoPegarConteudosEmNiveis.addParameter("level", niveisFeitos); List resultado = servicoPegarConteudosEmNiveis.run(); if(resultado.get(0)==null || resultado.get(0) instanceof String) @@ -88,19 +90,11 @@ public int compare(Content o1, Content o2) { return "não há conteúdos"; } List conteudos = (List) resultado.get(0); - List conteudosDoGrupoNaoFeitos = new ArrayList<>(); + List conteudosDasTrilhas = new ArrayList<>(); if(!grupo.isEmpty()) { //VERIFICAR TRILHAS E PEGAR CONTEUDOS UTILIZADOS NOS SEMELHANTES - List conteudosDasTrilhas = filtrarConteudosDasTrilhasDosAlunosDoGrupo(grupo, conteudos); - - for (Content content : conteudosDasTrilhas) { - if(!aluno.getTrilha().contains(content.getName())) - { - conteudos.remove(content); - conteudosDoGrupoNaoFeitos.add(content); - } - } + conteudosDasTrilhas = filtrarConteudosDasTrilhasDosAlunosDoGrupo(grupo, conteudos); } @@ -125,9 +119,9 @@ public int compare(Content o1, Content o2) { //recomendar os tópicos de níveis mais baixos. //pegar os conteúdos desse tópico + int proximoNivelAluno = 0; if(topicosFaltantes.isEmpty()) - { - int proximoNivelAluno = 1; + { for (int i = 0; i < niveisFeitos.length; i++) { int nivelFeito = niveisFeitos[i]; if(nivelFeito>proximoNivelAluno) @@ -153,18 +147,26 @@ public int compare(Content o1, Content o2) { conteudos = filtrarConteudosPorTags(conteudos, preferenciasAluno); Set conteudosFiltrados = new HashSet<>(); - conteudosFiltrados.addAll(conteudosPorNovasTaxonomias); + //conteudosFiltrados.addAll(conteudosPorNovasTaxonomias); conteudosFiltrados.addAll(conteudos); - conteudosFiltrados.addAll(conteudosDoGrupoNaoFeitos); + //conteudosFiltrados.addAll(conteudosDasTrilhas); //PRIORIZAR POR PONTOS for (Content content : conteudosFiltrados) { content.pontos += calculateTagPoints(content.getTags(), preferenciasAluno); - if(conteudosPorNovasTaxonomias.contains(content)) + + if(!aluno.getTrilha().contains(content.getName())) { - content.pontos++; - } - sortedContent.add(content); + // if(conteudosPorNovasTaxonomias.contains(content)) + // { + // content.pontos+=10; + // } + if(topicosFaltantes.contains(content.getTopic())) + { + content.pontos+=5; + } + sortedContent.add(content); + } } List top10Conteudos = new ArrayList<>(); @@ -178,6 +180,22 @@ public int compare(Content o1, Content o2) { else{ top10Conteudos.addAll(sortedContent); } + int nivelMaximo = niveisFeitos.length; + String topicoMaximo = "0"; + if(!nivelETopico.isEmpty()) + { + List list = nivelETopico.get(nivelMaximo); + if(list!=null && !list.isEmpty()) + topicoMaximo = list.get(list.size()-1); + } + + + DataHolder.getInstance().adicionarDados("nv"+nivelMaximo+"-tpc"+topicoMaximo, "grupo", grupo); + DataHolder.getInstance().adicionarDados("nv"+nivelMaximo+"-tpc"+topicoMaximo, "estudante", aluno); + DataHolder.getInstance().adicionarDados("nv"+nivelMaximo+"-tpc"+topicoMaximo, "recomendacoes", conteudosDasTrilhas); + DataHolder.getInstance().adicionarDados("nv"+nivelMaximo+"-tpc"+topicoMaximo, "topicosFaltantes", topicosFaltantes); + DataHolder.getInstance().adicionarDados("nv"+nivelMaximo+"-tpc"+topicoMaximo, "proximoNivel", proximoNivelAluno); + DataHolder.getInstance().imprimirDados(); //retornando conteudos String exercicio = top10Conteudos.toString(); @@ -232,6 +250,7 @@ private List descobrirTopicosFaltantes(List conteudosBuscados, HashMap> niveisETopicosBuscados = descobrirNiveisETopicos(conteudosBuscados); List topicosFaltantes = new ArrayList<>(); + if(!niveisTopicosDoAluno.isEmpty()) for (Entry> entry : niveisETopicosBuscados.entrySet()) { Integer key = entry.getKey(); List listaTopicos = entry.getValue(); @@ -290,6 +309,10 @@ private List filtrarConteudosPorTags(List conteudos, List descobrirProximosConteudosPorTaxonomia(List trilha) { Map taxonomiaPorTopicos = new HashMap<>(); + if(trilha.isEmpty()) + { + taxonomiaPorTopicos.put("t1", LEMBRAR); + } for (Content content : trilha) { String topico = content.getTopic(); @@ -366,13 +389,13 @@ private List filtrarConteudosDasTrilhasDosAlunosDoGrupo(List g Map conteudosOrdenados = sortByValue(conteudosDoGrupo); - if(conteudosOrdenados.size()>10) - { - conteudosOrdenados = conteudosOrdenados.entrySet().stream() - .limit(10) - .collect(LinkedHashMap::new, (m, e) -> m.put(e.getKey(), e.getValue()), Map::putAll); + // if(conteudosOrdenados.size()>10) + // { + // conteudosOrdenados = conteudosOrdenados.entrySet().stream() + // .limit(10) + // .collect(LinkedHashMap::new, (m, e) -> m.put(e.getKey(), e.getValue()), Map::putAll); - } + // } for (Entry entry : conteudosOrdenados.entrySet()) { diff --git a/src/main/java/sacip/sti/components/DBConnection.java b/src/main/java/sacip/sti/components/DBConnection.java index 87f9711..748f6f2 100644 --- a/src/main/java/sacip/sti/components/DBConnection.java +++ b/src/main/java/sacip/sti/components/DBConnection.java @@ -3,6 +3,7 @@ import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -10,6 +11,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.TextNode; import org.midas.as.AgentServer; import org.midas.as.agent.templates.Component; @@ -368,11 +370,21 @@ private String editUserListAttr(String name, String atributeName, JsonNode newVa { try { - var result = cypher.writequery("MATCH (n:USER { name: $name })"+ - "\nSET n."+atributeName+" = coalesce(n."+atributeName+", []) + $newValue"+ - "\nRETURN n.name, n."+atributeName - , Map.of("name", name, "atribute", atributeName, "newValue", newValue.toString())); - return result.toString(); + if(newValue instanceof TextNode) + { + var result = cypher.writequery("MATCH (n:USER { name: $name })"+ + "\nSET n."+atributeName+" = coalesce(n."+atributeName+", []) + $newValue"+ + "\nRETURN n.name, n."+atributeName + , Map.of("name", name, "atribute", atributeName, "newValue", newValue.asText())); + return result.toString(); + } + else{ + var result = cypher.writequery("MATCH (n:USER { name: $name })"+ + "\nSET n."+atributeName+" = coalesce(n."+atributeName+", []) + $newValue"+ + "\nRETURN n.name, n."+atributeName + , Map.of("name", name, "atribute", atributeName, "newValue", newValue.toString())); + return result.toString(); + } } catch (Exception e) { @@ -488,9 +500,11 @@ else if(entry.getKey().equals("~name")) if(entry.getValue() instanceof String[]) { String[] attrs = (String[]) entry.getValue(); + if(attrs.length!=0) for (String attr : attrs) { query.append("'"+attr+"',"); } + if(attrs.length!=0) query.deleteCharAt(query.length()-1); } else if(entry.getValue() instanceof Integer[]) @@ -499,9 +513,10 @@ else if(entry.getValue() instanceof Integer[]) for (Integer attr : attrs) { query.append(attr+","); } + if(attrs.length!=0) query.deleteCharAt(query.length()-1); } - else if(entry.getValue() instanceof Boolean) + else if(entry.getValue() instanceof Boolean || entry.getValue() instanceof Integer) { query.append(entry.getValue()); } @@ -517,7 +532,7 @@ else if(entry.getValue() instanceof Boolean) var result = cypher.readquery(query.toString(), attributes); if(result.isEmpty()) { - return null; + return new ArrayList(); } //Faz uma lista de conteudos encontrados @@ -657,16 +672,18 @@ public static void main(String[] args) { System.exit(0); } - private static List contentNames = new ArrayList<>(); + private static List contentNames = new ArrayList<>(); private void dummyData() { - String[] tags = {"carros", "musica", "animes", "desenhos", "animação", "jogos", "geografia", - "matemática", "linguas", "biologia", "animais", "pets", "imagens", "memes", - "mitologia", "marvel", "dc", "monstros", "youtube", "comédia", "cultura", - "filmes", "super-heróis", "História", "Esportes", "Ciência", "Brinquedos", - "Internacional", "Tecnologia", "Comidas", "Livros" - }; + String[] tags = { + "carros", "musica", "animes", "desenhos", "animacao", "jogos", "geografia", + "matematica", "linguas", "biologia", "animais", "pets", "imagens", "memes", + "mitologia", "marvel", "dc", "monstros", "youtube", "comedia", "cultura", + "filmes", "superherois", "historia", "esportes", "ciencia", "brinquedos", + "internacional", "tecnologia", "comidas", "livros" + } + ; String[][] topicos = { {"t1", "t2", "t3"}, {"t4", "t5", "t6"}, @@ -688,11 +705,11 @@ private void dummyData() String[] generos = {"Masculino", "Feminino", "Transsexual", "Outro"}; - String[] niveisEdu = {"Fundamental", "Ensino Médio", "Graduação"}; + String[] niveisEdu = {"Fundamental", "EnsinoMedio", "Graduacao"}; final int LIMITE = 500; - final int LIMALU = 50; + final int LIMALU = 250; List> mapas = new ArrayList<>(); @@ -705,7 +722,7 @@ private void dummyData() } for (int i = 0; i < LIMALU; i++) { - dummyStudents.add(mapaCriacaoUsuario(returnRandomTags(tags), returnRandomContents(contentNames.toArray(new String[contentNames.size()])), generos, niveisEdu)); + dummyStudents.add(mapaCriacaoUsuario(returnRandomTags(tags), generos, niveisEdu)); } //System.out.println(queryBuilt.toString()); @@ -734,12 +751,12 @@ private static Map mapaCriacaoConteudo(String[] tags, String[][] String complexidade = complexidades[generator.nextInt(4)]; int taxonomia = generator.nextInt(6); boolean exercicio = generator.nextBoolean(); - String[] links = {"https://cdn.discordapp.com/attachments/571157550956019741/800619655366574091/12243585_1694508097447198_1004266710788666891_n.jpg", - "https://cdn.discordapp.com/attachments/571157550956019741/800619703089365002/21077295_1119616784841346_734019202998452151_n.jpg", - "https://cdn.discordapp.com/attachments/571157550956019741/800619727889629264/1521285067403.jpg"}; + String[] links = {"https://cdn.discordapp.com/attachments/571157550956019741/808827449109512282/8340.jpg", + "https://cdn.discordapp.com/attachments/571157550956019741/808827457775599616/d00428efa0bf27b9edd37eac32dfd2c1.png", + "https://cdn.discordapp.com/attachments/571157550956019741/808827462914015242/blog-10.png"}; String imageLink = links[generator.nextInt(3)]; - contentNames.add(name); + contentNames.add(new Content(name, descricao, level, topico, complexidade, exercicio, taxonomia, Arrays.asList(tags), name, imageLink)); conteudo.put("name", name); conteudo.put("descricao", descricao); @@ -755,7 +772,7 @@ private static Map mapaCriacaoConteudo(String[] tags, String[][] return conteudo; } - private static Map mapaCriacaoUsuario(String[] tags, String[] conteudos, String[] generos, String[] niveisEdu) + private static Map mapaCriacaoUsuario(String[] tags, String[] generos, String[] niveisEdu) { Map conteudo = new HashMap<>(); Random generator = new Random(); @@ -778,7 +795,7 @@ private static Map mapaCriacaoUsuario(String[] tags, String[] co conteudo.put("nivelEdu", nivelEdu); conteudo.put("idade", idade); conteudo.put("preferencias", tags); - conteudo.put("trilha", conteudos); + conteudo.put("trilha", returnRandomContents(contentNames.toArray(new Content[contentNames.size()]), Arrays.asList(tags))); return conteudo; } @@ -817,22 +834,31 @@ private static String[] returnRandomTags(String[] tags) return selectedTags.toArray(new String[selectedTags.size()]); } - private static String[] returnRandomContents(String[] tags) + private static String[] returnRandomContents(Content[] contents, List usertags) { - List selectedTags = new ArrayList<>(); + List selectedContent = new ArrayList<>(); + List filteredContent = new ArrayList<>(); + List topicsDone = new ArrayList<>(); + + for (Content content : contents) { + if(!Collections.disjoint(usertags, content.getTags())) + filteredContent.add(content); + } int randtags = new Random().nextInt(30); for (int i = 0; i < randtags; i++) { - String chosenTag = tags[new Random().nextInt(tags.length)]; - if(selectedTags.contains(chosenTag)) + Content chosenContent = filteredContent.get(new Random().nextInt(filteredContent.size())); + if(selectedContent.contains(chosenContent.getName())) { - i--; + i--; } - else{ - selectedTags.add(chosenTag); + else { + if(!topicsDone.contains(chosenContent.getTopic())) + selectedContent.add(chosenContent.getName()); + topicsDone.add(chosenContent.getTopic()); } } - return selectedTags.toArray(new String[selectedTags.size()]); + return selectedContent.toArray(new String[selectedContent.size()]); } } diff --git a/src/main/java/sacip/sti/dataentities/Student.java b/src/main/java/sacip/sti/dataentities/Student.java index d93640d..e454876 100644 --- a/src/main/java/sacip/sti/dataentities/Student.java +++ b/src/main/java/sacip/sti/dataentities/Student.java @@ -124,6 +124,10 @@ public void setTrilha(List trilha) { this.trilha = trilha; } + public void addNovoPassoTrilha(String content) { + this.trilha.add(content); + } + public List getExerciciosResolvidos() { return this.exerciciosResolvidos; } diff --git a/src/main/java/sacip/sti/evaluation/DataHolder.java b/src/main/java/sacip/sti/evaluation/DataHolder.java new file mode 100644 index 0000000..77a4418 --- /dev/null +++ b/src/main/java/sacip/sti/evaluation/DataHolder.java @@ -0,0 +1,298 @@ +package sacip.sti.evaluation; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import sacip.sti.components.DBConnection; +import sacip.sti.dataentities.Content; +import sacip.sti.dataentities.Student; + +public class DataHolder { + + public static DataHolder dh; + + Map> dados = new HashMap<>(); + String mainTopic = ""; + + public DataHolder() { + super(); + } + + public static DataHolder getInstance() + { + if(dh==null) + { + dh = new DataHolder(); + } + return dh; + } + + public Map> getDados() + { + return dados; + } + + public void adicionarDados(String posicaoAluno, String tipodado, Object dado) + { + if(!dados.containsKey(posicaoAluno)) + { + Map mapadedados = new HashMap<>(); + //List listaDados = new ArrayList<>(); + + //listaDados.add(dado); + mapadedados.put(tipodado, dado); + dados.put(posicaoAluno, mapadedados); + } + else + { + Map mapadedados = dados.get(posicaoAluno); + if(!mapadedados.containsKey(tipodado)) + { + //List listaDados = new ArrayList<>(); + //listaDados.add(dado); + mapadedados.put(tipodado, dado); + } + else + { + //List listaDados = mapadedados.get(tipodado); + //listaDados.add(dado); + mapadedados.put(tipodado, dado); + } + dados.put(posicaoAluno, mapadedados); + } + } + + public void setTopic(String topic) + { + this.mainTopic = topic; + } + + public void imprimirDados() + { + String[] colunas = {"posicao conhecimento", "interesses do aluno", + "10 tags mais comuns nos conteudos", "10 tags mais comuns do grupo","Melhor conteudo do grupo e tags","Melhor conteudo do banco e tags", + "% tags semelhantes ao melhor"}; + //Map printableData = new LinkedHashMap<>(); + List fileLines = new ArrayList<>(); + fileLines.add("posicao conhecimento;interesses do aluno;10 tags mais comuns nos conteudos;topicos do grupo;10 tags mais comuns do grupo;10 melhores conteudos do grupo;melhor conteudo do banco;% tags semelhantes ao estudante"); + + File printTxT = new File("C:\\Users\\shina\\Desktop\\data.txt"); + for (Entry> entry : dados.entrySet()) { + String position = entry.getKey(); + Student estudante = (Student) entry.getValue().get("estudante"); + List grupo = (List) entry.getValue().get("grupo"); + List recomendacoes = (List) entry.getValue().get("recomendacoes"); + List topicosFaltantes = (List) entry.getValue().get("topicosFaltantes"); + Integer proximoNivel = (Integer) entry.getValue().get("proximoNivel"); + + + String interessesDoAluno = String.join(" ", estudante.getPreferencias()); + String tagscomuns = retorneTagsComuns(recomendacoes); + String tagsComunsGrupo = retorneTagsComunsGrupo(grupo); + List melhoresConteudos = retorneMelhoresConteudos(recomendacoes, estudante.getPreferencias(), topicosFaltantes, proximoNivel); + String melhoresConteudosString = melhoresConteudos.get(0).getName() + ": " + melhoresConteudos.get(0).getTags(); + Content melhorConteudoBanco = retorneMelhorConteudoBanco(estudante.getPreferencias(), topicosFaltantes, proximoNivel); + String conteuDoBanco = melhorConteudoBanco.getName() + ": " + melhorConteudoBanco.getTags(); + String propSemelhantes = retorneProporcaoSemelhantes(estudante.getPreferencias(), melhoresConteudos.get(0)); + + fileLines.add(position+";"+interessesDoAluno+";"+tagscomuns+";"+mainTopic+";"+tagsComunsGrupo+";"+melhoresConteudosString+";"+conteuDoBanco+";"+propSemelhantes); + } + + try { + givenWritingStringToFile_whenUsingPrintWriter_thenCorrect(printTxT, fileLines); + } catch (Exception e) { + System.out.println("ERRO DE PRINT" + e); + } + } + + public void givenWritingStringToFile_whenUsingPrintWriter_thenCorrect(File fileName, List lines) + throws IOException { + FileWriter fileWriter = new FileWriter(fileName); + PrintWriter printWriter = new PrintWriter(fileWriter); + for (String string : lines) { + printWriter.println(string); + } + printWriter.close(); + } + + private String retorneMelhoresConteudosEmString(List conteudos) + { + StringBuilder builder = new StringBuilder(); + int i = 0; + for (Content content : conteudos) { + builder.append(content.getName()); + builder.append(" "); + i++; + if(i>=10) + { + break; + } + } + return builder.toString(); + } + + private String retorneProporcaoSemelhantes(List preferencias, Content melhorConteudoRecom) + { + double found = 0; + for (String tag : preferencias) { + + if(melhorConteudoRecom.getTags().contains(tag)) + { + found++; + } + } + + return (found/preferencias.size())*100+"%"; + } + + private Content retorneMelhorConteudoBanco(List preferencias, List topicosFaltantes, int proximoNivel) + { + try { + DBConnection conect = new DBConnection(); + Map data = new HashMap<>(); + data.put("tags", preferencias); + List out = new ArrayList<>(); + conect.provide("getContentByTags", data, out); + List busca = (List)out.get(0); + + return retorneMelhoresConteudos(busca, preferencias, topicosFaltantes, proximoNivel).get(0); + + } catch (Exception e) { + System.out.println("ERRRO DO MELHOR CONTEUDO: "+e); + return null; + } + } + + private List retorneMelhoresConteudos(List conteudos, List preferenciasAluno, List topicosFaltantes, int proximoNivel) + { + List sortedContent = new ArrayList(){ + @Override + public boolean add(Content e) { + super.add(e); + Collections.sort(this, new Comparator(){ + @Override + public int compare(Content o1, Content o2) { + return o2.pontos-o1.pontos; + } + }); + return true; + } + }; + + for (Content content : conteudos) { + content.pontos = 0; + content.pontos += calculateTagPoints(content.getTags(), preferenciasAluno); + if(topicosFaltantes.contains(content.getTopic())) + { + content.pontos += 10; + } + else if(proximoNivel == content.getLevel()) + { + content.pontos += 10; + } + sortedContent.add(content); + } + + return sortedContent; + } + + private String retorneTagsComuns(List conteudos) + { + List tagsComuns = new ArrayList<>(); + Map pontuacaoTags = new LinkedHashMap<>(); + + for (Content conteudo : conteudos) { + List tags = conteudo.getTags(); + for (String tag : tags) { + if(pontuacaoTags.containsKey(tag)) + { + pontuacaoTags.put(tag, pontuacaoTags.get(tag)+1); + } + else + { + pontuacaoTags.put(tag, 1); + } + } + } + pontuacaoTags = sortByValue(pontuacaoTags); + int i = 0; + for (String string : pontuacaoTags.keySet()) { + i++; + tagsComuns.add(string); + if(i>=10) + { + break; + } + } + + return String.join(" ", tagsComuns); + } + + private String retorneTagsComunsGrupo(List estudante) + { + List tagsComuns = new ArrayList<>(); + Map pontuacaoTags = new LinkedHashMap<>(); + + for (Student conteudo : estudante) { + List tags = conteudo.getPreferencias(); + for (String tag : tags) { + if(pontuacaoTags.containsKey(tag)) + { + pontuacaoTags.put(tag, pontuacaoTags.get(tag)+1); + } + else + { + pontuacaoTags.put(tag, 1); + } + } + } + pontuacaoTags = sortByValue(pontuacaoTags); + int i = 0; + for (String string : pontuacaoTags.keySet()) { + i++; + tagsComuns.add(string); + if(i>=10) + { + break; + } + } + + return String.join(" ", tagsComuns); + } + + private int calculateTagPoints(List tagsConteudo, List preferenciasAluno) + { + int pontos = 0; + + for (String tagConteudo : tagsConteudo) { + if(preferenciasAluno.contains(tagConteudo)) + { + pontos++; + } + } + + return pontos; + } + + public static > Map sortByValue(Map map) { + List> list = new ArrayList<>(map.entrySet()); + list.sort(Entry.comparingByValue()); + + Map result = new LinkedHashMap<>(); + for (Entry entry : list) { + result.put(entry.getKey(), entry.getValue()); + } + + return result; + } +}