Subversion Repositories wimsdev

Rev

Rev 11125 | Rev 12196 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. /*    Copyright (C) 1998-2003 XIAO, Gang of Universite de Nice - Sophia Antipolis
  2.  *
  3.  *  This program is free software; you can redistribute it and/or modify
  4.  *  it under the terms of the GNU General Public License as published by
  5.  *  the Free Software Foundation; either version 2 of the License, or
  6.  *  (at your option) any later version.
  7.  *
  8.  *  This program is distributed in the hope that it will be useful,
  9.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  10.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11.  *  GNU General Public License for more details.
  12.  *
  13.  *  You should have received a copy of the GNU General Public License
  14.  *  along with this program; if not, write to the Free Software
  15.  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  16.  */
  17.  
  18. /* student score management */
  19.  
  20. #include "wimslogd.h"
  21. #define MAX_TRY 60000
  22. double oldfactor=0.85;  /* quality factor, should remain stable. */
  23.  
  24. /* User score information of an exercise.
  25.  * size in bytes : 28 + 4*MAX_REQUIRE/10
  26.  * information to change if struct scoredata changes
  27.  */
  28. typedef struct scoredata {
  29.     unsigned short int num, new, try, hint;
  30.     float user, user2, last, best, level, high[MAX_REQUIRE/10];
  31. } scoredata;
  32.  
  33. /* size in bytes: 200
  34.  * information to change if scoreheader changes
  35.  */
  36. struct scoreheader {
  37.     char raf[8][20];
  38.     int sheet, exo;
  39.     char session[32];
  40. } scoreheader;
  41. struct scoredata uscore[MAX_CLASSEXOS];
  42.  
  43. struct scoreresult tscore[MAX_CLASSEXOS];
  44.  
  45. #define oldraf scoreheader.raf
  46. #define oldsheet scoreheader.sheet
  47. #define oldexo scoreheader.exo
  48. #define oldsession scoreheader.session
  49.  
  50. /* one line of score. */
  51. void scoreline(struct classdata *cd, char *l)
  52. {
  53.     int i,sheet,exo,num;
  54.     char *pm[16];
  55.     struct scoredata *thiscore;
  56.     float score;
  57.  
  58.     i=cutwords(l,pm,8); if(i<6) return;
  59.     if(strcmp(pm[i-1],"noscore")==0 || strcmp(pm[i-1],"erased")==0) {
  60.      if(strcmp(pm[1],oldsession)!=0)
  61.        mystrncpy(oldsession,pm[1],sizeof(oldsession));
  62.      return;
  63.     }
  64.     sheet=atoi(pm[2]); exo=atoi(pm[3]);
  65.     if(sheet<=0 || sheet>MAX_SHEETS || exo<=0 || exo>MAX_EXOS) return;
  66.     num=search_data(cd->exos,cd->exocnt,sizeof(exodata),((sheet-1)<<8)+(exo-1));
  67.     if(num<0) return;
  68.     thiscore=uscore+num;
  69.     if(strcmp(pm[4],"score")==0) {
  70.      score=atof(pm[5]);
  71.      if(!isfinite(score)) score=0;
  72.      if(score>10) score=10;
  73.      if(score<-10) score=-10;
  74.  
  75.      if(strcmp(pm[1],oldsession)==0 &&   /* measure to prohibit simultaneous scoring. */
  76.         sheet==oldsheet && exo==oldexo &&
  77.         strncmp(pm[0],oldraf[6],13)!=0   /* prohibit scores immediately after rafale */
  78.         ) {
  79.          thiscore->user+=score;
  80.          thiscore->user2*=oldfactor;
  81.          thiscore->user2+=score;
  82.          thiscore->last=score;
  83.        if (thiscore->high[0] < score)
  84.          {
  85.            int k;
  86.            thiscore->best += (score - thiscore->high[0]);
  87.            for (k = 1; 10*k < cd->exos[num].require && thiscore->high[k] < score; k++)
  88.              thiscore->high[k-1] = thiscore->high[k];
  89.            thiscore->high[k-1] = score;
  90.            thiscore->level=thiscore->high[0];
  91.          }
  92.        if(thiscore->try<MAX_TRY) thiscore->try++;
  93.        oldsheet=oldexo=0;
  94.      }
  95.     }
  96.     else {
  97.      if(strcmp(pm[4],"rafale")==0) { /* rafale punishment */
  98.          if(strncmp(pm[0],oldraf[3],13)==0 && thiscore->new<MAX_TRY) thiscore->new++;
  99.          memmove(oldraf[1],oldraf[0],sizeof(oldraf[0])*7);
  100.          mystrncpy(oldraf[0],pm[0],sizeof(oldraf[0]));
  101.      }
  102.      if(strcmp(pm[4],"resume")!=0 && strcmp(pm[4],"rafale")!=0) {
  103.          if(strcmp(pm[4],"hint")==0) thiscore->hint++;
  104.          else if(thiscore->new<MAX_TRY) thiscore->new++;
  105.      }
  106.      mystrncpy(oldsession,pm[1],sizeof(oldsession));
  107.      oldsheet=sheet; oldexo=exo;
  108.     }
  109. }
  110.  
  111. unsigned int _cuttime(char ends[], char starts[], unsigned int startn)
  112. {
  113.     int h1,h2,m1,m2,s2, t;
  114.     if(ends[0]==0) return 0;
  115.     if(strncmp(ends,starts,14)<0) return 10;
  116.     if(strncmp(ends,starts,8)>0) return 0;
  117.     h1=atoi(ends+9);   m1=atoi(ends+12);
  118.     h2=atoi(starts+9); m2=atoi(starts+12); s2=atoi(starts+15);
  119.     t=((h1-h2)*60+(m1-m2))*60-s2;
  120.     return startn+t;
  121. }
  122.  
  123. /* Gather exam score. */
  124. void examscorecalc(struct classdata *cd, char *uname)
  125. {
  126.     struct scoredata *thiscore;
  127.     char nbuf[MAX_FNAME+1];
  128.     char cuttimes[MAX_EXOS][16];
  129.     char rbuf[MAX_FILELEN+1];
  130.     char *wlist[8];
  131.     char *p1, *p2;
  132.     int i, k, ecnt, num;
  133.     double ss, sc[MAX_EXOS], sc2[MAX_EXOS], scb[MAX_EXOS], scb2[MAX_EXOS];
  134.     int ind[MAX_EXOS];
  135.     unsigned int tr[MAX_EXOS], all[MAX_EXOS], ver[MAX_EXOS], start[MAX_EXOS], dure[MAX_EXOS];
  136.     char *ip[MAX_EXOS], *ses[MAX_EXOS];
  137.     unsigned int start1, endtime[MAX_EXOS];
  138.     signed int dure1;
  139.  
  140.     ecnt=cd->examcnt; if(ecnt<=0) return; if(ecnt>MAX_EXOS) ecnt=MAX_EXOS;
  141.     memset(all,0,sizeof(all)), memset(ver,0,sizeof(ver));
  142.     for(i=0;i<MAX_EXOS;i++) ind[i]=-1;
  143.     for(i=0;i<ecnt;i++) {
  144.      k=((cd->exos[i+cd->examstart].num)&255);
  145.      all[k]=cd->exos[i+cd->examstart].require;
  146.      ind[k]=i+cd->examstart;
  147.     }
  148.     memset(sc,0,sizeof(sc)); memset(sc2,0,sizeof(sc2));
  149.     memset(scb,0,sizeof(scb)); memset(scb2,0,sizeof(scb2));
  150.     memset(tr,0,sizeof(tr)); memset(cuttimes,0,sizeof(cuttimes));
  151.     memset(dure,0,sizeof(dure)); memset(start,0,sizeof(start));
  152.     memset(endtime,0,sizeof(endtime));
  153.     memset(ip,0,sizeof(ip)); memset(ses,0,sizeof(ses));
  154.     snprintf(nbuf,sizeof(nbuf),"score/%s.exam",uname);
  155.     readfile(nbuf,rbuf,sizeof(rbuf));
  156.     if(rbuf[0]==0) goto end;
  157.     for(p1=rbuf; p1!=NULL && *p1; p1=p2) {
  158.      p2=strchr(p1,'\n'); if(p2!=NULL) *p2++=0;
  159.      i=cutwords(find_word_start(p1),wlist,7);
  160.      if(i<6) continue;
  161.      i=atoi(wlist[0])-1; if(i<0 || i>=ecnt) continue;
  162.      dure1=atoi(wlist[2]); start1=atoi(wlist[3]);
  163.      if(strcmp(wlist[1],"--")==0) {     /* session closure */
  164.          start[i]=dure[i]=0; ip[i]=ses[i]="";
  165.          continue;
  166.      }
  167.      if(strcmp(wlist[1],"00")==0) {
  168.          if(sc2[i]<sc[i]) sc2[i]=sc[i];
  169.          if(scb2[i]<scb[i]) scb2[i]=scb[i];
  170.          ver[i]=1; tr[i]++; start[i]=start1; dure[i]=dure1; sc[i]=0; scb[i]=0;
  171.          ip[i]=wlist[4]; ses[i]=wlist[5];
  172.          if(tr[i]==1 && wlist[6]!=NULL) {
  173.           char *pp1, *pp2, lbuf[CTBUFLEN];
  174.           if(cd->ctptr[ind[i]]>=0)
  175.             mystrncpy(lbuf,cd->ctbuf+cd->ctptr[ind[i]],sizeof(lbuf));
  176.           else lbuf[0]=0;
  177.           if(lbuf[0]) {
  178.               for(pp1=find_word_start(lbuf); *pp1; pp1=find_word_start(pp2)) {
  179.                pp2=find_word_end(pp1); if(pp2-pp1!=14) continue;
  180.                if(*pp2) *pp2++=0;
  181.                pp1[8]='.'; pp1[11]=':';
  182.                if(strcmp(pp1,wlist[6])<0) continue;
  183.                memmove(cuttimes[i],pp1,15); break;
  184.               }
  185.           }
  186.          }
  187.          endtime[i]=_cuttime(cuttimes[i],wlist[6],start1);
  188.      }
  189.      else if(ver[i]==0) tr[i]++;
  190.      if(tr[i]>all[i]) continue;
  191.      ss=atof(wlist[1]); if(ss<=0) continue; if(ss>10) ss=10;
  192. /* checking conditions with ip checking*/
  193.      if(ss!=sc[i] && (dure1>=0 ||
  194.                (start1-start[i]<dure[i]*60 &&
  195.                 dure[i]>0 && dure[i]<4096 &&
  196.                 *ses[i]!=0 && *ip[i]!=0 &&
  197.                 start[i]!=0 && start1>start[i] &&
  198.                 (endtime[i]==0 || endtime[i]>=start1) &&
  199.                 strcmp(ip[i],wlist[4])==0 &&
  200.                 strcmp(ses[i],wlist[5])==0)))
  201.        sc[i]=ss;
  202. /* checking conditions without ip checking -- will be in structure best */
  203.     if(ss!=scb[i] && (dure1>=0 ||
  204.                (start1-start[i]<dure[i]*60 &&
  205.                 dure[i]>0 && dure[i]<4096 &&
  206.                 *ses[i]!=0 && *ip[i]!=0 &&
  207.                 start[i]!=0 && start1>start[i] &&
  208.                 (endtime[i]==0 || endtime[i]>=start1) &&
  209.                 strcmp(ses[i],wlist[5])==0)))
  210.        scb[i]=ss;
  211.     }
  212.     end:
  213.     for(i=0; i<ecnt; i++) {
  214.      if(sc2[i]<sc[i]) sc2[i]=sc[i];
  215.      if(scb2[i]<scb[i]) scb2[i]=scb[i];
  216.      num=search_data(cd->exos,cd->exocnt,sizeof(exodata),0xFF00+i);
  217.      if(num<0) continue;
  218.      thiscore=uscore+num;
  219.      thiscore->user=sc2[i];
  220.      thiscore->best=scb2[i];
  221.      thiscore->try=tr[i];
  222.      if(cuttimes[i][0] && strncmp(cuttimes[i],nowstr,14)<0) k=0; else k=1;
  223.      thiscore->hint=k;
  224.     }
  225. }
  226.  
  227. /* calculate score from raw data, core routine. */
  228. void rawscorecalc(struct classdata *cd, char *uname)
  229. {
  230.     int i;
  231.     char fbuf[MAX_FILELEN+1];
  232.     char *p1, *p2;
  233.     char namebuf[MAX_FNAME+1];
  234.  
  235.     memset(uscore,0,sizeof(uscore[0])*cd->exocnt);
  236.     memset(&scoreheader,0,sizeof(scoreheader));
  237.     for(i=0;i<cd->exocnt;i++) uscore[i].num=cd->exos[i].num;
  238.     snprintf(namebuf,sizeof(namebuf),"score/%s",uname);
  239.     readfile(namebuf,fbuf,sizeof(fbuf));
  240.     if(fbuf[0]!=0) {
  241.      oldsession[0]=oldsheet=oldexo=0;
  242.      for(p1=fbuf; *p1; p1=p2) {
  243.          p2=strchr(p1,'\n'); if(p2) *p2++=0; else p2=p1+strlen(p1);
  244.          if(myisdigit(*p1)) scoreline(cd,p1);
  245.      }
  246.     }
  247.     examscorecalc(cd,uname);
  248. }
  249.  
  250. /* size of the file *.bin:
  251.  * 200 + (28+4*MAX_REQUIRE/10)*(number_exos_in_sheets + number_exams)
  252.  * information to change if struct scoredata or scoreheader change
  253.  */
  254. void savescorebin(struct classdata *cd, char *uname)
  255. {
  256.     int fd, cnt;
  257.     char fname[MAX_FNAME+1];
  258.     snprintf(fname,sizeof(fname),"score/%s.bin",uname);
  259.     cnt=cd->exocnt;
  260.     fd=creat(fname,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
  261.     if(fd==-1) return;
  262.     (void)write(fd,&scoreheader,sizeof(scoreheader));
  263.     (void)write(fd,uscore,sizeof(uscore[0])*cnt);
  264.     close(fd);
  265. }
  266.  
  267. void readscorebin(char *fname,int cnt)
  268. {
  269.     int fd;
  270.     fd=open(fname,O_RDONLY);
  271.     if(fd==-1) return;
  272.     (void)read(fd,&scoreheader,sizeof(scoreheader));
  273.     (void)read(fd,uscore,sizeof(uscore[0])*cnt);
  274.     close(fd);
  275. }
  276.  
  277. void getscore(struct classdata *cd, char *user)
  278. {
  279.     struct stat st[3];
  280.     int i, cnt, non[3];
  281.     char buf[3][MAX_FNAME+1];
  282.  
  283.     snprintf(buf[0],sizeof(buf[0]),"score/%s",user);
  284.     snprintf(buf[1],sizeof(buf[1]),"score/%s.exam",user);
  285.     snprintf(buf[2],sizeof(buf[2]),"score/%s.bin",user);
  286.     cnt=cd->exocnt; if(cnt<=0) return;
  287.     for(i=0;i<3;i++) non[i]=stat(buf[i],st+i);
  288.     if(non[0] && non[1]) {
  289.      memset(uscore,0,sizeof(uscore[0])*cnt);
  290.      memset(&scoreheader,0,sizeof(scoreheader));
  291.      return;
  292.     }
  293.     if(!non[2] &&
  294.        st[2].st_size==sizeof(scoreheader)+sizeof(uscore[0])*cnt &&
  295.        (non[0] || st[2].st_mtime>=st[0].st_mtime) &&
  296.        st[2].st_mtime>=cd->modif) {
  297.      readscorebin(buf[2],cnt);
  298.      if(!non[1] && st[2].st_mtime<st[1].st_mtime) {
  299.          examscorecalc(cd,user);
  300.          savescorebin(cd,user);
  301.      }
  302.      return;
  303.     }
  304.     rawscorecalc(cd,user);
  305.     savescorebin(cd,user);
  306. }
  307.  
  308. void cmd_getscore(char *p)
  309. {
  310.     struct classdata *cd;
  311.     char *cut[4];
  312.     int i, sheet, exo, snew, stry, thissheet, thisexo;
  313.     double score, score2, slast, quality, tt, ts, thisscore, sbest;
  314.     float slevel=0;
  315.  
  316.     if(cwdtype!=dir_class) {
  317.      sockerror(2,"getscore_no_class"); return;
  318.     }
  319.     if(*opt_user==0) {
  320.      sockerror(2,"getscore_no_user"); return;
  321.     }
  322.     cd=getclasscache(opt_class);
  323.     if(cd==NULL) {
  324.      sockerror(2,"getscore_bad_class"); return;
  325.     }
  326.     if(cutwords(p,cut,3)==3) {
  327.      thissheet=atoi(cut[0]); thisexo=atoi(cut[1]); thisscore=atof(cut[2]);
  328.      if(!isfinite(thisscore)) thisscore=0;
  329.      if(thisscore<-10) thisscore=-10;
  330.      if(thisscore>10) thisscore=10;
  331.     }
  332.     else {thissheet=thisexo=thisscore=0;}
  333.     getscore(cd,opt_user);
  334.     for(i=0;i<cd->exocnt;i++) {
  335.      tscore[i].num=cd->exos[i].num;
  336.      tscore[i].require=cd->exos[i].require;
  337.      tscore[i].weight=cd->exos[i].weight;
  338.      sheet=(cd->exos[i].num>>8)+1;
  339.      exo=((cd->exos[i].num)&255)+1;
  340.      score=uscore[i].user; stry=uscore[i].try;
  341.      score2=uscore[i].user2;
  342.      slast=uscore[i].last;
  343.      sbest=uscore[i].best;
  344.      slevel=uscore[i].level;
  345. /* case of one exo in a sheet */
  346.      if(sheet==thissheet && exo==thisexo) {
  347.          score+=thisscore; stry++;
  348.          score2*=oldfactor; score2+=thisscore;
  349.          slast=thisscore;
  350.     /* one compute the new sbest and slevel */
  351.          if (uscore[i].high[0] < thisscore) {
  352.            sbest += (thisscore - uscore[i].high[0]);
  353.            int k;
  354.            for (k = 1; 10*k < tscore[i].require && uscore[i].high[k] < thisscore; k++)
  355.              uscore[i].high[k-1] = uscore[i].high[k];
  356.            uscore[i].high[k-1] = thisscore;
  357.            slevel=uscore[i].high[0];
  358.         }
  359.      }
  360.      if(sheet==256) {
  361.          tscore[i].score=score;
  362.          tscore[i].mean=stry*2+uscore[i].hint;
  363.          tscore[i].last=slast;
  364.          tscore[i].try=stry;
  365.          tscore[i].best=sbest;
  366.          tscore[i].level=slevel;
  367.          continue;
  368.      }
  369.      if(score>cd->exos[i].require) score=cd->exos[i].require;
  370.      if(score>0 && stry>0) {
  371.          snew=uscore[i].new; if(uscore[i].hint>0) snew++;
  372. /* here we give up to 1 time unsuccessful tries.
  373.  * Together with a premium of 5 uncounted tries.
  374.  */
  375.          if(snew<stry*2+5) tt=1;
  376.          else tt=(double) (snew-4)/(2*stry); /* tt>=1 */
  377.          ts=(1-pow(oldfactor,stry))/(1-oldfactor);
  378.          quality=score2/(ts*tt);
  379.      }
  380.      else {
  381.        score=quality=slast=stry=sbest=slevel=0;
  382.      }
  383.      tscore[i].score=score; tscore[i].mean=quality;
  384.      tscore[i].last=slast;
  385.      tscore[i].try=stry;
  386.      tscore[i].best=sbest;
  387.      tscore[i].level=slevel;
  388.      }
  389.     answerlen=cd->exocnt*sizeof(tscore[0]);
  390.     memmove(textbuf+3,tscore,answerlen);
  391.     answerlen+=3;
  392. }
  393.  
  394. void cmd_scorelog(char *p)
  395. {
  396.     struct classdata *cd;
  397.     char buf[MAX_LINELEN+1];
  398.  
  399.     if(cwdtype!=dir_class) {
  400.      sockerror(2,"scorelog_no_class"); return;
  401.     }
  402.     if(*opt_user==0) {
  403.      sockerror(2,"scorelog_no_user"); return;
  404.     }
  405.     cd=getclasscache(opt_class);
  406.     if(cd==NULL) {
  407.      sockerror(2,"scorelog_bad_class"); return;
  408.     }
  409.     getscore(cd,opt_user);
  410.     p=find_word_start(p); strip_trailing_spaces(p);
  411.     snprintf(buf,sizeof(buf),"%s\n",p);
  412.     wlogdaccessfile(buf,"a","score/%s",opt_user);
  413.     if(myisdigit(*p)) scoreline(cd,p);
  414.     savescorebin(cd,opt_user);
  415. }
  416.