Subversion Repositories wimsdev

Rev

Rev 17885 | 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 : 32 +4*MAX_REQUIRE/10 + MAX_SCORESEED*(SEEDSIZE+4)
  26.  * information to change if struct scoredata changes
  27.  */
  28. typedef struct scoredata {
  29.   unsigned short int new, allnew, try, hint, seed_scorecnt, seedlastcnt;
  30.   float user, user2, last, best, level, high[MAX_REQUIRE/10];
  31.   struct{
  32.     char seed[SEEDSIZE];
  33.     float score;
  34.   } seed_score[MAX_SCORESEED];
  35. } scoredata;
  36.  
  37. struct scoredata uscore[MAX_CLASSEXOS];
  38.  
  39. /* size in bytes: 200
  40.  * information to change if scoreheader changes
  41.  */
  42. struct scoreheader {
  43.   char raf[8][20];
  44.   int sheet, exo;
  45.   char session[32];
  46. } scoreheader;
  47.  
  48.  
  49. #define oldraf scoreheader.raf
  50. #define oldsheet scoreheader.sheet
  51. #define oldexo scoreheader.exo
  52. #define oldsession scoreheader.session
  53.  
  54. /* one line of score:
  55.   format (see log.c):
  56.   exnowstr sess sh exo cc\tip\tse\tallow
  57.   allow contains information about noscore
  58.   noscore must be at the end to be taken in account
  59.   noscore can be preceded by an explaining word for the moment maxtry
  60.   se is the seed
  61.   tabulation is useful in adm/class/userscore/getraw.sh
  62. */
  63. void scoreline(struct classdata *cd, char *l)
  64. {
  65.   int i,sheet,exo,num,indnum;
  66.   char *pm[16];
  67.   struct scoredata *thiscore;
  68.   struct sheetdata *ts;
  69.   float score;
  70.  
  71.   i=cutwords(l,pm,12); if(i<6) return;
  72.   sheet=atoi(pm[2]); exo=atoi(pm[3]);
  73.   if(sheet<=0 || sheet>MAX_SHEETS || exo<=0 || exo>MAX_EXOS) return;
  74.   ts=cd->sheets+sheet-1;
  75.   num=ts->start+exo-1;
  76.   indnum=ts->indstart + exo-1 + ts->exocnt*ts->techval;
  77.   if(num<0) return;
  78.   if(strcmp(pm[i-1],"noscore")==0 || strcmp(pm[i-1],"erased")==0) {
  79.     if(strcmp(pm[1],oldsession)!=0)
  80.       mystrncpy(oldsession,pm[1],sizeof(oldsession));
  81.     if(strcmp(pm[4],"new")==0 || strcmp(pm[4],"renew")==0) {
  82.       thiscore=uscore+num;
  83.       thiscore->allnew ++;
  84.       if(i>8) {
  85.         /* copy-paste: seeds are kept even if the last word of the line
  86.           contains noscore
  87.          */
  88.         if(thiscore->seed_scorecnt==MAX_SCORESEED) {
  89.           int k;
  90.           /* when one reaches the limit one kept at the end only what is necessary to
  91.             test if the seed should be changed so MAX_SEEDSCORE
  92.             one wants to keep the first ones (for exotrymax test)
  93.            */
  94.           for(k=MAX_SCORESEED-MAX_SEEDSCORE; k<MAX_SCORESEED; ++k)
  95.             thiscore->seed_score[k-1]=thiscore->seed_score[k];
  96.           thiscore->seed_scorecnt--;
  97.         }
  98.         mystrncpy(thiscore->seed_score[thiscore->seed_scorecnt].seed,pm[6],SEEDSIZE);
  99.         thiscore->seed_score[thiscore->seed_scorecnt++].score=-2;
  100.         thiscore->seedlastcnt=1;
  101.         {int k;
  102.           for(k=thiscore->seed_scorecnt-1; k>=1 &&
  103.               strcmp(thiscore->seed_score[k].seed,thiscore->seed_score[k-1].seed)==0;k--)
  104.             thiscore->seedlastcnt++;
  105.         }
  106.       }
  107.     }
  108.     return;
  109.   }
  110.   thiscore=uscore+num;
  111.   /* line with score in word number 5*/
  112.   if(strcmp(pm[4],"score")==0) {
  113.     score=atof(pm[5]);
  114.     if(!isfinite(score)) score=0;
  115.     if(score>10) score=10;
  116.     if(score<-10) score=-10;
  117.  
  118.     if(strcmp(pm[1],oldsession)==0 &&   /* measure to prohibit simultaneous scoring. */
  119.         sheet==oldsheet && exo==oldexo &&
  120.         strncmp(pm[0],oldraf[6],13)!=0   /* prohibit scores immediately after rafale */
  121.         ) {
  122.       thiscore->user+=score;
  123.       thiscore->user2*=oldfactor;
  124.       thiscore->user2+=score;
  125.       thiscore->last=score;
  126.       if (thiscore->high[0] < score)
  127.       {
  128.         int k;
  129.         thiscore->best += (score - thiscore->high[0]);
  130.         for (k = 1; 10*k < cd->exos[indnum].require && thiscore->high[k] < score; k++)
  131.         thiscore->high[k-1] = thiscore->high[k];
  132.         thiscore->high[k-1] = score;
  133.         thiscore->level=thiscore->high[0];
  134.       }
  135.       if(thiscore->try<MAX_TRY) thiscore->try++;
  136.       oldsheet=oldexo=0;
  137.       thiscore->seed_score[thiscore->seed_scorecnt-1].score=score;
  138.     }
  139.   }
  140.   /* end of line with score */
  141.   else {
  142.     if(strcmp(pm[4],"rafale")==0) { /* rafale punishment */
  143.       if(strncmp(pm[0],oldraf[3],13)==0 && thiscore->new<MAX_TRY)
  144.         { thiscore->new++; thiscore->allnew++; }
  145.       memmove(oldraf[1],oldraf[0],sizeof(oldraf[0])*7);
  146.       mystrncpy(oldraf[0],pm[0],sizeof(oldraf[0]));
  147.     }
  148.     if(strcmp(pm[4],"resume")!=0 && strcmp(pm[4],"rafale")!=0) {
  149.       if(strcmp(pm[4],"hint")==0) thiscore->hint++;
  150.       else if(thiscore->new<MAX_TRY) {
  151.         thiscore->new++;
  152.         if(thiscore->allnew<MAX_TRY) thiscore->allnew++;
  153.       }
  154.       /* what about resume ? */
  155.       if((strcmp(pm[4],"new")==0 || strcmp(pm[4],"renew")==0) && i>6){
  156.       /* the first seed is forgotten if there is already MAX_SCORESEED */
  157.         if(thiscore->seed_scorecnt==MAX_SCORESEED) {
  158.           int k;
  159.           for(k=1; k<MAX_SCORESEED; ++k)
  160.             thiscore->seed_score[k-1]=thiscore->seed_score[k];
  161.           thiscore->seed_scorecnt--;
  162.         }
  163.         mystrncpy(thiscore->seed_score[thiscore->seed_scorecnt].seed,pm[6],SEEDSIZE);
  164.         thiscore->seed_score[thiscore->seed_scorecnt++].score=-1;
  165.         thiscore->seedlastcnt=1;
  166.         {int k;
  167.           for(k=thiscore->seed_scorecnt-1; k>=1 &&
  168.               strcmp(thiscore->seed_score[k].seed,thiscore->seed_score[k-1].seed)==0;k--)
  169.             thiscore->seedlastcnt++;
  170.         }
  171.       }
  172.     }
  173.     mystrncpy(oldsession,pm[1],sizeof(oldsession));
  174.     oldsheet=sheet; oldexo=exo;
  175.   }
  176. }
  177.  
  178. unsigned int _cuttime(char ends[], char starts[], unsigned int startn)
  179. {
  180.   int h1,h2,m1,m2,s2, t;
  181.   if(ends[0]==0) return 0;
  182.   if(strncmp(ends,starts,14)<0) return 10;
  183.   if(strncmp(ends,starts,8)>0) return 0;
  184.   h1=atoi(ends+9);   m1=atoi(ends+12);
  185.   h2=atoi(starts+9); m2=atoi(starts+12); s2=atoi(starts+15);
  186.   t=((h1-h2)*60+(m1-m2))*60-s2;
  187.   return startn+t;
  188. }
  189.  
  190. /* Gather exam score. */
  191. void examscorecalc(struct classdata *cd, char *uname)
  192. {
  193.   struct scoredata *thiscore;
  194.   char nbuf[MAX_FNAME+1];
  195.   char cuttimes[MAX_EXAMS][16];
  196.   char rbuf[MAX_FILELEN+1];
  197.   char *wlist[8];
  198.   char *p1, *p2;
  199.   int i, k, ecnt, num;
  200.   double ss, sc[MAX_EXAMS], sc2[MAX_EXAMS], scb[MAX_EXAMS], scb2[MAX_EXAMS];
  201.   int ind[MAX_EXAMS];
  202.   unsigned int tr[MAX_EXAMS], all[MAX_EXAMS], ver[MAX_EXAMS], start[MAX_EXAMS], dure[MAX_EXAMS];
  203.   char *ip[MAX_EXAMS], *ses[MAX_EXAMS];
  204.   unsigned int start1, endtime[MAX_EXAMS];
  205.   signed int dure1;
  206.  
  207.   ecnt=cd->examcnt;
  208.   if(ecnt<=0) return;
  209.   if(ecnt>MAX_EXAMS) ecnt=MAX_EXAMS;
  210.   memset(all,0,sizeof(all)), memset(ver,0,sizeof(ver));
  211.   for(i=0;i<ecnt;i++) {
  212.     ind[i]=i+cd->exam.indstart;
  213.     all[i]=cd->exos[ind[i]].require;
  214.   }
  215.   memset(sc,0,sizeof(sc)); memset(sc2,0,sizeof(sc2));
  216.   memset(scb,0,sizeof(scb)); memset(scb2,0,sizeof(scb2));
  217.   memset(tr,0,sizeof(tr)); memset(cuttimes,0,sizeof(cuttimes));
  218.   memset(dure,0,sizeof(dure)); memset(start,0,sizeof(start));
  219.   memset(endtime,0,sizeof(endtime));
  220.   memset(ip,0,sizeof(ip)); memset(ses,0,sizeof(ses));
  221.   snprintf(nbuf,sizeof(nbuf),"score/%s.exam",uname);
  222.   readfile(nbuf,rbuf,sizeof(rbuf));
  223.   if(rbuf[0]==0) goto end;
  224.   for(p1=rbuf; p1!=NULL && *p1; p1=p2) {
  225.     p2=strchr(p1,'\n'); if(p2!=NULL) *p2++=0;
  226.     i=cutwords(find_word_start(p1),wlist,7);
  227.     if(i<6) continue;
  228.     i=atoi(wlist[0])-1; if(i<0 || i>=ecnt) continue;
  229.     dure1=atoi(wlist[2]); start1=atoi(wlist[3]);
  230.     if(strcmp(wlist[1],"--")==0) {     /* session closure */
  231.       start[i]=dure[i]=0; ip[i]=ses[i]="";
  232.       continue;
  233.     }
  234.     if(strcmp(wlist[1],"00")==0) {
  235.       if(sc2[i]<sc[i]) sc2[i]=sc[i];
  236.       if(scb2[i]<scb[i]) scb2[i]=scb[i];
  237.       ver[i]=1; tr[i]++; start[i]=start1; dure[i]=dure1; sc[i]=0; scb[i]=0;
  238.       ip[i]=wlist[4]; ses[i]=wlist[5];
  239.       if(tr[i]==1 && wlist[6]!=NULL) {
  240.         char *pp1, *pp2, lbuf[CTBUFLEN];
  241.         if(cd->ctptr[ind[i]]>=0)
  242.           mystrncpy(lbuf,cd->ctbuf+cd->ctptr[ind[i]],sizeof(lbuf));
  243.         else lbuf[0]=0;
  244.         if(lbuf[0]) {
  245.           for(pp1=find_word_start(lbuf); *pp1; pp1=find_word_start(pp2)) {
  246.             pp2=find_word_end(pp1); if(pp2-pp1!=14) continue;
  247.             if(*pp2) *pp2++=0;
  248.             pp1[8]='.'; pp1[11]=':';
  249.             if(strcmp(pp1,wlist[6])<0) continue;
  250.             memmove(cuttimes[i],pp1,15); break;
  251.           }
  252.         }
  253.       }
  254.       endtime[i]=_cuttime(cuttimes[i],wlist[6],start1);
  255.     }
  256.     else if(ver[i]==0) tr[i]++;
  257.     if(tr[i]>all[i]) continue;
  258.     ss=atof(wlist[1]); if(ss<=0) continue; if(ss>10) ss=10;
  259.     /* checking conditions with ip checking*/
  260.     if(ss!=sc[i] && (dure1>=0 || (
  261.         start1-start[i]<dure[i]*60 &&
  262.         dure[i]>0 && dure[i]<4096 &&
  263.         *ses[i]!=0 && *ip[i]!=0 &&
  264.         start[i]!=0 && start1>start[i] &&
  265.         (endtime[i]==0 || endtime[i]>=start1) &&
  266.         strcmp(ip[i],wlist[4])==0 &&
  267.         strcmp(ses[i],wlist[5])==0)))
  268.       sc[i]=ss;
  269.     /* checking conditions without ip checking -- will be in structure best */
  270.     if(ss!=scb[i] && (dure1>=0 ||
  271.         (start1-start[i]<dure[i]*60 &&
  272.          dure[i]>0 && dure[i]<4096 &&
  273.          *ses[i]!=0 && *ip[i]!=0 &&
  274.          start[i]!=0 && start1>start[i] &&
  275.          (endtime[i]==0 || endtime[i]>=start1) &&
  276.          strcmp(ses[i],wlist[5])==0)))
  277.       scb[i]=ss;
  278.   }
  279.   end:
  280.   for(i=0; i<ecnt; i++) {
  281.     if(sc2[i]<sc[i]) sc2[i]=sc[i];
  282.     if(scb2[i]<scb[i]) scb2[i]=scb[i];
  283.     num=cd->exocnt - cd->examcnt+i;
  284.     if(num<0) continue;
  285.     thiscore=uscore+num;
  286.     thiscore->user=sc2[i];
  287.     thiscore->best=scb2[i];
  288.     thiscore->try=tr[i];
  289.     if(cuttimes[i][0] && strncmp(cuttimes[i],nowstr,14)<0) k=0; else k=1;
  290.     thiscore->hint=k;
  291.   }
  292. }
  293.  
  294. /* calculate score from raw data, core routine. */
  295. void rawscorecalc(struct classdata *cd, char *uname)
  296. {
  297.   char fbuf[MAX_FILELEN+1];
  298.   char *p1, *p2;
  299.   char namebuf[MAX_FNAME+1];
  300.   /* initialize everything to zero */
  301.   memset(uscore,0,sizeof(uscore[0])*cd->exocnt);
  302.   memset(&scoreheader,0,sizeof(scoreheader));
  303.   snprintf(namebuf,sizeof(namebuf),"score/%s",uname);
  304.   readfile(namebuf,fbuf,sizeof(fbuf));
  305.   if(fbuf[0]!=0) {
  306.     oldsession[0]=oldsheet=oldexo=0;
  307.     for(p1=fbuf; *p1; p1=p2) {
  308.       p2=strchr(p1,'\n'); if(p2) *p2++=0; else p2=p1+strlen(p1);
  309.       if(myisdigit(*p1)) scoreline(cd,p1);
  310.     }
  311.   }
  312.   examscorecalc(cd,uname);
  313. }
  314.  
  315. /* size of the file *.bin:
  316.  * 200 + (28+4*MAX_REQUIRE/10)*(number_exos_in_sheets + number_exams)
  317.  * information to change if struct scoredata or scoreheader change
  318.  */
  319. void savescorebin(struct classdata *cd, char *uname)
  320. {
  321.   int fd, cnt;
  322.   char fname[MAX_FNAME+1];
  323.   snprintf(fname,sizeof(fname),"score/%s.bin",uname);
  324.   cnt=cd->exocnt;
  325.   fd=creat(fname,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
  326.   if(fd==-1) return;
  327.   (void)write(fd,&scoreheader,sizeof(scoreheader));
  328.   (void)write(fd,uscore,sizeof(uscore[0])*cnt);
  329.   close(fd);
  330. }
  331.  
  332. void readscorebin(char *fname,int cnt)
  333. {
  334.   int fd;
  335.   fd=open(fname,O_RDONLY);
  336.   if(fd==-1) return;
  337.   (void)read(fd,&scoreheader,sizeof(scoreheader));
  338.   (void)read(fd,uscore,sizeof(uscore[0])*cnt);
  339.   close(fd);
  340. }
  341. /* copie provisoire  dans wimslogd venant de wims */
  342. /*  Get variable definition from a file.
  343.  * Result stored in buffer value of length MAX_LINELEN.
  344.  */
  345. void _getdef(char buf[], char *name, char value[])
  346. {
  347.   char *p1, *p2, *p3, *p4;
  348.  
  349.   if(*name==0) goto nothing;      /* this would create segfault. */
  350.   for(p1=strstr(buf,name); p1!=NULL; p1=strstr(p1+1,name)) {
  351.     p2=find_word_start(p1+strlen(name));
  352.     if((p1>buf && !isspace(*(p1-1))) || *p2!='=') continue;
  353.     p3=p1; while(p3>buf && *(p3-1)!='\n') p3--;
  354.     p3=find_word_start(p3);
  355.     if(p3<p1 && *p3!='!') continue;
  356.     if(p3<p1) {
  357.       p3++; p4=find_word_end(p3);
  358.       if(find_word_start(p4)!=p1) continue;
  359.       if(p4-p3!=3 || (strncmp(p3,"set",3)!=0 &&
  360.            strncmp(p3,"let",3)!=0 &&
  361.            strncmp(p3,"def",3)!=0)) {
  362.         if(p4-p3!=6 || strncmp(p3,"define",6)!=0) continue;
  363.       }
  364.     }
  365.     p2++;p3=strchr(p2,'\n'); if(p3==NULL) p3=p2+strlen(p2);
  366.     p2=find_word_start(p2);
  367.     if(p2>p3) goto nothing;
  368.     /*if(p3-p2>=MAX_LINELEN) user_error("cmd_output_too_long");*/
  369.     memmove(value,p2,p3-p2); value[p3-p2]=0;
  370.     strip_trailing_spaces(value); return;
  371.   }
  372. nothing:
  373.   value[0]=0;
  374. }
  375.  
  376. void getdef(char *fname, char *name, char value[])
  377. {
  378.   FILE *f;
  379.   char *buf;
  380.   int l;
  381.  
  382.   value[0]=0;
  383.   f=fopen(fname,"r"); if(f==NULL) return;
  384.   fseek(f,0,SEEK_END); l=ftell(f); fseek(f,0,SEEK_SET);
  385.   buf=xmalloc(l+256); l=fread(buf,1,l,f);
  386.   fclose(f);
  387.   if(l<=0) return; else buf[l]=0;
  388.   _getdef(buf,name,value);
  389.   free(buf);
  390. }
  391. /* define number of the technical variable associated to user by sheet */
  392. void techvals (struct classdata *cd, char *user)
  393. {
  394.   char fname[MAX_FNAME+1];
  395.   char techname[MAX_FNAME+1], techval[MAX_FNAME+1];
  396.   char *p1;
  397.   int i, j;
  398.  
  399.   // snprintf(fname,sizeof(fname),".users/%s",user);
  400.   if(cd->sclass[0] != 0 ){
  401.     snprintf(fname,sizeof(fname),"%s/%s/%s/.users/%s",cwd,classd,cd->sclass,user);
  402.   } else {
  403.     snprintf(fname,sizeof(fname),"%s/%s/%s/.users/%s",cwd,classd,cd->name,user);
  404.   }
  405.   for (i = 0; i < cd->sheetcnt; ++i){
  406.     cd->sheets[i].techval=0;
  407.     if (cd->sheets[i].techcnt>1) {
  408.       p1 = cd->techs + cd->sheets[i].techoffset;
  409.       snprintf(techname,sizeof(techname),"user_techvar_%s", p1);
  410.       getdef (fname, techname, techval);
  411.       if (techval[0])
  412.         for (j = 0; j < cd->sheets[i].techcnt; j++){
  413.           p1 += strlen(p1); p1++;
  414.           if (!strcmp(techval, p1)) {cd->sheets[i].techval=j; break;}
  415.         }
  416.       }
  417.   }
  418. }
  419.  
  420. void getscore(struct classdata *cd, char *user)
  421. {
  422.   struct stat st[3];
  423.   int i, cnt, non[3];
  424.   char buf[3][MAX_FNAME+1];
  425.  
  426.   snprintf(buf[0],sizeof(buf[0]),"score/%s",user);
  427.   snprintf(buf[1],sizeof(buf[1]),"score/%s.exam",user);
  428.   snprintf(buf[2],sizeof(buf[2]),"score/%s.bin",user);
  429.   cnt=cd->exocnt; if(cnt<=0) return;
  430.   for(i=0;i<3;i++) non[i]=stat(buf[i],st+i);
  431.   if(non[0] && non[1]) {
  432.     memset(uscore,0,sizeof(uscore[0])*cnt);
  433.     memset(&scoreheader,0,sizeof(scoreheader));
  434.     return;
  435.   }
  436.   if(!non[2] &&
  437.       st[2].st_size==sizeof(scoreheader)+sizeof(uscore[0])*cnt &&
  438.       (non[0] || st[2].st_mtime>=st[0].st_mtime) &&
  439.       st[2].st_mtime>=cd->modif) {
  440.     readscorebin(buf[2],cnt);
  441.     if(!non[1] && st[2].st_mtime<st[1].st_mtime) {
  442.       examscorecalc(cd,user);
  443.       savescorebin(cd,user);
  444.     }
  445.     return;
  446.   }
  447.   rawscorecalc(cd,user);
  448.   savescorebin(cd,user);
  449. }
  450.  
  451. void cmd_getscore(char *p)
  452. {
  453.   struct classdata *cd;
  454.   struct scoreresult tscore[MAX_CLASSEXOS];
  455.   char *cut[4];
  456.   int i, j, sheet, exo, snew, stry, thissheet, thisexo;
  457.   double score, score2, quality, tt, ts, thisscore, sbest;
  458.   float slevel=0;
  459.  
  460.   if(cwdtype!=dir_class) {
  461.     sockerror(2,"getscore_no_class"); return;
  462.   }
  463.   if(*opt_user==0) {
  464.     sockerror(2,"getscore_no_user"); return;
  465.   }
  466.   cd=getclasscache(opt_class);
  467.   if(cd==NULL) {
  468.     sockerror(2,"getscore_bad_class"); return;
  469.   }
  470.   if(cutwords(p,cut,3)==3) {
  471.     thissheet=atoi(cut[0])-1; thisexo=atoi(cut[1])-1; thisscore=atof(cut[2]);
  472.     if(!isfinite(thisscore)) thisscore=0;
  473.     if(thisscore<-10) thisscore=-10;
  474.     if(thisscore>10) thisscore=10;
  475.   }
  476.   else {thissheet=thisexo=-1;thisscore=0;}
  477.   techvals(cd,opt_user);
  478.   getscore(cd,opt_user);
  479.   for(i=sheet=0;sheet<=cd->sheetcnt;sheet++){
  480.     for(exo=0;exo<cd->sheets[sheet].exocnt;exo++,i++) {
  481.       /* j <-> i is the correspondance between the numerotation of
  482.         exercises with all versions and with one version */
  483.       j=cd->sheets[sheet].indstart+cd->sheets[sheet].techval*cd->sheets[sheet].exocnt+exo;
  484.       tscore[i].require=cd->exos[j].require;
  485.       if(cd->exos[j].require==0) {tscore[i].weight=0;}
  486.         else {tscore[i].weight=cd->exos[j].weight;}
  487.       tscore[i].active=cd->exos[j].active;
  488.       tscore[i].sh=sheet;
  489.       tscore[i].exo=exo;
  490.       score=uscore[i].user;
  491.       stry=uscore[i].try;
  492.       score2=uscore[i].user2;
  493.       sbest=uscore[i].best;
  494.       slevel=uscore[i].level;
  495.       /* case of one exo in a sheet */
  496.       if(sheet==thissheet && exo==thisexo) {
  497.         score+=thisscore;
  498.         stry++;
  499.         score2*=oldfactor; score2+=thisscore;
  500.         /* one compute the new sbest and slevel */
  501.         if (uscore[i].high[0] < thisscore) {
  502.           sbest += (thisscore - uscore[i].high[0]);
  503.           int k;
  504.           for (k = 1; 10*k < tscore[i].require && uscore[i].high[k] < thisscore; k++)
  505.             uscore[i].high[k-1] = uscore[i].high[k];
  506.           uscore[i].high[k-1] = thisscore;
  507.           slevel=uscore[i].high[0];
  508.         }
  509.       }
  510.       if(sheet==cd->sheetcnt) { /* examens */
  511.         tscore[i].score=score;
  512.         tscore[i].mean=stry*2+uscore[i].hint;
  513.         tscore[i].try=stry;
  514.         tscore[i].best=sbest;
  515.         tscore[i].level=slevel;
  516.         tscore[i].sh=MAX_SHEETS;
  517.         continue;
  518.       }
  519.       if(score>tscore[i].require) score=tscore[i].require;
  520.       if(stry>0) {
  521.       snew=uscore[i].new; if(uscore[i].hint>0) snew++;
  522.         /* here we give up to 1 time unsuccessful tries.
  523.          * Together with a premium of 5 uncounted tries.
  524.          */
  525.         if(snew<stry*2+5) tt=1;
  526.         else tt=(double) (snew-4)/(2*stry); /* tt>=1 */
  527.         ts=(1-pow(oldfactor,stry))/(1-oldfactor);
  528.         quality=score2/(ts*tt);
  529.       }
  530.       else {
  531.         score=quality=stry=sbest=slevel=0;
  532.       }
  533.       tscore[i].score=score;
  534.       tscore[i].mean=quality;
  535.       tscore[i].try=stry;
  536.       tscore[i].best=sbest;
  537.       tscore[i].level=slevel;
  538.       tscore[i].last=uscore[i].last;
  539.       tscore[i].new=uscore[i].allnew;
  540.       mystrncpy(tscore[i].seedlast,
  541.         uscore[i].seed_score[uscore[i].seed_scorecnt-1].seed,SEEDSIZE);
  542.       tscore[i].seedscorelast=uscore[i].seed_score[uscore[i].seed_scorecnt-1].score;
  543.       p=tscore[i].seedscores;
  544.       *p++ = '[';
  545.       for(j=0;j<uscore[i].seed_scorecnt;j++){
  546.         if (j) *p++=';';
  547.         mystrncpy(p,uscore[i].seed_score[j].seed,SEEDSIZE);
  548.         p+=strlen(p); *p++=',';
  549.         p=moneyprint(p,uscore[i].seed_score[j].score);
  550.       }
  551.       *p++ = ']'; *p=0;
  552.       tscore[i].seedlastcnt=uscore[i].seedlastcnt;
  553.     }
  554.   }
  555.   answerlen=cd->exocnt*sizeof(tscore[0]);
  556.   memmove(textbuf+3,tscore,answerlen);
  557.   answerlen+=3;
  558. }
  559.  
  560. void cmd_scorelog(char *p)
  561. {
  562.   struct classdata *cd;
  563.   char buf[MAX_LINELEN+1];
  564.  
  565.   if(cwdtype!=dir_class) {
  566.     sockerror(2,"scorelog_no_class"); return;
  567.   }
  568.   if(*opt_user==0) {
  569.     sockerror(2,"scorelog_no_user"); return;
  570.   }
  571.   cd=getclasscache(opt_class);
  572.   if(cd==NULL) {
  573.     sockerror(2,"scorelog_bad_class"); return;
  574.   }
  575.   getscore(cd,opt_user);
  576.   p=find_word_start(p); strip_trailing_spaces(p);
  577.   snprintf(buf,sizeof(buf),"%s\n",p);
  578.   wlogdaccessfile(buf,"a","score/%s",opt_user);
  579.   if(myisdigit(*p)) scoreline(cd,p);
  580.   savescorebin(cd,opt_user);
  581. }
  582.