Subversion Repositories wimsdev

Rev

Rev 8252 | Rev 8342 | 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. typedef struct scoredata {
  26.     unsigned short int num, new, try, hint;
  27.     float user, user2, last, best, level, high[MAX_REQUIRE/10];
  28. } scoredata;
  29.  
  30. struct scoreheader {
  31.     char raf[8][20];
  32.     int sheet, exo;
  33.     char session[32];
  34. } scoreheader;
  35. struct scoredata uscore[MAX_CLASSEXOS];
  36.  
  37. struct scoreresult tscore[MAX_CLASSEXOS];
  38.  
  39. #define oldraf scoreheader.raf
  40. #define oldsheet scoreheader.sheet
  41. #define oldexo scoreheader.exo
  42. #define oldsession scoreheader.session
  43.  
  44. /* one line of score. */
  45. void scoreline(struct classdata *cd, char *l)
  46. {
  47.     int i,sheet,exo,num;
  48.     char *pm[16];
  49.     struct scoredata *thiscore;
  50.     float score;
  51.  
  52.     i=cutwords(l,pm,8); if(i<6) return;
  53.     if(strcmp(pm[i-1],"noscore")==0 || strcmp(pm[i-1],"erased")==0) {
  54.      if(strcmp(pm[1],oldsession)!=0)
  55.        mystrncpy(oldsession,pm[1],sizeof(oldsession));
  56.      return;
  57.     }
  58.     sheet=atoi(pm[2]); exo=atoi(pm[3]);
  59.     if(sheet<=0 || sheet>MAX_SHEETS || exo<=0 || exo>MAX_EXOS) return;
  60.     num=search_data(cd->exos,cd->exocnt,sizeof(exodata),((sheet-1)<<8)+(exo-1));
  61.     if(num<0) return;
  62.     thiscore=uscore+num;
  63.     if(strcmp(pm[4],"score")==0) {
  64.      score=atof(pm[5]); if(!isfinite(score)) score=0;
  65.      if(score>10) score=10; if(score<-10) score=-10;
  66.  
  67.      if(strcmp(pm[1],oldsession)==0 &&   /* measure to prohibit simultaneous scoring. */
  68.         sheet==oldsheet && exo==oldexo &&
  69.         strncmp(pm[0],oldraf[6],13)!=0   /* prohibit scores immediately after rafale */
  70.         ) {
  71.          thiscore->user+=score;
  72.          thiscore->user2*=oldfactor;
  73.          thiscore->user2+=score;
  74.          thiscore->last=score;
  75.          if (thiscore->high[0] < score)
  76.            {
  77.              int k;
  78.              thiscore->best += (score - thiscore->high[0]);
  79.              for (k = 1; 10*k < cd->exos[num].require && thiscore->high[k] < score; k++)
  80.                thiscore->high[k-1] = thiscore->high[k];
  81.              thiscore->high[k-1] = score;
  82.              thiscore->level=thiscore->high[0];
  83.            }
  84.          if(thiscore->try<MAX_TRY) thiscore->try++;
  85.          oldsheet=oldexo=0;
  86.      }
  87.     }
  88.     else {
  89.      if(strcmp(pm[4],"rafale")==0) { /* rafale punishment */
  90.          if(strncmp(pm[0],oldraf[3],13)==0 && thiscore->new<MAX_TRY) thiscore->new++;
  91.          memmove(oldraf[1],oldraf[0],sizeof(oldraf[0])*7);
  92.          mystrncpy(oldraf[0],pm[0],sizeof(oldraf[0]));
  93.      }
  94.      if(strcmp(pm[4],"resume")!=0 && strcmp(pm[4],"rafale")!=0) {
  95.          if(strcmp(pm[4],"hint")==0) thiscore->hint++;
  96.          else if(thiscore->new<MAX_TRY) thiscore->new++;
  97.      }
  98.      mystrncpy(oldsession,pm[1],sizeof(oldsession));
  99.      oldsheet=sheet; oldexo=exo;
  100.     }
  101. }
  102.  
  103. unsigned int _cuttime(char ends[], char starts[], unsigned int startn)
  104. {
  105.     int h1,h2,m1,m2,s2, t;
  106.     if(ends[0]==0) return 0;
  107.     if(strncmp(ends,starts,14)<0) return 10;
  108.     if(strncmp(ends,starts,8)>0) return 0;
  109.     h1=atoi(ends+9);   m1=atoi(ends+12);
  110.     h2=atoi(starts+9); m2=atoi(starts+12); s2=atoi(starts+15);
  111.     t=((h1-h2)*60+(m1-m2))*60-s2;
  112.     return startn+t;
  113. }
  114.  
  115. /* Gather exam score. */
  116. void examscorecalc(struct classdata *cd, char *uname)
  117. {
  118.     struct scoredata *thiscore;
  119.     char nbuf[MAX_FNAME+1];
  120.     char cuttimes[MAX_EXOS][16];
  121.     char rbuf[MAX_FILELEN+1];
  122.     char *wlist[8];
  123.     char *p1, *p2;
  124.     int i, k, ecnt, num;
  125.     double ss, sc[MAX_EXOS], sc2[MAX_EXOS];
  126.     int ind[MAX_EXOS];
  127.     unsigned int tr[MAX_EXOS], all[MAX_EXOS], ver[MAX_EXOS], start[MAX_EXOS], dure[MAX_EXOS];
  128.     char *ip[MAX_EXOS], *ses[MAX_EXOS];
  129.     unsigned int start1, endtime[MAX_EXOS];
  130.     signed int dure1;
  131.  
  132.     ecnt=cd->examcnt; if(ecnt<=0) return; if(ecnt>MAX_EXOS) ecnt=MAX_EXOS;
  133.     memset(all,0,sizeof(all)), memset(ver,0,sizeof(ver));
  134.     for(i=0;i<MAX_EXOS;i++) ind[i]=-1;
  135.     for(i=0;i<ecnt;i++) {
  136.      k=((cd->exos[i+cd->examstart].num)&255);
  137.      all[k]=cd->exos[i+cd->examstart].require;
  138.      ind[k]=i+cd->examstart;
  139.     }
  140.     memset(sc,0,sizeof(sc)); memset(sc2,0,sizeof(sc2));
  141.     memset(tr,0,sizeof(tr)); memset(cuttimes,0,sizeof(cuttimes));
  142.     memset(dure,0,sizeof(dure)); memset(start,0,sizeof(start));
  143.     memset(endtime,0,sizeof(endtime));
  144.     memset(ip,0,sizeof(ip)); memset(ses,0,sizeof(ses));
  145.     snprintf(nbuf,sizeof(nbuf),"score/%s.exam",uname);
  146.     readfile(nbuf,rbuf,sizeof(rbuf));
  147.     if(rbuf[0]==0) goto end;
  148.     for(p1=rbuf; p1!=NULL && *p1; p1=p2) {
  149.      p2=strchr(p1,'\n'); if(p2!=NULL) *p2++=0;
  150.      i=cutwords(find_word_start(p1),wlist,7);
  151.      if(i<6) continue;
  152.      i=atoi(wlist[0])-1; if(i<0 || i>=ecnt) continue;
  153.      dure1=atoi(wlist[2]); start1=atoi(wlist[3]);
  154.      if(strcmp(wlist[1],"--")==0) {     /* session closure */
  155.          start[i]=dure[i]=0; ip[i]=ses[i]="";
  156.          continue;
  157.      }
  158.      if(strcmp(wlist[1],"00")==0) {
  159.          if(sc2[i]<sc[i]) sc2[i]=sc[i];
  160.          ver[i]=1; tr[i]++; start[i]=start1; dure[i]=dure1; sc[i]=0;
  161.          ip[i]=wlist[4]; ses[i]=wlist[5];
  162.          if(tr[i]==1 && wlist[6]!=NULL) {
  163.           char *pp1, *pp2, lbuf[CTBUFLEN];
  164.           if(cd->ctptr[ind[i]]>=0)
  165.             mystrncpy(lbuf,cd->ctbuf+cd->ctptr[ind[i]],sizeof(lbuf));
  166.           else lbuf[0]=0;
  167.           if(lbuf[0]) {
  168.               for(pp1=find_word_start(lbuf); *pp1; pp1=find_word_start(pp2)) {
  169.                pp2=find_word_end(pp1); if(pp2-pp1!=14) continue;
  170.                if(*pp2) *pp2++=0;
  171.                pp1[8]='.'; pp1[11]=':';
  172.                if(strcmp(pp1,wlist[6])<0) continue;
  173.                memmove(cuttimes[i],pp1,15); break;
  174.               }
  175.           }
  176.          }
  177.          endtime[i]=_cuttime(cuttimes[i],wlist[6],start1);
  178.      }
  179.      else if(ver[i]==0) tr[i]++;
  180.      if(tr[i]>all[i]) continue;
  181.      ss=atof(wlist[1]); if(ss<=0) continue; if(ss>10) ss=10;
  182.      if(ss!=sc[i] && (dure1>=0 ||     /* checking conditions */
  183.                (start1-start[i]<dure[i]*60 &&
  184.                 dure[i]>0 && dure[i]<4096 &&
  185.                 *ses[i]!=0 && *ip[i]!=0 &&
  186.                 start[i]!=0 && start1>start[i] &&
  187.                 (endtime[i]==0 || endtime[i]>=start1) &&
  188.                 strcmp(ip[i],wlist[4])==0 &&
  189.                 strcmp(ses[i],wlist[5])==0)))
  190.        sc[i]=ss;
  191.     }
  192.     end:
  193.     for(i=0; i<ecnt; i++) {
  194.      if(sc2[i]<sc[i]) sc2[i]=sc[i];
  195.      num=search_data(cd->exos,cd->exocnt,sizeof(exodata),0xFF00+i);
  196.      if(num<0) continue;
  197.      thiscore=uscore+num;
  198.      thiscore->user=sc2[i];
  199.      thiscore->try=tr[i];
  200.      if(cuttimes[i][0] && strncmp(cuttimes[i],nowstr,14)<0) k=0; else k=1;
  201.      thiscore->hint=k;
  202.     }
  203. }
  204.  
  205. /* calculate score from raw data, core routine. */
  206. void rawscorecalc(struct classdata *cd, char *uname)
  207. {
  208.     int i;
  209.     char fbuf[MAX_FILELEN+1];
  210.     char *p1, *p2;
  211.     char namebuf[MAX_FNAME+1];
  212.  
  213.     memset(uscore,0,sizeof(uscore[0])*cd->exocnt);
  214.     memset(&scoreheader,0,sizeof(scoreheader));
  215.     for(i=0;i<cd->exocnt;i++) uscore[i].num=cd->exos[i].num;
  216.     snprintf(namebuf,sizeof(namebuf),"score/%s",uname);
  217.     readfile(namebuf,fbuf,sizeof(fbuf));
  218.     if(fbuf[0]!=0) {
  219.      oldsession[0]=oldsheet=oldexo=0;
  220.      for(p1=fbuf; *p1; p1=p2) {
  221.          p2=strchr(p1,'\n'); if(p2) *p2++=0; else p2=p1+strlen(p1);
  222.          if(myisdigit(*p1)) scoreline(cd,p1);
  223.      }
  224.     }
  225.     examscorecalc(cd,uname);
  226. }
  227.  
  228. void savescorebin(struct classdata *cd, char *uname)
  229. {
  230.     int fd, cnt;
  231.     char fname[MAX_FNAME+1];
  232.     snprintf(fname,sizeof(fname),"score/%s.bin",uname);
  233.     cnt=cd->exocnt;
  234.     fd=creat(fname,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
  235.     if(fd==-1) return;
  236.     (void)write(fd,&scoreheader,sizeof(scoreheader));
  237.     (void)write(fd,uscore,sizeof(uscore[0])*cnt);
  238.     close(fd);
  239. }
  240.  
  241. void readscorebin(char *fname,int cnt)
  242. {
  243.     int fd;
  244.     fd=open(fname,O_RDONLY);
  245.     if(fd==-1) return;
  246.     (void)read(fd,&scoreheader,sizeof(scoreheader));
  247.     (void)read(fd,uscore,sizeof(uscore[0])*cnt);
  248.     close(fd);
  249. }
  250.  
  251. void getscore(struct classdata *cd, char *user)
  252. {
  253.     struct stat st[3];
  254.     int i, cnt, non[3];
  255.     char buf[3][MAX_FNAME+1];
  256.  
  257.     snprintf(buf[0],sizeof(buf[0]),"score/%s",user);
  258.     snprintf(buf[1],sizeof(buf[1]),"score/%s.exam",user);
  259.     snprintf(buf[2],sizeof(buf[2]),"score/%s.bin",user);
  260.     cnt=cd->exocnt; if(cnt<=0) return;
  261.     for(i=0;i<3;i++) non[i]=stat(buf[i],st+i);
  262.     if(non[0] && non[1]) {
  263.      memset(uscore,0,sizeof(uscore[0])*cnt);
  264.      memset(&scoreheader,0,sizeof(scoreheader));
  265.      return;
  266.     }
  267.     if(!non[2] &&
  268.        st[2].st_size==sizeof(scoreheader)+sizeof(uscore[0])*cnt &&
  269.        (non[0] || st[2].st_mtime>=st[0].st_mtime) &&
  270.        st[2].st_mtime>=cd->modif) {
  271.      readscorebin(buf[2],cnt);
  272.      if(!non[1] && st[2].st_mtime<st[1].st_mtime) {
  273.          examscorecalc(cd,user);
  274.          savescorebin(cd,user);
  275.      }
  276.      return;
  277.     }
  278.     rawscorecalc(cd,user);
  279.     savescorebin(cd,user);
  280. }
  281.  
  282. void cmd_getscore(char *p)
  283. {
  284.     struct classdata *cd;
  285.     char *cut[4];
  286.     int i, sheet, exo, snew, stry, thissheet, thisexo;
  287.     double score, score2, slast, quality, tt, ts, thisscore, sbest;
  288.     float slevel=0;
  289.  
  290.     if(cwdtype!=dir_class) {
  291.      sockerror(2,"getscore_no_class"); return;
  292.     }
  293.     if(*opt_user==0) {
  294.      sockerror(2,"getscore_no_user"); return;
  295.     }
  296.     cd=getclasscache(opt_class);
  297.     if(cd==NULL) {
  298.      sockerror(2,"getscore_bad_class"); return;
  299.     }
  300.     if(cutwords(p,cut,3)==3) {
  301.      thissheet=atoi(cut[0]); thisexo=atoi(cut[1]); thisscore=atof(cut[2]);
  302.      if(!isfinite(thisscore)) thisscore=0;
  303.      if(thisscore<-10) thisscore=-10;
  304.      if(thisscore>10) thisscore=10;
  305.     }
  306.     else {thissheet=thisexo=thisscore=0;}
  307.     getscore(cd,opt_user);
  308.     for(i=0;i<cd->exocnt;i++) {
  309.      tscore[i].num=cd->exos[i].num;
  310.      tscore[i].require=cd->exos[i].require;
  311.      tscore[i].weight=cd->exos[i].weight;
  312.      sheet=(cd->exos[i].num>>8)+1;
  313.      exo=((cd->exos[i].num)&255)+1;
  314.      score=uscore[i].user; stry=uscore[i].try;
  315.      score2=uscore[i].user2;
  316.      slast=uscore[i].last;
  317.      sbest=uscore[i].best;
  318.      slevel=uscore[i].level;
  319.      if(sheet==thissheet && exo==thisexo) {
  320.          score+=thisscore; stry++;
  321.          score2*=oldfactor; score2+=thisscore;
  322.          slast=thisscore;
  323.      }
  324.      if(sheet==256) {
  325.          tscore[i].score=score;
  326.          tscore[i].mean=stry*2+uscore[i].hint;
  327.          tscore[i].last=slast;
  328.          tscore[i].try=stry;
  329.          tscore[i].best=sbest;
  330.          tscore[i].level=slevel;
  331.          continue;
  332.      }
  333.      if(score>cd->exos[i].require) score=cd->exos[i].require;
  334.      if(score>0 && stry>0) {
  335.          snew=uscore[i].new; if(uscore[i].hint>0) snew++;
  336. /* here we give up to 1 time unsuccessful tries.
  337.  * Together with a premium of 5 uncounted tries.
  338.  */
  339.          if(snew<stry*2+5) tt=1;
  340.          else tt=(double) (snew-4)/(2*stry); /* tt>=1 */
  341.          ts=(1-pow(oldfactor,stry))/(1-oldfactor);
  342.          quality=score2/(ts*tt);
  343.      }
  344.      else {
  345.        score=quality=slast=stry=sbest=slevel=0;
  346.      }
  347.      tscore[i].score=score; tscore[i].mean=quality;
  348.      tscore[i].last=slast;
  349.      tscore[i].try=stry;
  350.      tscore[i].best=sbest;
  351.      tscore[i].level=slevel;
  352.      }
  353.     answerlen=cd->exocnt*sizeof(tscore[0]);
  354.     memmove(textbuf+3,tscore,answerlen);
  355.     answerlen+=3;
  356. }
  357.  
  358. void cmd_scorelog(char *p)
  359. {
  360.     struct classdata *cd;
  361.     char buf[MAX_LINELEN+1];
  362.  
  363.     if(cwdtype!=dir_class) {
  364.      sockerror(2,"scorelog_no_class"); return;
  365.     }
  366.     if(*opt_user==0) {
  367.      sockerror(2,"scorelog_no_user"); return;
  368.     }
  369.     cd=getclasscache(opt_class);
  370.     if(cd==NULL) {
  371.      sockerror(2,"scorelog_bad_class"); return;
  372.     }
  373.     getscore(cd,opt_user);
  374.     p=find_word_start(p); strip_trailing_spaces(p);
  375.     snprintf(buf,sizeof(buf),"%s\n",p);
  376.     accessfile(buf,"a","score/%s",opt_user);
  377.     if(myisdigit(*p)) scoreline(cd,p);
  378.     savescorebin(cd,opt_user);
  379. }
  380.