Back to index

wims  3.65+svn20090927
score.c
Go to the documentation of this file.
00001 /*    Copyright (C) 1998-2003 XIAO, Gang of Universite de Nice - Sophia Antipolis
00002  *
00003  *  This program is free software; you can redistribute it and/or modify
00004  *  it under the terms of the GNU General Public License as published by
00005  *  the Free Software Foundation; either version 2 of the License, or
00006  *  (at your option) any later version.
00007  *
00008  *  This program is distributed in the hope that it will be useful,
00009  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011  *  GNU General Public License for more details.
00012  *
00013  *  You should have received a copy of the GNU General Public License
00014  *  along with this program; if not, write to the Free Software
00015  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00016  */
00017 
00018               /* student score management */
00019 
00020 double oldfactor=0.85;  /* quality factor, should remain stable. */
00021 
00022        /* User score information of an exercise. Size: 16 bytes. */
00023 typedef struct scoredata {
00024     unsigned short int num, new, try, hint;
00025     float user, user2;
00026 } scoredata;
00027 
00028 struct scoreheader {
00029     char raf[8][20];
00030     int sheet, exo;
00031     char session[32];
00032 } scoreheader;
00033 struct scoredata uscore[MAX_CLASSEXOS];
00034 
00035 struct scoreresult tscore[MAX_CLASSEXOS];
00036 
00037 #define oldraf scoreheader.raf
00038 #define oldsheet scoreheader.sheet
00039 #define oldexo scoreheader.exo
00040 #define oldsession scoreheader.session
00041 
00042        /* one line of score. */
00043 void scoreline(struct classdata *cd, char *l)
00044 {
00045     int i,sheet,exo,num;
00046     char *pm[16];
00047     struct scoredata *thiscore;
00048     float score;
00049 
00050     i=cutwords(l,pm,8); if(i<6) return;
00051     if(strcmp(pm[i-1],"noscore")==0 || strcmp(pm[i-1],"erased")==0) {
00052        if(strcmp(pm[1],oldsession)!=0) 
00053          mystrncpy(oldsession,pm[1],sizeof(oldsession));
00054        return;
00055     }
00056     sheet=atoi(pm[2]); exo=atoi(pm[3]);
00057     if(sheet<=0 || sheet>MAX_SHEETS || exo<=0 || exo>MAX_EXOS) return;
00058     num=search_data(cd->exos,cd->exocnt,sizeof(exodata),((sheet-1)<<8)+(exo-1));
00059     if(num<0) return;
00060     thiscore=uscore+num;
00061     if(strcmp(pm[4],"score")==0) {
00062        score=atof(pm[5]); if(!finite(score)) score=0;
00063        if(score>10) score=10; if(score<-10) score=-10;
00064        
00065        if(strcmp(pm[1],oldsession)==0 &&   /* measure to prohibit simultaneous scoring. */
00066           sheet==oldsheet && exo==oldexo &&
00067           strncmp(pm[0],oldraf[6],13)!=0   /* prohit scores immediately after rafale */
00068           ) {
00069            thiscore->user+=score;
00070            thiscore->user2*=oldfactor;
00071            thiscore->user2+=score;
00072            if(thiscore->try<60000) thiscore->try++;
00073            oldsheet=oldexo=0;
00074        }
00075     }
00076     else {
00077        if(strcmp(pm[4],"rafale")==0) { /* rafale punishment */
00078            if(strncmp(pm[0],oldraf[3],13)==0 && thiscore->new<60000) thiscore->new++;
00079            memmove(oldraf[1],oldraf[0],sizeof(oldraf[0])*7);
00080            mystrncpy(oldraf[0],pm[0],sizeof(oldraf[0]));
00081        }
00082        if(strcmp(pm[4],"resume")!=0 && strcmp(pm[4],"rafale")!=0) {
00083            if(strcmp(pm[4],"hint")==0) thiscore->hint++;
00084            else if(thiscore->new<60000) thiscore->new++;
00085        }
00086        mystrncpy(oldsession,pm[1],sizeof(oldsession));
00087        oldsheet=sheet; oldexo=exo;
00088     }
00089 }
00090 
00091 unsigned int _cuttime(char ends[], char starts[], unsigned int startn)
00092 {
00093     int h1,h2,m1,m2,s2, t;
00094     if(ends[0]==0) return 0;
00095     if(strncmp(ends,starts,14)<0) return 10;
00096     if(strncmp(ends,starts,8)>0) return 0;
00097     h1=atoi(ends+9);   m1=atoi(ends+12);
00098     h2=atoi(starts+9); m2=atoi(starts+12); s2=atoi(starts+15);
00099     t=((h1-h2)*60+(m1-m2))*60-s2;
00100     return startn+t;
00101 }
00102 
00103        /* Gather exam score. */
00104 void examscorecalc(struct classdata *cd, char *uname)
00105 {
00106     struct scoredata *thiscore;
00107     char nbuf[MAX_FNAME+1];
00108     char cuttimes[MAX_EXOS][16];
00109     char rbuf[MAX_FILELEN+1];
00110     char *wlist[8];
00111     char *p1, *p2;
00112     int i, k, ecnt, num;
00113     double ss, sc[MAX_EXOS], sc2[MAX_EXOS];
00114     int ind[MAX_EXOS];
00115     unsigned int tr[MAX_EXOS], all[MAX_EXOS], ver[MAX_EXOS], start[MAX_EXOS], dure[MAX_EXOS];
00116     char *ip[MAX_EXOS], *ses[MAX_EXOS];
00117     unsigned int start1, endtime[MAX_EXOS];
00118     signed int dure1;
00119 
00120     ecnt=cd->examcnt; if(ecnt<=0) return; if(ecnt>MAX_EXOS) ecnt=MAX_EXOS;
00121     memset(all,0,sizeof(all)), memset(ver,0,sizeof(ver));
00122     for(i=0;i<MAX_EXOS;i++) ind[i]=-1;
00123     for(i=0;i<ecnt;i++) {
00124        k=((cd->exos[i+cd->examstart].num)&255);
00125        all[k]=cd->exos[i+cd->examstart].require;
00126        ind[k]=i+cd->examstart;
00127     }
00128     memset(sc,0,sizeof(sc)); memset(sc2,0,sizeof(sc2));
00129     memset(tr,0,sizeof(tr)); memset(cuttimes,0,sizeof(cuttimes));
00130     memset(dure,0,sizeof(dure)); memset(start,0,sizeof(start));
00131     memset(endtime,0,sizeof(endtime));
00132     memset(ip,0,sizeof(ip)); memset(ses,0,sizeof(ses));
00133     snprintf(nbuf,sizeof(nbuf),"score/%s.exam",uname);
00134     readfile(nbuf,rbuf,sizeof(rbuf));
00135     if(rbuf[0]==0) goto end;
00136     for(p1=rbuf; p1!=NULL && *p1; p1=p2) {
00137        p2=strchr(p1,'\n'); if(p2!=NULL) *p2++=0;
00138        i=cutwords(find_word_start(p1),wlist,7);
00139        if(i<6) continue;
00140        i=atoi(wlist[0])-1; if(i<0 || i>=ecnt) continue;
00141        dure1=atoi(wlist[2]); start1=atoi(wlist[3]);
00142        if(strcmp(wlist[1],"--")==0) {     /* session closure */
00143            start[i]=dure[i]=0; ip[i]=ses[i]="";
00144            continue;
00145        }
00146        if(strcmp(wlist[1],"00")==0) {
00147            if(sc2[i]<sc[i]) sc2[i]=sc[i];
00148            ver[i]=1; tr[i]++; start[i]=start1; dure[i]=dure1; sc[i]=0;
00149            ip[i]=wlist[4]; ses[i]=wlist[5];
00150            if(tr[i]==1 && wlist[6]!=NULL) {
00151               char *pp1, *pp2, lbuf[CTBUFLEN];
00152               if(cd->ctptr[ind[i]]>=0)
00153                 mystrncpy(lbuf,cd->ctbuf+cd->ctptr[ind[i]],sizeof(lbuf));
00154               else lbuf[0]=0;
00155               if(lbuf[0]) {
00156                   for(pp1=find_word_start(lbuf); *pp1; pp1=find_word_start(pp2)) {
00157                      pp2=find_word_end(pp1); if(pp2-pp1!=14) continue;
00158                      if(*pp2) *pp2++=0;
00159                      pp1[8]='.'; pp1[11]=':';
00160                      if(strcmp(pp1,wlist[6])<0) continue;
00161                      memmove(cuttimes[i],pp1,15); break;
00162                   }
00163               }
00164            }
00165            endtime[i]=_cuttime(cuttimes[i],wlist[6],start1);
00166        }
00167        else if(ver[i]==0) tr[i]++;
00168        if(tr[i]>all[i]) continue;
00169        ss=atof(wlist[1]); if(ss<=0) continue; if(ss>10) ss=10;
00170        if(ss!=sc[i] && (dure1>=0 ||       /* checking conditions */
00171                      (start1-start[i]<dure[i]*60 &&
00172                       dure[i]>0 && dure[i]<4096 &&
00173                       *ses[i]!=0 && *ip[i]!=0 &&
00174                       start[i]!=0 && start1>start[i] &&
00175                       (endtime[i]==0 || endtime[i]>=start1) &&
00176                       strcmp(ip[i],wlist[4])==0 &&
00177                       strcmp(ses[i],wlist[5])==0)))
00178          sc[i]=ss;
00179     }
00180     end:
00181     for(i=0; i<ecnt; i++) {
00182        if(sc2[i]<sc[i]) sc2[i]=sc[i];
00183        num=search_data(cd->exos,cd->exocnt,sizeof(exodata),0xFF00+i);
00184        if(num<0) continue;
00185        thiscore=uscore+num;
00186        thiscore->user=sc2[i];
00187        thiscore->try=tr[i];
00188        if(cuttimes[i][0] && strncmp(cuttimes[i],nowstr,14)<0) k=0; else k=1;
00189        thiscore->hint=k;
00190     }
00191 }
00192 
00193        /* calculate score from raw data, core routine. */
00194 void rawscorecalc(struct classdata *cd, char *uname)
00195 {
00196     int i;
00197     char fbuf[MAX_FILELEN+1];
00198     char *p1, *p2;
00199     char namebuf[MAX_FNAME+1];
00200 
00201     memset(uscore,0,sizeof(uscore[0])*cd->exocnt);
00202     memset(&scoreheader,0,sizeof(scoreheader));
00203     for(i=0;i<cd->exocnt;i++) uscore[i].num=cd->exos[i].num;
00204     snprintf(namebuf,sizeof(namebuf),"score/%s",uname);
00205     readfile(namebuf,fbuf,sizeof(fbuf));
00206     if(fbuf[0]!=0) {
00207        oldsession[0]=oldsheet=oldexo=0;
00208        for(p1=fbuf; *p1; p1=p2) {
00209            p2=strchr(p1,'\n'); if(p2) *p2++=0; else p2=p1+strlen(p1);
00210            if(myisdigit(*p1)) scoreline(cd,p1);
00211        }
00212     }
00213     examscorecalc(cd,uname);
00214 }
00215 
00216 void savescorebin(struct classdata *cd, char *uname)
00217 {
00218     int fd, cnt;
00219     char fname[MAX_FNAME+1];
00220     snprintf(fname,sizeof(fname),"score/%s.bin",uname);
00221     cnt=cd->exocnt;
00222     fd=creat(fname,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
00223     if(fd==-1) return;
00224     write(fd,&scoreheader,sizeof(scoreheader));
00225     write(fd,uscore,sizeof(uscore[0])*cnt);
00226     close(fd);
00227 }
00228 
00229 void readscorebin(char *fname,int cnt)
00230 {
00231     int fd;
00232     fd=open(fname,O_RDONLY);
00233     if(fd==-1) return;
00234     read(fd,&scoreheader,sizeof(scoreheader));
00235     read(fd,uscore,sizeof(uscore[0])*cnt);
00236     close(fd);
00237 }
00238 
00239 void getscore(struct classdata *cd, char *user)
00240 {
00241     struct stat st[3];
00242     int i, cnt, non[3];
00243     char buf[3][MAX_FNAME+1];
00244     
00245     snprintf(buf[0],sizeof(buf[0]),"score/%s",user);
00246     snprintf(buf[1],sizeof(buf[1]),"score/%s.exam",user);
00247     snprintf(buf[2],sizeof(buf[2]),"score/%s.bin",user);
00248     cnt=cd->exocnt; if(cnt<=0) return;
00249     for(i=0;i<3;i++) non[i]=stat(buf[i],st+i);
00250     if(non[0] && non[1]) {
00251        memset(uscore,0,sizeof(uscore[0])*cnt);
00252        memset(&scoreheader,0,sizeof(scoreheader));
00253        return;
00254     }
00255     if(!non[2] &&
00256        st[2].st_size==sizeof(scoreheader)+sizeof(uscore[0])*cnt &&
00257        (non[0] || st[2].st_mtime>=st[0].st_mtime) &&
00258        st[2].st_mtime>=cd->modif) {
00259        readscorebin(buf[2],cnt);
00260        if(!non[1] && st[2].st_mtime<st[1].st_mtime) {
00261            examscorecalc(cd,user);
00262            savescorebin(cd,user);
00263        }
00264        return;
00265     }
00266     rawscorecalc(cd,user);
00267     savescorebin(cd,user);
00268 }
00269 
00270 void cmd_getscore(char *p)
00271 {
00272     struct classdata *cd;
00273     char *cut[4];
00274     int i, sheet, exo, snew, stry, lastsheet, thissheet, thisexo;
00275     double score, score2, quality, tt, ts, thisscore;
00276     if(cwdtype!=dir_class) {
00277        sockerror(2,"getscore_no_class"); return;
00278     }
00279     if(*opt_user==0) {
00280        sockerror(2,"getscore_no_user"); return;
00281     }
00282     cd=getclasscache(opt_class);
00283     if(cd==NULL) {
00284        sockerror(2,"getscore_bad_class"); return;
00285     }
00286     if(cutwords(p,cut,3)==3) {
00287        thissheet=atoi(cut[0]); thisexo=atoi(cut[1]); thisscore=atof(cut[2]);
00288        if(!finite(thisscore)) thisscore=0;
00289        if(thisscore<-10) thisscore=-10;
00290        if(thisscore>10) thisscore=10;
00291     }
00292     else {thissheet=thisexo=thisscore=0;}
00293     getscore(cd,opt_user);
00294     lastsheet=0;
00295     for(i=0;i<cd->exocnt;i++) {
00296        tscore[i].num=cd->exos[i].num;
00297        tscore[i].require=cd->exos[i].require;
00298        tscore[i].weight=cd->exos[i].weight;
00299        sheet=(cd->exos[i].num>>8)+1;
00300        exo=((cd->exos[i].num)&255)+1;
00301        score=uscore[i].user; stry=uscore[i].try; score2=uscore[i].user2;
00302        if(sheet==thissheet && exo==thisexo) {
00303            score+=thisscore; stry++;
00304            score2*=oldfactor; score2+=thisscore;
00305        }
00306        if(sheet==256) {
00307            tscore[i].score=score;
00308            tscore[i].mean=stry*2+uscore[i].hint;
00309            continue;
00310        }
00311        if(score>cd->exos[i].require) score=cd->exos[i].require;
00312        if(score>0 && stry>0) {
00313            snew=uscore[i].new; if(uscore[i].hint>0) snew++;
00314               /* here we give up to 1 time unsuccessful tries.
00315                * Together with a premium of 5 uncounted tries. */
00316            if(snew<stry*2+5) tt=1;
00317            else tt=(double) (snew-4)/(2*stry); /* tt>=1 */
00318            ts=(1-pow(oldfactor,stry))/(1-oldfactor);
00319            quality=score2/(ts*tt);
00320        }
00321        else score=quality=0;
00322        tscore[i].score=score; tscore[i].mean=quality;
00323     }
00324     answerlen=cd->exocnt*sizeof(tscore[0]);
00325     memmove(textbuf+3,tscore,answerlen);
00326     answerlen+=3;
00327 }
00328 
00329 void cmd_scorelog(char *p)
00330 {
00331     struct classdata *cd;
00332     char buf[MAX_LINELEN+1];
00333     
00334     if(cwdtype!=dir_class) {
00335        sockerror(2,"scorelog_no_class"); return;
00336     }
00337     if(*opt_user==0) {
00338        sockerror(2,"scorelog_no_user"); return;
00339     }
00340     cd=getclasscache(opt_class);
00341     if(cd==NULL) {
00342        sockerror(2,"scorelog_bad_class"); return;
00343     }
00344     getscore(cd,opt_user);
00345     p=find_word_start(p); strip_trailing_spaces(p);
00346     snprintf(buf,sizeof(buf),"%s\n",p);
00347     accessfile(buf,"a","score/%s",opt_user);
00348     if(myisdigit(*p)) scoreline(cd,p);
00349     savescorebin(cd,opt_user);
00350 }
00351