File:  [LON-CAPA] / capa / capa51 / Historic / Scorer / scorer.c
Revision 1.1: download - view: text, annotated - select for diffs
Wed Aug 30 15:02:30 2000 UTC (23 years, 8 months ago) by albertel
Branches: MAIN
CVS tags: version_2_9_X, version_2_9_99_0, version_2_9_1, version_2_9_0, version_2_8_X, version_2_8_99_1, version_2_8_99_0, version_2_8_2, version_2_8_1, version_2_8_0, version_2_7_X, version_2_7_99_1, version_2_7_99_0, version_2_7_1, version_2_7_0, version_2_6_X, version_2_6_99_1, version_2_6_99_0, version_2_6_3, version_2_6_2, version_2_6_1, version_2_6_0, version_2_5_X, version_2_5_99_1, version_2_5_99_0, version_2_5_2, version_2_5_1, version_2_5_0, version_2_4_X, version_2_4_99_0, version_2_4_2, version_2_4_1, version_2_4_0, version_2_3_X, version_2_3_99_0, version_2_3_2, version_2_3_1, version_2_3_0, version_2_2_X, version_2_2_99_1, version_2_2_99_0, version_2_2_2, version_2_2_1, version_2_2_0, version_2_1_X, version_2_1_99_3, version_2_1_99_2, version_2_1_99_1, version_2_1_99_0, version_2_1_3, version_2_1_2, version_2_1_1, version_2_1_0, version_2_12_X, version_2_11_X, version_2_11_4_uiuc, version_2_11_4_msu, version_2_11_4, version_2_11_3_uiuc, version_2_11_3_msu, version_2_11_3, version_2_11_2_uiuc, version_2_11_2_msu, version_2_11_2_educog, version_2_11_2, version_2_11_1, version_2_11_0_RC3, version_2_11_0_RC2, version_2_11_0_RC1, version_2_11_0, version_2_10_X, version_2_10_1, version_2_10_0_RC2, version_2_10_0_RC1, version_2_10_0, version_2_0_X, version_2_0_99_1, version_2_0_2, version_2_0_1, version_2_0_0, version_1_99_3, version_1_99_2, version_1_99_1_tmcc, version_1_99_1, version_1_99_0_tmcc, version_1_99_0, version_1_3_X, version_1_3_3, version_1_3_2, version_1_3_1, version_1_3_0, version_1_2_X, version_1_2_99_1, version_1_2_99_0, version_1_2_1, version_1_2_0, version_1_1_X, version_1_1_99_5, version_1_1_99_4, version_1_1_99_3, version_1_1_99_2, version_1_1_99_1, version_1_1_99_0, version_1_1_3, version_1_1_2, version_1_1_1, version_1_1_0, version_1_0_99_3, version_1_0_99_2, version_1_0_99_1, version_1_0_99, version_1_0_3, version_1_0_2, version_1_0_1, version_1_0_0, version_0_99_5, version_0_99_4, version_0_99_3, version_0_99_2, version_0_99_1, version_0_99_0, version_0_6_2, version_0_6, version_0_5_1, version_0_5, version_0_4, stable_2002_spring, stable_2002_july, stable_2002_april, stable_2001_fall, loncapaMITrelate_1, language_hyphenation_merge, language_hyphenation, conference_2003, bz6209-base, bz6209, STABLE, HEAD, GCI_3, GCI_2, GCI_1, CAPA_5-1-6, CAPA_5-1-5, CAPA_5-1-4_RC1, BZ4492-merge, BZ4492-feature_horizontal_radioresponse, BZ4492-feature_Support_horizontal_radioresponse, BZ4492-Support_horizontal_radioresponse
- documentation updates

/*
 * scorer.c
 * Copyright Guy Albertelli II 1997
 */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#ifdef NeXT
#endif
#ifdef linux
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#endif
#include "Capa/capaCommon.h"
#include "scorer.h"

extern int Parsemode_f;

void initScreen()
{
  printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
  printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
  printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
  printf("Welcome to Scorer, the Automated Scoring Office converter.\n");
  printf("By: Guy Albertelli II\n");
  printf("Version %s.%s for %s\n\n\n",MAJORVER,MINORVER,ARCHSTR);
}

void getClassInfo(Flags *flags,FILE** scantron)
{
  char buffer[MAX_LINE_LENGTH],buffer2[MAX_LINE_LENGTH];
  int done=FALSE;

  printf("What is the class name?");
  scanf("%s",flags->Class);
  flags->Class[8]='\0';
  printf("What is the SetId?");
  scanf("%d",&(flags->SetId));
  
  while(!done)
    {
      printf("What is the name of the scoring offices report file?");
      scanf("%s",buffer);
      *scantron=fopen(buffer,"r");
      if (*scantron != NULL)
	{
	  rewind(*scantron);
	  if (EOF == fscanf(*scantron,"%s",buffer2))
	    printf("This file appears to be empty. Please enter a new name.\n");
	  else
	    {
	      rewind(*scantron);
	      done=1;
	    }
	}
      else
	printf("Unable to open the report file %s\n",buffer);
    }
}

int openOutputFile(char filename[MAX_LINE_LENGTH], FILE ** outputFile)
{
  char buf[MAX_LINE_LENGTH];

  *outputFile=fopen(filename,"r+");

  if (*outputFile != NULL)
    {
      rewind(*outputFile);
      if (EOF == fscanf(*outputFile,"%s",buf))
	  buf[0]='\0';
    }
  
  if (*outputFile==NULL || buf[0]=='\0')
    {
      if (*outputFile != NULL)
	fclose(*outputFile);
      *outputFile=fopen(filename,"w+");
      if (*outputFile==NULL)
	{
	  fprintf(stderr,"Unable to open the output file %s\n",filename);
	  return SCO_FATALERROR;
	}
      else
	return SCO_NEW;
    }
  else
    {
      rewind(*outputFile);
      return SCO_EXIST;
    }  
}

void initValues(Question questions[MAX_QUEST],Flags *flags)
{
  int i;
  flags->NumQuestions=0;
  flags->CheckPIN=TRUE;
  flags->AnonMode=FALSE;
  flags->CheckSpaces=FALSE;
  flags->SurveyMode=FALSE;
  flags->SurveyHeader=FALSE;
  flags->Pause=FALSE;
  flags->PauseTime=0.0;
  flags->IdFormat=ANUMBER_FORMAT;
  flags->CheckMultipleMarks=FALSE;
  flags->QueryAboutPID=FALSE;
  flags->log=TRUE;
  for(i=0;i<MAX_QUEST;i++)
    {
      questions[i].type='\0';
      questions[i].points=0;
      questions[i].leafs=0;
    }
}

int getPreviousValues(FILE *outputFile, Question questions[MAX_QUEST],
		      Flags *flags)

{
  char prevClass[MAX_LINE_LENGTH],prevFlags[MAX_LINE_LENGTH],
    prevQuestions[MAX_LINE_LENGTH],buf[MAX_LINE_LENGTH];
  int prevSetId,prevNumQuestions,done,i;
  float pauseTime;

  rewind(outputFile);
  fscanf(outputFile,"%s %d %d %s %f %s",prevClass,&prevSetId,&prevNumQuestions,
	 prevFlags,&pauseTime,prevQuestions);
  if ( strcmp(prevClass,flags->Class)!=0)
    {
      printf("Classname as found in the current scorer.output file: %s\n",
	     prevClass);
      printf("Classname as entered: %s\n",flags->Class);
      printf("Please select one as correct. previous or new (p/n).\n");
      done=FALSE;
      while(!done)
	{
	  scanf("%s",buf);
	  switch(buf[0])
	    {
	    case 'n':case'N':
	      done=TRUE;
	      break;
	    case 'p':case'P':
	      done=TRUE;
	      strcpy(flags->Class,prevClass);
	      break;
	    default:
	      printf("Please enter one of previous(p) or new(n).\n");
	      break;
	    }
	}
    }
  if ( prevSetId != flags->SetId )
    {
      printf("SetId as found in the current scorer.output file: %d\n",
	     prevSetId);
      printf("SetId as entered: %d\n",flags->SetId);
      printf("Please select one as correct. previous or new (p/n).\n");
      done=FALSE;
      while(!done)
	{
	  scanf("%s",buf);
	  switch(buf[0])
	    {
	    case 'n':case'N':
	      done=TRUE;
	      break;
	    case 'p':case'P':
	      done=TRUE;
	      flags->SetId=prevSetId;
	      break;
	    default:
	      printf("Please enter one of previous(p) or new(n).\n");
	      break;
	    }
	}
    }
  flags->NumQuestions=prevNumQuestions;
  flags->CheckPIN=(int)(prevFlags[0]-'0');
  flags->AnonMode=(int)(prevFlags[1]-'0');
  flags->CheckSpaces=(int)(prevFlags[2]-'0');
  flags->SurveyMode=(int)(prevFlags[3]-'0');
  flags->SurveyHeader=(int)(prevFlags[4]-'0');
  flags->Pause=(int)(prevFlags[5]-'0');
  flags->PauseTime=pauseTime;
  flags->IdFormat=(int)(prevFlags[6]-'0');
  flags->CheckMultipleMarks=(int)(prevFlags[7]-'0');
  flags->QueryAboutPID=(int)(prevFlags[8]-'0');
  flags->log=(int)(prevFlags[9]-'0');
  for(i=0;i<flags->NumQuestions;i++)
    {
      questions[i].type=prevQuestions[3*i];
      questions[i].points=(int)(prevQuestions[3*i+1]-'0');
      questions[i].leafs=(int)(prevQuestions[3*i+2]-'0');
    }
  for(i=flags->NumQuestions;i<MAX_QUEST;i++)
    {
      questions[i].type='\0';
      questions[i].points=0;
      questions[i].leafs=0;
    }
  return 0;
}

int printFlagMenu(Flags *flags, int *flagToModify)
{
  int i=0;
  if(flags->SurveyMode)
    {
      printf("%d. The Paper being graded is a Survey.\n",++i);
      flagToModify[i]=SURVEY_MODE;
      if(flags->SurveyHeader)
	printf("%d. The Survey has a header.\n",++i);
      else
	printf("%d. The Survey does not have a header.\n",++i);
      flagToModify[i]=SURVEY_HEADER;
    }
  else
    {
      printf("%d. The paper being graded is an Exam or Quiz.\n",++i);
      flagToModify[i]=SURVEY_MODE;
      if(flags->CheckPIN)
	{
	  printf("%d. The coded CAPA ID will be checked for validity.\n",++i);
	  flagToModify[i]=CHECK_PIN;
	  if(flags->AnonMode)
	    {
	      printf("%d. Scorer will run in Anonymous Mode. (search for \n\tthe correct Student Number based on the CAPA ID)\n",++i);
	      flagToModify[i]=ANON_MODE;
	      if(flags->QueryAboutPID)
		printf("%d. Scorer will ask which PID to use if multiple are found.\n",++i);
	      else
		printf("%d. Scorer will pick highest score if multiple PIDs found.\n",++i);
	      flagToModify[i]=QUERY_ABOUT_PID;
	    }
	  else
	    {
	      printf("%d. Scorer will not run in Anonymous Mode. (Bubbled \n\tstudent number is correct for the CAPA ID.)\n",++i);
	      flagToModify[i]=ANON_MODE;
	    }
	}
      else
	{
	  printf("%d. The coded CAPA ID will be ignored.\n",++i);
	  flagToModify[i]=CHECK_PIN;
	}
      if(flags->CheckSpaces)
	{
	  if (flags->log)
	    printf("%d. Scorer will log sheets containing blank answers.\n",++i);
	  else
	    printf("%d. Scorer will issue a warning and pause when encountering blank answers.\n",++i);
	}
      else
	{
	  printf("%d. Scorer will ignore blank answers.\n",++i);
	}
      flagToModify[i]=CHECK_SPACES;
      if(flags->CheckMultipleMarks)
	{
	  if (flags->log)
	    printf("%d. Scorer will log answer sheets containing multiple marks.\n",++i);
	  else
	    printf("%d. Scorer will issue a warning and pause when encountering multiple marks.\n",++i);
	}
      else
	{
	  printf("%d. Scorer will mark multiple marks wrong.\n",++i);
	}
      flagToModify[i]=MULTIPLE_MARKS;
      switch(flags->IdFormat)
	{
	case ANUMBER_FORMAT:
	  printf("%d. The StudentId is in A<number> format.\n",++i);
	  break;
	case SOC_SEC_FORMAT:
	  printf("%d. The StudentId is in Social Security format.\n",++i);
	  break;
	default:
	  flags->IdFormat=ANUMBER_FORMAT;
	  printf("%d. The StudentId is in A<number> format.\n",++i);
	  break;
	}
      flagToModify[i]=ID_FORMAT;
    }
  if (flags->Pause)
    printf("%d. The program will pause %.2f seconds between papers.\n",++i,
	   flags->PauseTime);
  else
    printf("%d. The program will process papers as quickly as possible.\n",
	   ++i);
  flagToModify[i]=PAUSE_TIME;
  if (flags->log)
    printf("%d. The program will log all errors.\n",++i);
  else
    printf("%d. The program will query user on all errors.\n",++i);
  flagToModify[i]=LOGGING;
  return i;
}

int getYesOrNo()
{
  int done=FALSE;
  char buf[MAX_LINE_LENGTH];
  while(!done)
    {
      scanf("%s",buf);
      switch(buf[0])
	{
	case 'n':case'N':
	  return NO;
	  done=TRUE;
	  break;
	case 'y':case'Y':
	  return YES;
	  done=TRUE;
	  break;
	default:
	  printf("Please enter y or n.");
	  done=FALSE;
	  break;
	}
    }
  exit(E_GETYESNO);
  return -1;
}
  
void modifyFlag(Flags *flags,int *flagToModify,int i)
{
  int valid=FALSE;
  int done=FALSE;
  char buf[MAX_LINE_LENGTH];
  switch(flagToModify[i])
    {
    case CHECK_PIN:
      printf("Should the student coded CAPA ID be checked for correctness?\n");
      printf("(y or n)");
      flags->CheckPIN=getYesOrNo();
      break;
    case ANON_MODE:
      printf("Should scorer run in Anonymous mode (search for correct \n");
      printf("Student Number based on the CAPA ID)? (y or n)");
      flags->AnonMode=getYesOrNo();
      break;
    case QUERY_ABOUT_PID:
      printf("Should scorer Query you when multiple PIDs match a single PIN.(y or n)\n");
      flags->QueryAboutPID=getYesOrNo();
      break;
    case CHECK_SPACES:
      printf("Should scorer check for blank answers and issue a warning \n");
      printf("when one is encountered? (y or n)");
      flags->CheckSpaces=getYesOrNo();
      break;
    case MULTIPLE_MARKS:
      printf("Should scorer check for multiple marks and issue a warning \n");
      printf("when encountered? (y or n)");
      flags->CheckMultipleMarks=getYesOrNo();
      break;
    case SURVEY_MODE:
      printf("Is this a survey?(y or n)");
      flags->SurveyMode=getYesOrNo();
      break;
    case SURVEY_HEADER:
      printf("Does the form include a Header?(y or n)");
      flags->SurveyHeader=getYesOrNo();
      break;
    case PAUSE_TIME:
      printf("Do you wish for there to be a pause between papers?(y or n)");
      flags->Pause=getYesOrNo();
      if (flags->Pause)
	{
	  printf("Please enter the minimum delay between papers in seconds.\n");
	  valid=FALSE;
	  while(!valid)
	    {
	      scanf("%s",buf);
	      flags->PauseTime=(float)atof(buf);
	      if (flags->PauseTime < 0.00001)
		{
		  printf("You have entered a rather short time, are you sure you meant %f seconds?",flags->PauseTime);
		  switch(getYesOrNo())
		    {
		    case NO:
		      printf("Please enter the minimum delay between papers.\n");
		      break;
		    case YES:
		      valid=TRUE;
		      break;
		    default:
		      fprintf(stderr,"getYesOrNo returned a bad result\n");
		      exit(E_GETYESNO);
		      break;
		    }
		}
	      else
		valid=TRUE;
	    }
	}
      else
	flags->PauseTime=0.0;
      break;
    case ID_FORMAT:
      printf("Is the StudentID Format A<number> or Social Security? (a or s)");
      while(!done)
	{
	  scanf("%s",buf);
	  switch(buf[0])
	    {
	    case 'a':case'A':
	      flags->IdFormat=ANUMBER_FORMAT;
	      done=TRUE;
	      break;
	    case 's':case'S':
	      flags->IdFormat=SOC_SEC_FORMAT;
	      done=TRUE;
	      break;
	    default:
	      printf("Please enter a or s.");
	      done=FALSE;
	      break;
	    }
	}
      break;
    case LOGGING:
      printf("Should Scorer log errors?(y or n)");
      flags->log=getYesOrNo();
      break;
    default:
      fprintf(stderr,"Corrupted information in modifyFlag, i=%d\n",i);
      fprintf(stderr,"flagToModify[i]=%d",flagToModify[i]);
      exit(E_MODIFYFLAG);
      break;
    }
}

void checkFlagConsistency(Flags *flags)
{
  if(flags->SurveyMode)
    {
      flags->CheckPIN=FALSE;
      flags->AnonMode=FALSE;
      flags->QueryAboutPID=FALSE;
      flags->CheckSpaces=FALSE;
      flags->CheckMultipleMarks=FALSE;
    }
  else
    {
      flags->SurveyHeader=FALSE;
      if(flags->CheckPIN);
      else
	flags->AnonMode=FALSE;
    }
  if (!flags->AnonMode) flags->QueryAboutPID=FALSE;
}
  
void getFlags(Flags *flags)
{
  int done=FALSE,valid=FALSE,i,flagToModify[MAX_NUM_FLAG];
  char buf[MAX_LINE_LENGTH];
  while(!done)
    {
      i=printFlagMenu(flags,flagToModify);
      printf("Please select which one of the above (1-%d) you wish to \n",i);
      printf("change. Otherwise enter 0 (zero) to continue.\n");
      valid=FALSE;
      while(!valid)
	{
	  scanf("%s",buf);
	  if (isdigit(buf[0]))
	    {
	      i=atoi(buf);
	      valid=TRUE;
	    }
	  else
	    printf("Please enter a number between (0-%d)\n",i);
	}
      if (i)
	modifyFlag(flags,flagToModify,i);
      else
	done=TRUE;
    }
  checkFlagConsistency(flags);
}

void printQuestions(Question questions[MAX_QUEST],int numQuestions)
{
  int i;
  for(i=0;i<numQuestions;i++)
    {
      printf("%d. ",i+1);
      switch(questions[i].type)
	{
	case ONE_OUT_OF_8:
	  printf("One out of 8.                                 ");
	  break;
	case GLE:
	  printf("Choose one of >, <, =,                        ");
	  break;
	case TF:
	  printf("True or False.                                ");
	  break;
	case ASSIGNED:
	  printf("Assigned Score.                               ");
	  break;
	case N_OUT_OF_M:
	  printf("Pick N out of M.                              ");
	  break;
	case SINGLE_DIGIT:
	  printf("Single digit answer.                          ");
	  break;
	case STRING_MATCH:
	  printf("Exact string matching.(10 or less bubbles)    ");
	  break;
	default:
	  fprintf(stderr,"\nCorrupt data in Questions struct while inside printQuestions. Dying\n");
	  exit(E_PRINTQUESTIONS);
	  break;
	}
      printf(" %d leafs, Worth: %d points\n",questions[i].leafs,
	     questions[i].points);
    }  
}

int wantToChangeQuestions()
{
  int done=FALSE;
  char buf[MAX_LINE_LENGTH];
  printf("Do you want to change any of the above?(y or n)\n");
  while(!done)
    {
      scanf("%s",buf);
      switch(buf[0])
	{
	case 'n':case'N':
	  return 0;
	  done=TRUE;
	  break;
	case 'y':case 'Y':
	  return 1;
	  done=TRUE;
	  break;
	default:
	  printf("Please enter either (y or n).\n");
	  break;
	}
    }
  return -1;
}

void printQuestionMenu()
{
  printf("For Each Question enter \"%c\" for a one out of 8\n",ONE_OUT_OF_8);
  printf("                        \"%c\" for a GLE type\n",GLE);
  printf("                        \"%c\" for a TF type.\n",TF);
  printf("                        \"%c\" for an assigned score.\n",ASSIGNED);
  printf("                        \"%c\" for an n out of m.\n",N_OUT_OF_M);
  printf("                        \"%c\" for single digit answer.\n",
	 SINGLE_DIGIT);
  printf("                        \"%c\" for exact string matching (8 or less bubbles)\n",
	 STRING_MATCH);
  printf("                        \"%c\" to print this menu again\n",
	 QUESTION_MENU);
  printf("                        \"%c\" to stop entering questions\n",
	 QUESTION_STOP);
}

int getSingleDigit()
{  
  int done=FALSE;
  char buf[MAX_LINE_LENGTH];
  while(!done)
    {
      scanf("%s",buf);
      if (isdigit(buf[0]) && (atoi(buf) >=1) && (atoi(buf) <=9))
	{
	  done=TRUE;
	  return atoi(buf);
	}
      else
	printf("Please enter a digit between 1 and 9\n");
    }
  return 0;
}

int getNewQuestion(Question questions[MAX_QUEST],int numQuestions)
{
  int done=FALSE;
  char buf[MAX_LINE_LENGTH];
  printf("Question#%2d: (enter %c to see menu): ",numQuestions+1,QUESTION_MENU);
  while(!done)
    {
      scanf("%s",buf);
      questions[numQuestions].leafs=1;
      switch(buf[0])
	{
	case GLE:
	case TF:
	  printf("How many parts to the problem?");
	  if (!(questions[numQuestions].leafs=getSingleDigit())) 
	    {
	      fprintf(stderr,"Weird result from getSingleDigit\n");
	      exit(E_SINGLEDIGIT);
	    }
	case ONE_OUT_OF_8:
	case ASSIGNED:
	case SINGLE_DIGIT:
	case STRING_MATCH:
	  printf("How many points are possible?");
	  done=TRUE;
	  break;
	case N_OUT_OF_M:
	  printf("What is m in the problem?");
	  if (!(questions[numQuestions].leafs=getSingleDigit())) 
	    {
	      fprintf(stderr,"Weird result from getSingleDigit\n");
	      exit(E_SINGLEDIGIT);
	    }
	  printf("How many points are possible?");
	  done=TRUE;
	  break;
	case QUESTION_MENU:
	  printQuestionMenu();
	  printf("Question#%2d: ",numQuestions+1);
	  break;
	case QUESTION_STOP:
	  return 0;
	  break;
	default:
	  printf("Please enter one of %c,%c,%c,%c,%c,%c,%c,%c,%c.\n",
		 ONE_OUT_OF_8,GLE,TF,ASSIGNED,N_OUT_OF_M,SINGLE_DIGIT,
		 STRING_MATCH,QUESTION_MENU,QUESTION_STOP);
	  printf("Question#%2d: ",numQuestions+1);
	  break;
	}
    }
  questions[numQuestions].type=buf[0];
  if (!(questions[numQuestions].points=getSingleDigit())) 
    {
      fprintf(stderr,"Weird result from getSingleDigit\n");
      exit(E_SINGLEDIGIT);
    }
  return 1;
}

void printModifyQuestionMenu()
{
  printf("(a)dd a question\n");
  printf("(c)hange a question\n");
  printf("(d)elete a question\n");
  printf("(f)inished changing\n");
}

void addQuestion(Question questions[MAX_QUEST],int *numQuestions)
{
  printQuestionMenu();
  if (getNewQuestion(questions,*numQuestions))
    (*numQuestions)=(*numQuestions)+1;
}

void changeQuestion(Question questions[MAX_QUEST],int numQuestions)
{
  int done=FALSE;
  char buf[MAX_LINE_LENGTH];
  printf("Which question do you wish to change?(1-%d) ",numQuestions);
  printf("Or 0 for none\n");
  while(!done)
    {
      scanf("%s",buf);
      if(isdigit(buf[0]) && (atoi(buf) >= 0) && (atoi(buf) <= numQuestions))
	  done=TRUE;
      else
	  printf("Please enter a number between 0 and %d.\n",numQuestions);
    }
  if (atoi(buf) !=0)
    {
      printQuestionMenu();
      getNewQuestion(questions,atoi(buf)-1);
    }
}

void deleteQuestion(Question questions[MAX_QUEST],int *numQuestions)
{
  int done=FALSE,i;
  char buf[MAX_LINE_LENGTH];
  printf("Which question do you wish to delete?(1-%d) ",*numQuestions);
  printf("Or 0 for none\n");
  while(!done)
    {
      scanf("%s",buf);
      if(isdigit(buf[0]) && (atoi(buf) >= 0) && (atoi(buf) <= (*numQuestions)))
	done=TRUE;
      else
	printf("Please enter a number between 0 and %d.\n",*numQuestions);
    }
  if (atoi(buf) !=0)
    {
      for(i=atoi(buf)-1;i<(*numQuestions)-1;i++)
	{
	  questions[i].type=questions[i+1].type;
	  questions[i].points=questions[i+1].points;
	  questions[i].leafs=questions[i+1].leafs;
	}
      (*numQuestions)=(*numQuestions)-1;
    }
}

void modifyQuestions(Question questions[MAX_QUEST],int *numQuestions)
{
  int done=FALSE;
  char buf[MAX_LINE_LENGTH];
  while(!done)
    {
      printQuestions(questions,*numQuestions);
      printModifyQuestionMenu();
      scanf("%s",buf);
      switch(buf[0])
	{
	case 'a':
	  addQuestion(questions,numQuestions);
	  break;
	case 'c':
	  changeQuestion(questions,*numQuestions);
	  break;
	case 'd':
	  deleteQuestion(questions,numQuestions);
	  break;
	case 'f':
	  done=TRUE;
	  break;
	default:
	  printf("Please enter one of a, c, d, f\n");
	  break;
	}
    }
}

int getNumSurveyQuest()
{
  int numQuestions=0,done=FALSE;
  char buf[MAX_LINE_LENGTH];

  printf("Please enter the number of questions on the Survey.\n");
  while(!done)
    {
      scanf("%s",buf);
      if(isdigit(buf[0]))
	{
	  numQuestions=atoi(buf);
	  done=TRUE;
	}
      else
	{
	  printf("Please enter a number.\n");
	}
    }
  return numQuestions;
}

void getQuestionInfo(Question questions[MAX_QUEST],Flags *flags)
{
  int done=FALSE,i;
  if (flags->SurveyMode)
    {
      flags->NumQuestions=getNumSurveyQuest();
      for (i=0;i<flags->NumQuestions;i++)
	{
	  questions[i].type=ASSIGNED;
	  questions[i].points=9;
	  questions[i].leafs=1;
	}
    }
  else
    {
      if (flags->NumQuestions!=0)
	{
	  printQuestions(questions,flags->NumQuestions);
	  if (!wantToChangeQuestions())
	    return;
	}
      else
	{
	  printQuestionMenu();
	  while(!done)
	    if (getNewQuestion(questions,flags->NumQuestions))
	      flags->NumQuestions++;
	    else
	      done=TRUE;
	}
      modifyQuestions(questions,&(flags->NumQuestions));
    }
} 

void getInfo(FILE ** outputFile,FILE **scantron,
	     Question questions[MAX_QUEST],Flags *flags)
{
  char filename[MAX_LINE_LENGTH];

  getClassInfo(flags,scantron);
  sprintf(filename,"records/scorer.output.%d",flags->SetId);
  switch(openOutputFile(filename,outputFile))
    {
    case SCO_FATALERROR:
      exit (E_OPENOUTPUT);
      break;
    case SCO_NEW:
      initValues(questions,flags);
      break;
    case SCO_EXIST:
      getPreviousValues(*outputFile,questions,flags);
      break;
    default:
      fprintf(stderr,"openOutputFile returned an invalid code. Dying\n");
      exit(E_OPENOUTPUTRETURN);
      break;
    }
  getFlags(flags);
  getQuestionInfo(questions,flags);
}

void saveInfo(FILE *outputFile,Question questions[MAX_QUEST],Flags *flags)
{
  char configLine[CONFIG_LINE_LENGTH+2],buf[MAX_LINE_LENGTH],
    buf2[MAX_LINE_LENGTH],logname[MAX_LINE_LENGTH];
  int i;
  for(i=0;i<flags->NumQuestions;i++)
    {
      buf[3*i]=questions[i].type;
      buf[3*i+1]=((char)questions[i].points)+'0';
      buf[3*i+2]=((char)questions[i].leafs)+'0';
    }
  buf[3*i]='\0';
  buf2[0]=((char)flags->CheckPIN)+'0';
  buf2[1]=((char)flags->AnonMode)+'0';
  buf2[2]=((char)flags->CheckSpaces)+'0';
  buf2[3]=((char)flags->SurveyMode)+'0';
  buf2[4]=((char)flags->SurveyHeader)+'0';
  buf2[5]=((char)flags->Pause)+'0';
  buf2[6]=((char)flags->IdFormat)+'0';
  buf2[7]=((char)flags->CheckMultipleMarks)+'0';
  buf2[8]=((char)flags->QueryAboutPID)+'0';
  buf2[9]=((char)flags->log)+'0';
  buf2[10]='\0';
  sprintf(configLine,"%s %d %d %s %f %s",flags->Class,flags->SetId,
	  flags->NumQuestions,buf2,flags->PauseTime,buf);
  for(i=strlen(configLine);i<CONFIG_LINE_LENGTH;i++)
    configLine[i]=' ';
  configLine[CONFIG_LINE_LENGTH]='\n';
  configLine[CONFIG_LINE_LENGTH+1]='\0';
  rewind(outputFile);
  fprintf(outputFile,configLine);

  if(flags->log)
    {
      sprintf(logname,"records/scorer.log.%d",flags->SetId);
      
      flags->logFile=fopen(logname,"w");
      if (flags->logFile == NULL)
	{
	  fprintf(stderr,"Unable to open the logFile %s\n",logname);
	  exit(E_LOGERROR);
	}
    }
}

void logMultipleAnon(PIDPINlist PIDandPINlist[MAX_SECTION_SIZE],
		     int matches[MAX_PID_MATCHES],Student* student,
		     Flags* flags,int maxTotal,int total[MAX_PID_MATCHES])
{
  int i;

  fprintf(flags->logFile,"#Multiple matching PINs for PID %s :",
	  student->answerPID);
  for(i=0;i<MAX_PID_MATCHES;i++)
    {
      if (matches[i]==-1) break;
      fprintf(flags->logFile," %s score: %d,",
	      PIDandPINlist[matches[i]].PID,total[i]);
    }
  
  flags->loggedErrors++;
  fprintf(flags->logFile," picked: %s, serialNumber: %s\n",
	  PIDandPINlist[matches[maxTotal]].PID,student->serialNumber);
  fprintf(flags->logFile,"%s",student->origString);  
}

void logError(Student *student,Flags *flags,int errorCode)
{
  flags->loggedErrors++;
  student->error=1;
  switch(errorCode)
    {
    case LOG_NOPINFOUND:
      fprintf(flags->logFile,"#In Anonymous Mode no Student ID could be found for the PIN encoded, PIN=%s (not graded)",student->PIN);
      break;
    case LOG_PINWRONG:
      if (flags->AnonMode)
	fprintf(flags->logFile,"#The coded PIN had errors (not graded)");
      else
	fprintf(flags->logFile,"#The coded PIN had errors, assumed zero for bad responses");
      break;
    case LOG_STUDENTNOTEXIST:
      fprintf(flags->logFile,"#The Student ID %s was not found in the classl file(not graded)",student->answerPID);
      break;
    case LOG_PINNOTMATCH:
      fprintf(flags->logFile,"#The student coded an incorrect PIN(%s) for the specified PID(%s) using the classl PIN, %04d",student->PIN,student->answerPID,student->classlPIN); 
      break;
    case LOG_BADPID:
      fprintf(flags->logFile,"#The coded Student ID(%s) was not found in the classl file (not graded)",student->answerPID);
      break;
    case LOG_BADPIN:
      fprintf(flags->logFile,"#The coded PIN (%s) was incorrect(notGraded)",student->PIN);
      break;
    case LOG_SPACES:
      fprintf(flags->logFile,"#The student had blank answers");
      break;
    case LOG_MULTIPLEMARKS:
      fprintf(flags->logFile,"#The student had multiple marks on single mark questions");
      break;
    default:
      fprintf(flags->logFile,"#And unknown error %d occured with this student",errorCode);
      break;
    }
  fprintf(flags->logFile," serialNumber: %s.\n",student->serialNumber);
}

/* stolen from allpin.c and modified by Guy Albertelli*/
int buildPIDandPINlist(int setId, PIDPINlist PIDandPINlist[MAX_SECTION_SIZE] )
{
  int i=0,numStudents;
  T_student *curStudent,*headStudent;

  printf("Building lists of CapaIDs and PIDs\n");
  numStudents=capa_get_section(&headStudent, 0);
  curStudent=headStudent;
  for(i=0;curStudent;curStudent=curStudent->s_next,i++)
    {
      strcpy(PIDandPINlist[i].PID,curStudent->s_sn);
      PIDandPINlist[i].PIN=capa_PIN(curStudent->s_sn,setId,0);
    }
  free_students(headStudent);
  printf("\nDone\n");
  return numStudents;
}

int getForm(Student **newStudent,FILE *scantron,
		  Question questions[MAX_QUEST],Flags *flags)
{
  char buffer[MAX_LINE_LENGTH];
  char *eof;
  int i=0,h=0,j=0,q=0,space=0,found,stepsize,pin,result=0,multiplemarks=0;
  int done=FALSE,pinWrong=FALSE;
  static int formNumber;
  char * array;

  if (flags->Pause)
      usleep(flags->PauseTime*1000000);
  while(!done)
    {
      eof=fgets(buffer,MAX_LINE_LENGTH,scantron);
      if (eof==NULL) return GF_EOF;
      if (buffer[0]!='#') done=TRUE;
    }
  flags->linesRead++;
  if (!flags->log)
    printf("The Next line is:\n%s\n",buffer);

#ifdef DEBUG
  printf("Interpreting the line.\n");
#endif

  *newStudent=(Student *)malloc(sizeof(Student));
  if (flags->SurveyMode) 
    /* assign a unique student number since there isn't one on the sheet*/
    switch(flags->IdFormat)
      {
      case ANUMBER_FORMAT:
	sprintf((*newStudent)->answerPID,"a%08d",formNumber++);
	break;
      case SOC_SEC_FORMAT:
	sprintf((*newStudent)->answerPID,"%09d",formNumber++);
	break;
      default:
	fprintf(stderr,"Invalid IdFormat data in interpretForm,%d\n",
		flags->IdFormat);
	exit(E_INVALIDIDFORMAT);
	break;
      }
  else
    strncpy((*newStudent)->answerPID,&buffer[56],9);
  (*newStudent)->answerPID[9]='\0';
  strncpy((*newStudent)->Name,&buffer[40],16);
  (*newStudent)->Name[16]='\0';
  strncpy((*newStudent)->serialNumber,&buffer[3],6);
  (*newStudent)->serialNumber[6]='\0';
  strncpy((*newStudent)->origString,buffer,SCAN_INPUT_LENGTH);
  (*newStudent)->origString[SCAN_INPUT_LENGTH]='\0';
  (*newStudent)->error=0;
  /* i is the current position in buffer and h is the current question*/
  for(i=0;i<flags->NumQuestions;i++)
    {
      array=LETTER;
      stepsize=2;
      switch(questions[i].type)
	{
	case ASSIGNED:
	case SINGLE_DIGIT:
	  /*the first 5 are special for Anon mode, only first 4 for 
	   *otherwise
	   */
	  if (flags->AnonMode) 
	    if (i > 4)
	      array=NUMBER;
	  else
	    if (i > 3)
	      array=NUMBER;
	case ONE_OUT_OF_8:
	  found=0;
	  for(j=(i*10)+76;j<((i+1)*10)+76;j++)
	    {
	      if (buffer[j]=='1') 
		{
		  h=j;
		  found++;
		}
	    }
	  if (found > 1)
	    {
	      (*newStudent)->Answers[i][0]=' ';
	      multiplemarks++;
	    }
	  if (found < 1)
	    {
	      (*newStudent)->Answers[i][0]=' ';
	      space++;
	    }
	  if (found == 1)
	    (*newStudent)->Answers[i][0]=array[h-(i*10+76)];
	  (*newStudent)->Answers[i][1]='\0';
	  break;
	case GLE:
	  stepsize=3;
	case TF:
	  for(j=0; j<questions[i].leafs; j++)
	    {
	      found=0;
	      for(h = (i*10+76)+j*stepsize; h<(i*10+76)+(j+1)*stepsize; h++)
		{
		  if (buffer[h]=='1')
		    {
		      q=h;
		      found++;
		    }
		  if (found > 1)
		    {
		      (*newStudent)->Answers[i][j]=' ';
		      multiplemarks++;
		    }
		  if (found < 1)
		    {
		      (*newStudent)->Answers[i][j]=' ';
		      space++;
		    }
		  if (found == 1)
		    (*newStudent)->Answers[i][j]=array[q-(i*10+76)];
		}
	    }
	  (*newStudent)->Answers[i][questions[i].leafs]='\0';
	  break;
	case N_OUT_OF_M:
	case STRING_MATCH:
	  found=0;
	  for(j=(i*10)+76;j<((i+1)*10)+76;j++)
	    {
	      if (buffer[j]=='1')
		{
		  (*newStudent)->Answers[i][found]=array[j-(i*10+76)];
		  found++;
		}
	    }
	  if (found==0) space++;
	  (*newStudent)->Answers[i][found]='\0';
	  break;
	default:
	  fprintf(stderr,"Invalid question type %c for question %d in Interpret form.\n Dying\n",questions[h].type,h);
	  exit(E_INTREPRETFORM);
	  break;
	}
    }
  pin=0;
  for(i=0;i<4;i++)
    {
      pin*=10;
      switch((*newStudent)->Answers[i][0])
	{
	case 'A': pin+=1;break;
	case 'B': pin+=2;break;
	case 'C': pin+=3;break;
	case 'D': pin+=4;break;
	case 'E': pin+=5;break;
	case 'F': pin+=6;break;
	case 'G': pin+=7;break;
	case 'H': pin+=8;break;
	case 'I': pin+=9;break;
	case 'J': pin+=0;break;
	case ' ':
	default: 
	  if (!flags->log)
	    fprintf(stderr,"Garbage answer '%c' for PIN question %d",
		    (*newStudent)->Answers[i][0],i);
	  result|=GF_PINWRONG;
	  pinWrong=TRUE;
	  pin+=0;break;
	}
    }
  if (pinWrong && flags->log)
    logError((*newStudent),flags,LOG_PINWRONG);
  sprintf((*newStudent)->PIN,"%04d",pin);
  if (space!=0) result|=GF_SPACES;
  if (multiplemarks!=0) result|=GF_MULTIPLEMARKS;
  return result;
}

int gradeQuestion(Question questions[MAX_QUEST],int questionIndex,
		  Problem_t *problem,Student *student,Flags* flags)
{
  int numRight=0,leafs;
  char one=1,zero=0;
  char *ansOn[20],*stuOn[20];
  int i,j;
  int sortArray[256]; 
  char newAnswer[MAX_LINE_LENGTH],*oldAnswer;

  switch(questions[questionIndex].type)
    {
    case ONE_OUT_OF_8:
    case GLE:
    case TF:
    case SINGLE_DIGIT:
      if (!flags->log)
	printf("The correct answer:%10s  The student's answer:%10s, \t",
	       problem->answer,student->Answers[questionIndex]);
      for(leafs=0;problem->answer[leafs]!='\0';leafs++)
	if (problem->answer[leafs]==student->Answers[questionIndex][leafs])
	  numRight++;
      if (!flags->log)
	printf("%d right\n",numRight);
      break;
    case ASSIGNED:
      if (!flags->log)
	printf("The student got a %s out of %d\n",
	       student->Answers[questionIndex],
	       questions[questionIndex].points);
      if (isspace(student->Answers[questionIndex][0]))
	numRight=0;
      else
	numRight=(int)(student->Answers[questionIndex][0]-'0');
      break;
    case N_OUT_OF_M:
      if (!flags->log)
	printf("The correct answer:%10s  The student's answer:%10s, \t",
	       problem->answer,student->Answers[questionIndex]);
      if (problem->ans_type == ANSWER_IS_CHOICE) {
	for(i=0;i<255;i++) sortArray[i]=0;
	for(i=0;i< strlen(problem->answer);i++)
	  sortArray[(int)problem->answer[i]]=1;
	for(i=0,j=0;i<255;i++) {
	  if (sortArray[i]) {
	    newAnswer[j]=i;
	    j++;
	  }
	}
	newAnswer[j]='\0';
	if (!flags->log) 
	  printf("\nThe sorted correct answer:%10s\t\t\t",newAnswer);
	oldAnswer=problem->answer;
	problem->answer=newAnswer;
      }
      for(leafs=0;questions[questionIndex].leafs>leafs;leafs++)
	{
	  ansOn[leafs]=strchr(problem->answer,('A'+(char)leafs));
	}
      for(leafs=0;questions[questionIndex].leafs>leafs;leafs++)
	{
	  if (ansOn[leafs] != NULL ) 
	    ansOn[leafs]=&one; 
	  else
	    ansOn[leafs]=&zero;
	}
      for(leafs=0;questions[questionIndex].leafs>leafs;leafs++)
	{
	  stuOn[leafs]=strchr(student->Answers[questionIndex],
			      ('A'+(char)leafs));
	}
      for(leafs=0;questions[questionIndex].leafs>leafs;leafs++)
	{
	  if (stuOn[leafs] != NULL)
	    stuOn[leafs]=&one; 
	  else 
	    stuOn[leafs]=&zero;
	}
      for(leafs=0;questions[questionIndex].leafs>leafs;leafs++)
	if (ansOn[leafs] == stuOn[leafs]) 
	  numRight++;
      if (!flags->log)
	printf("%d right\n",numRight);
      if (problem->ans_type == ANSWER_IS_CHOICE) problem->answer=oldAnswer;
      break;
    case STRING_MATCH:
      if (!flags->log)
	printf("The correct answer:%10s  The student's answer:%10s, ",
	       problem->answer,student->Answers[questionIndex]);
      if (problem->ans_type == ANSWER_IS_CHOICE) {
	for(i=0;i<255;i++) sortArray[i]=0;
	for(i=0;i< strlen(problem->answer);i++)
	  sortArray[(int)problem->answer[i]]=1;
	for(i=0,j=0;i<255;i++) {
	  if (sortArray[i]) {
	    newAnswer[j]=i;
	    j++;
	  }
	}
	newAnswer[j]='\0';
	if (!flags->log) 
	  printf("\nThe sorted correct answer:%10s\t\t\t",newAnswer);
	oldAnswer=problem->answer;
	problem->answer=newAnswer;
      }
      if (!(strcasecmp(problem->answer,student->Answers[questionIndex]))) {
	if (!flags->log) printf("Answer is correct\n");
	numRight=questions[questionIndex].points;
      } else {
	if (!flags->log) printf("Answer is wrong\n");
	numRight=0;
      }
      if (problem->ans_type == ANSWER_IS_CHOICE) problem->answer=oldAnswer;
      break;
    default:
      fprintf(stderr,"Unknown question type while grading, %c.\nDying.\n",
	      questions[questionIndex].type);
      exit(E_UNKNOWN_QTYPE);
      break;
    }
  return numRight;
}

int surveyQuestion(Question questions[MAX_QUEST],int questionIndex,
		  Problem_t *problem,Student *student,Flags* flags)
{
  int numRight=0;

  switch(questions[questionIndex].type)
    {
    case ASSIGNED:
      if (!flags->log)
	printf("The student responded with %s\n",student->Answers[questionIndex]);
      if (isspace(student->Answers[questionIndex][0]))
	numRight=0;
      else
	numRight=(int)(student->Answers[questionIndex][0]-'0');
      break;
    case ONE_OUT_OF_8:
    case GLE:
    case TF:
    case SINGLE_DIGIT:
    case N_OUT_OF_M:
    case STRING_MATCH:
    default:
      fprintf(stderr,"Illegal question type while in SurveyMode, %c.\nDying.\n",
	      questions[questionIndex].type);
      exit(E_UNKNOWN_QTYPE);
      break;
    }
  return numRight;
}

long getScorerEntry(FILE *outputFile,char *PID)
{
  char oneline[MAX_LINE_LENGTH],fmtbuf[MAX_LINE_LENGTH],
    studentNumber[MAX_STUDENT_NUMBER];
  int done=FALSE,found=FALSE,offset=0,len=0,next_r=0;

  rewind(outputFile);
  sprintf(fmtbuf,"%%%dc",MAX_STUDENT_NUMBER);
  while(!done)
    {
      done=!fgets(oneline,MAX_LINE_LENGTH-1,outputFile);
      len=strlen(oneline);
      if (!done)
	{
	  sscanf(oneline,fmtbuf,studentNumber);
	  if (strncasecmp(studentNumber,PID,MAX_STUDENT_NUMBER)==0)
	    {
	      next_r=ftell(outputFile);
	      offset = next_r-len;
	      done=TRUE;
	      found=TRUE;
	    }
	}
      else
	{
	  fseek(outputFile,0L,SEEK_END);
	  offset=ftell(outputFile);
	  fseek(outputFile,-1L,SEEK_END);
	  while (fgetc(outputFile)=='\n')
	    {
	      offset--;
	      fseek(outputFile,offset,SEEK_SET);
	    }
	  offset= offset+2;
	  found=FALSE;
	  done=TRUE;
	}
    }
  if(!found) offset=-offset;
  return offset;
}
	  
void setScorerEntry(FILE * outputFile,char* answerPID,char* name,
		    char* answers,int score, int section, 
		    char* answerstring,char* questionPID, 
		    char* serialNumber,int offset)
{
  int len=0;
  char buf[MAX_LINE_LENGTH];
  
  rewind(outputFile);
  sprintf(buf,"%s %-30s %s %3d %2d %s %s %s\n",answerPID,name,answers,
	  score,section,answerstring,questionPID,serialNumber);
  len=strlen(buf);
  fseek(outputFile,abs(offset),0);
  if(!fwrite(buf,len,1,outputFile))
    fprintf(stderr,"Failed write.\n");
}

void saveForm(Student *student,FILE *outputFile,
	       Question questions[MAX_QUEST],Flags *flags)
{
  int result,capaQuestions,questionIndex,numRight,total=0;
  int offset;
  Problem_t *curProblem,*headProblem;
  char answerstring[MAX_LINE_LENGTH],grade[MAX_LINE_LENGTH];
  char buf[MAX_LINE_LENGTH];
  T_student capaStudent;

  if (!flags->SurveyMode)
    {
      switch(capa_get_student(student->answerPID, &capaStudent))
	{
	case 1: break;
	case 0:
	  if (flags->log)
	    logError(student,flags,LOG_STUDENTNOTEXIST);
	  else
	    {
	      fprintf(stderr,"Student %s was not found.\n",student->answerPID);
	      printf("Type start to continue");
	      scanf("%s",buf);
	    }
	  return;
	  break;
	case -1:
	  fprintf(stderr,"Unable to find the classl file while grading\n");
	  exit(E_CAPA_GET_STUDENT); break;
	default:
	  fprintf(stderr,"Unknow error from capa_get_student while grading\n");
	  exit(E_CAPA_GET_STUDENT); break;
	}	
      result=capa_parse(flags->SetId,&headProblem,student->questionPID,
			&capaQuestions);
      curProblem=headProblem;
      if (result==0 || result == -1)
	{
	  fprintf(stderr,"The Parse failed: %d\nDying\n",result);
	  exit(E_PARSER);
	}
      else if (result != flags->NumQuestions)
	{
	  fprintf(stderr,"The parser found %d questions, there were supposed to be %d questions.\nDying\n",result,flags->NumQuestions);
	  exit(E_PARSER_DIFFERENT);
	}
      for(questionIndex=0;questionIndex<flags->NumQuestions;questionIndex++)
	{
	  numRight=gradeQuestion(questions,questionIndex,curProblem,
				 student,flags);
	  total+=numRight;
	  grade[questionIndex]='0'+(char)numRight;
	  curProblem=curProblem->next;
	}
      grade[questionIndex]='\0';
      printf("Total right for student %s is %d\n\n",capaStudent.s_nm,total);
      free_problems(headProblem);
    }
  else
    {
      strcpy(capaStudent.s_nm,"Unknown                       ");
      capaStudent.s_sec=0;
      headProblem=curProblem=NULL;      
      for(questionIndex=0;questionIndex<flags->NumQuestions;questionIndex++)
	{
	  numRight=surveyQuestion(questions,questionIndex,curProblem,
				  student,flags);
	  total+=numRight;
	  grade[questionIndex]='0'+(char)numRight;
	} 
      grade[questionIndex]='\0';
    }
  
  answerstring[0]='\0';

  for(questionIndex=0;questionIndex<flags->NumQuestions;questionIndex++)
    strcat(answerstring,student->Answers[questionIndex]);

  offset=getScorerEntry(outputFile,student->answerPID);  
     
  setScorerEntry(outputFile,student->answerPID,capaStudent.s_nm,grade,
		  total,capaStudent.s_sec,answerstring,student->questionPID,
		  student->serialNumber,offset);
}

int getScore(Student *student,Question questions[MAX_QUEST],Flags *flags,
	     char* grade,int gradingMethod)
{
  int score=0,numRight=0,tempScore,i,leafs,points,unit;
  for(i=0;i<flags->NumQuestions;i++)
    {
      switch(questions[i].type)
	{
	case ONE_OUT_OF_8:
	case SINGLE_DIGIT:
	  numRight= (int) (grade[i]-'0');
	  score+=numRight*questions[i].points;
	  break;
	case STRING_MATCH:
	  /*for STRING_MATCH the score is stroed as the NumRight*/
	  numRight= (int) (grade[i]-'0');
	  score+=numRight;
	  break;
	case GLE:
	case TF:
	case N_OUT_OF_M:
	  numRight=(int) (grade[i]-'0');
	  leafs=questions[i].leafs;
	  points=questions[i].points;
	  unit=(int)ceil((double)points/(double)leafs);
	  if (unit==0) unit=points;
	  switch (gradingMethod)
	    {
	    case CAPA_METHOD:
	      tempScore=points-(2*unit*(leafs-numRight));
	      break;
	    case LENIENT_METHOD:
	      tempScore=points-(unit*(leafs-numRight));
	      break;
	    case STRICT:
	      if (numRight==leafs) tempScore=points;
	      else tempScore=0;
	      break;
	    default:
	      fprintf(stderr,"Unknown grading Method. %d\n",gradingMethod);
	      exit(E_GRADINGMETHOD);
	      break;
	    }
	  if (tempScore<0)
	    tempScore=0;
	  score+=tempScore;
	  break;
	case ASSIGNED:
	  numRight= (int) (grade[i]-'0');
	  score+=numRight;
	  break;
	default:
	  fprintf(stderr,"Unknown question type %c\n",questions[i].type);
	  break;
	}
    }
  return score;
}

void saveAnonForm(Student *student,FILE *outputFile,
		  Question questions[MAX_QUEST],
		  PIDPINlist PIDandPINlist[MAX_SECTION_SIZE],
		  Flags *flags)
{
  int i,j=0,matches[MAX_PID_MATCHES],done=FALSE;
  int result,capaQuestions,questionIndex,numRight,total[MAX_PID_MATCHES];
  int offset,maxTotal=0,score,correctID=-1;
  Problem_t *curProblem,*headProblem;
  char answerstring[MAX_LINE_LENGTH],buf[MAX_LINE_LENGTH],
    grade[MAX_PID_MATCHES][MAX_LINE_LENGTH],buffer[MAX_LINE_LENGTH];
  T_student capaStudent;

  if (!flags->log)
    printf("Attempting to find a student with CAPA PIN %s.\n",student->PIN);

  for(i=0;i<flags->NumOfStudents;i++)
    {
      if (atoi(student->PIN)==PIDandPINlist[i].PIN)
	{
	  matches[j]=i;
	  j++;
	}
    }
  matches[j]=-1;
  switch(j)
    {
    case 0:
      if(flags->log)
	logError(student,flags,LOG_NOPINFOUND);  
      else
	{
	  printf("No match for PIN %s\n",student->PIN);
	  printf("The current form's PIN is incorrect.\n");
	  printf("Please type start to continue.\n");
	  scanf("%s",buf);
	}
      return;
      break;
    case 1:
      if (!flags->log)
	printf("Only one match assuming PID %s\n",
	       PIDandPINlist[matches[0]].PID);
      strcpy(student->questionPID,PIDandPINlist[matches[0]].PID);
      saveForm(student,outputFile,questions,flags);
      return;
      break;
    default:
      break;
    }

  /* Only get here if there is more than one valid PID*/
  if (!flags->log)
    printf("Found %d matches.\n",j);

  maxTotal=-1;
  for(i=0;i<MAX_PID_MATCHES;i++)
    {
      total[i]=0;
      if (matches[i]==-1) break;
      switch(capa_get_student(student->answerPID, &capaStudent))
	{
	case 1: break;
	case 0:
	  if (flags->log)
	    logError(student,flags,LOG_STUDENTNOTEXIST);
	  else
	    {
	      fprintf(stderr,"Student %s was not found.\n",student->answerPID);
	      printf("Type start to continue");
	      scanf("%s",buf);
	    }
	  return;
	  break;
	case -1:
	  fprintf(stderr,"Unable to find the classl file while grading\n");
	  exit(E_CAPA_GET_STUDENT); break;
	default:
	  fprintf(stderr,"Unknown error from capa_get_student while grading\n");
	  exit(E_CAPA_GET_STUDENT); break;
	}	
      strcpy(student->questionPID,PIDandPINlist[matches[i]].PID);
      result=capa_parse(flags->SetId,&headProblem,student->questionPID,
			&capaQuestions);
      curProblem=headProblem;
      if (result==0 || result == -1)
	{
	  fprintf(stderr,"The Parse failed: %d\nDying\n",result);
	  exit(E_PARSER);
	}
      else if (result != flags->NumQuestions)
	{
	  fprintf(stderr,"The parser found %d questions, there were supposed to be %d questions.\nDying\n",result,flags->NumQuestions);
	  exit(E_PARSER_DIFFERENT);
	}
      for(questionIndex=0;questionIndex<flags->NumQuestions;questionIndex++)
	{
	  numRight=gradeQuestion(questions,questionIndex,curProblem,
				 student,flags);
	  if (questionIndex==4) 
	    if (numRight==1)
	      correctID=i;

	  total[i]+=numRight;
	  grade[i][questionIndex]='0'+(char)numRight;
	  curProblem=curProblem->next;
	}
      if (total[i] > total[maxTotal]) maxTotal=i;
      grade[i][questionIndex]='\0';
      
      /*printf("Total right for PID %s is %d\n",PIDandPINlist[matches[i]].PID,
	     total[i]);*/
      free_problems(headProblem);  
    }

  if (correctID!=-1)
    {
      answerstring[0]='\0';
      for(questionIndex=0;questionIndex<flags->NumQuestions;questionIndex++)
	strcat(answerstring,student->Answers[questionIndex]);
      
      offset=getScorerEntry(outputFile,student->answerPID);  

      printf("Total right for student %s is %d\n\n",capaStudent.s_nm,
	     total[correctID]);
      setScorerEntry(outputFile,student->answerPID,capaStudent.s_nm,
		     grade[correctID],total[correctID],capaStudent.s_sec,
		     answerstring,student->questionPID,student->serialNumber,
		     offset);
      return;
    }

  if (!flags->QueryAboutPID && flags->log) 
    {
      logMultipleAnon(PIDandPINlist,matches,student,flags,maxTotal,total);

      answerstring[0]='\0';
      for(questionIndex=0;questionIndex<flags->NumQuestions;questionIndex++)
	strcat(answerstring,student->Answers[questionIndex]);
      
      offset=getScorerEntry(outputFile,student->answerPID);  

      printf("Total right for student %s is %d\n\n",capaStudent.s_nm,
	     total[maxTotal]);
      setScorerEntry(outputFile,student->answerPID,capaStudent.s_nm,
		     grade[maxTotal],total[maxTotal],capaStudent.s_sec,
		     answerstring,student->questionPID,student->serialNumber,
		     offset);
    }
  else
    {
      while(!done)
	{
	  printf("Please press\n");
	  for(i=0;i<MAX_PID_MATCHES;i++)
	    {
	      if (matches[i]==-1)
		break;
	      
	      printf("%d) for student number %s ",i+1,
		     PIDandPINlist[matches[i]].PID);
	      strcpy(student->questionPID,PIDandPINlist[matches[i]].PID);
	      score=getScore(student,questions,flags,grade[i],
			     LENIENT_METHOD);
	      printf(" Lenient: %3d ",score);
	      score=getScore(student,questions,flags,grade[i],
			     CAPA_METHOD);
	      printf(" CAPA: %3d ",score);
	      score=getScore(student,questions,flags,grade[i],
			     STRICT);
	      printf(" Strict: %3d \n",score);
	      
	    }
	  scanf("%s",buffer);
	  if (isdigit(buffer[0]) && (atoi(buffer) < (j+1)) && 
	      (atoi(buffer) > 0))
	    {
	      strcpy(student->questionPID,
		     PIDandPINlist[matches[atoi(buffer)-1]].PID);
	      saveForm(student,outputFile,questions,flags);
	      done=TRUE;
	    }
	}
    }
}

int checkForm(Student * student,Flags *flags)
{
  int error,pin;
  T_student capaStudent;

#ifdef DEBUG
  int i,j;
  printf("PID:\t%s\nPIN:\t%s\nName:\t%s\n",
	 student->answerPID,student->PIN,student->Name);

  for(i=0,j=0;i<flags->NumQuestions;i++)
    {
      printf("Answer %d: %s\n",i+1,student->Answers[i]);
    }
#endif /*DEBUG*/

  error=capa_get_student(student->answerPID, &capaStudent);
  switch(error)
    {
    case 1:
      if (!flags->log)
	printf("The current student is %s from section %d\n",
	       capaStudent.s_nm,capaStudent.s_sec);
      break;
    case 0:
      if (!flags->log)
	{
	  printf("Error Finding the student.\n");
	  printf("The Scantron reported the PID:%s\n",student->answerPID);
	  printf("But this was not found in the classl file.\n");
	}
      return CF_STID;
      break;
    case -1:
      return CF_CLASSL;
      break;
    default:
      fprintf(stderr,"capa_get_student returned an invalid result");
      fprintf(stderr,"in CheckForm.\n Error=%d. Dying\n",error);
      exit(E_CAPA_GET_STUDENT);
      break;
    }
  
  if (flags->CheckPIN && !flags->AnonMode)
    {
      pin=capa_PIN(student->answerPID,flags->SetId,0);
      if (pin!=atoi(student->PIN))
	{
	  if (flags->log)
	    {
	      student->classlPIN=pin;
	      logError(student,flags,LOG_PINNOTMATCH);
	      sprintf(student->PIN,"%04d",pin);
	    }
	  else
	    {
	      printf("There is an error with the students PIN:\n");
	      printf("The Scantron reported:%s, The Classl file has:%d\n",
		     student->PIN,pin);
	      fprintf(stderr,"If you wish to use what the Classl file reports\n");
	      fprintf(stderr,"enter y, else enter n.\n");
	      if (getYesOrNo()==YES)
		sprintf(student->PIN,"%04d",pin);
	      else
		return CF_PIN;
	    }
	}
    }
  return CF_NOERROR;
}

int handleCheckForm(Student *student,FILE *outputFile,
		   PIDPINlist PIDandPINlist[MAX_SECTION_SIZE],
		   Question questions[MAX_QUEST],Flags *flags)
{
  int done=FALSE,error;
  char buf[MAX_LINE_LENGTH];

  switch(error=checkForm(student,flags))
    {
    case CF_STID:
      if (flags->log)
	logError(student,flags,LOG_BADPID);
      else
	{
	  printf("The current form's Student Id is incorrect.\n");
	  printf("Please type start to continue.\n");
	  scanf("%s",buf);
	}
      break;
    case CF_CLASSL:
      fprintf(stderr,"The classl file was not found in the");
      fprintf(stderr," current directory.\n");
      fprintf(stderr,"Please try again.\n");
      done=TRUE;
      break;
    case CF_PIN:
      if (flags->log)
	logError(student,flags,LOG_BADPIN);
      else
	{
	  fprintf(stderr,"The current form's PIN is incorrect.\n");
	  fprintf(stderr,"Please type start to continue.\n");
	  scanf("%s",buf);
	}
      break;
    case CF_NOERROR:
      printf("Grading and Saving Student %s Serial#: %s\n",
	     student->answerPID,student->serialNumber);
      if (flags->AnonMode) 
	saveAnonForm(student,outputFile,questions,PIDandPINlist,flags);
      else
	{
	  strcpy(student->questionPID,student->answerPID);
	  saveForm(student,outputFile,questions,flags);
	}
      break;
    default:
      fprintf(stderr,"Unimplemented error in checkForm %d\n",error);
      exit(E_CHECKFORM);
      break;
    }
  return done;
}

int handleGetFormStatus(int status,Student *student,FILE *outputFile,
		   PIDPINlist PIDandPINlist[MAX_SECTION_SIZE],
		   Question questions[MAX_QUEST],Flags *flags)
{
  int done=FALSE,save=TRUE;
  if (status & GF_SPACES)
    if (flags->CheckSpaces)
      {
	if (flags->log)
	  logError(student,flags,LOG_SPACES);
	else
	  {
	    printf("The current form appears to have some questions left\n");
	    printf("blank. Please enter yes if you wish to continue \n");
	    printf("grading of this form.\n");
	    if (getYesOrNo()==YES)
	      ;
	    else 
	      save=FALSE;
	  }
      }

  if(status & GF_MULTIPLEMARKS)
    if (flags->CheckMultipleMarks)
      {
	if (flags->log)
	  logError(student,flags,LOG_MULTIPLEMARKS);
	else
	  {
	    printf("The current form appears to have some questions with\n");
	    printf("multiple marks on lines that should have only one.\n");
	    printf("Please enter yes if you wish to continue grading of this form.\n");
	    if (getYesOrNo()==YES)
	      ;
	    else 
	      save=FALSE;
	  }
      }
  
  if (status == GF_EOF)
    done=TRUE;

  if ((status & GF_PINWRONG) && flags->AnonMode) return done;
   
  if (!done && save) 
    if (!flags->SurveyMode)
      {
#ifdef DEBUG
	printf("Checking form.\n");
#endif
	done=handleCheckForm(student,outputFile,PIDandPINlist,questions,
			     flags);
      }
    else
      if (flags->AnonMode)
	saveAnonForm(student,outputFile,questions,PIDandPINlist,flags);
      else
	{
	  strcpy(student->questionPID,student->answerPID);
	  saveForm(student,outputFile,questions,flags);
	}
  return done;
}

void processForms(FILE *outputFile, FILE *scantron,
		  Question questions[MAX_QUEST], Flags *flags)
{

  int done=FALSE;
  int status;
  Student *student=NULL;
  PIDPINlist PIDandPINlist[MAX_SECTION_SIZE];

  if (flags->AnonMode)
    {
      flags->NumOfStudents=buildPIDandPINlist(flags->SetId,PIDandPINlist);
      if (flags->NumOfStudents==0)
	{
	  fprintf(stderr,"buildPIDandPINlists returned 0 students.");
	  exit(E_BUILDPIDPIN);
	}
    }
  
  while(!done)
    {
#ifdef DEBUG
      printf("Reading in a new form.\n");
#endif
      status=getForm(&student,scantron,questions,flags);
      done=handleGetFormStatus(status,student,outputFile,PIDandPINlist,
			       questions,flags);
      if (student != NULL)
	{
	  if (student->error) 
	    fprintf(flags->logFile,"%s",student->origString);
	  free(student);
	}
      student=NULL;
    }
}

void printResults(Flags* flags)
{
  printf("Number scanned %d, number errors logged %d\n.",
	 flags->linesRead,flags->loggedErrors);
}

int main(int argc, char **argv)
{
  FILE *outputFile;
  Question questions[MAX_QUEST];
  Flags flags;
  FILE *scantron;
  
  Parsemode_f=ASCII_MODE;
  flags.linesRead=0;
  flags.loggedErrors=0;

  initScreen();
  getInfo(&outputFile,&scantron,questions,&flags);
  saveInfo(outputFile,questions,&flags);
  processForms(outputFile,scantron,questions,&flags);
  printResults(&flags);
  return 0;
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>