As you may have noticed, there are currently some 5000+ students writing examinations at this University. As you can imagine, some considerable effort is required of the academic staff to assess the some 25000+ examination scripts that will be generated by these students - something that has to be done under extreme pressure to enable the results to be released in time to spoil the holidays of some students and delight the parents of some others. What you may not know is that a crucial part of this process - and one that has to be accomplished over a period of only two or three days immediately prior to the release of results - is an analysis by the Deans of the Faculties of the collated results, so that students who are to be excluded, congratulated, awarded degrees, put on probation and so on can be identified and their results annotated with these decisions.
Over the years Yours Truly has been instrumental in designing computer programs to help with this "Dean's Analysis". For some years this has taken the preparation of a huge text file of summaries for each student, on the lines of that illustrated below. These files can run to over 800 pages of closely printed small text.
Fayled-Badleigh, HE 10L9876 BSCS 3 (3) 39.0 Pts 14 s/crs (Fails G7) INF 301 1 4207301 Information Systems 301 2B 61 INF 302 2 4207302 Information Systems 302 2B 65 CSC 201 1 5101201 Computer Science 201 F2 38 CSC 202 2 5101202 Computer Science 202 DPR MAT 1C1 1 540101A Mathematics 1C1 F1 48 F1 or F1S ? Passed 2/5 courses outright. Average mark 42.00 Weighted 42.0 +++++++++ OVER HALF THE COURSES FAILED +++++ Full firsts 0, Full upper seconds 0, Full credits 1, Full F1 0, AEG 0, Supps 0, DPR 1 +++++++++ On probation - READMIT AP: MUST COMPLETE BSCS 2 IN 2012 Has ACC 101 2A ACC 102 3 CSC 101 3 CSC 102 3 ECO 1 ACR ECO 101 3 Has INF 2 ACR INF 202 2A MAN 101 2B MAN 102 2B PHY 1E2 3 STA 1D 3 1 Acc ** CSc ** Eco ** Man ** St1D * PhyE * (10) 2 Inf ** ( 2) 3 Inf ** ( 2)
More recently the program that produces these summaries has also tried to assess or predict a set of actions for each student that the Deans will recommend to their Faculty Boards for approval. In principle this is not too difficult - after all, students who are deserving of congratulations are easily identified, as are those who, sadly, have failed too many courses to be allowed to return.
In practice, one has a maintenance nightmare. Hard-coding the criteria that apply (differently) to each degree, each year of study, each faculty, into a program that now runs to many thousand lines of C# code, demands the attention of a grey haired retired programmer, who may not always be available for last minute, dangerous hacks to the code. So this ASP (Ancient Skilled Programmer) has recommended that the Deans be asked to draw up a specification of the actions that should be applied to the students in their Faculty, on the lines of the following example (found in the file criteria.1 - more examples are to be found in your "kit"):
Humanities FirstYear : Exclude : Total_Credits < 2 and (Average < 40.0 or DPR >=2); Lowest_Mark < 10. Merit_List : Average > 75 and Lowest_Mark > 60 and Year_Firsts >= 2 and Fails = 0. ThirdYear : AP_Complete_In_One : Total_Credits < 10.0 and Years_Here > 3. AnyYear : Congratulate : Weighted_Average > 90.0 and Fails = 0.
This specification can act as input to a converter program that will read it and construct a C# method (or an equivalent Java version, which would be fairly similar) on the lines of the following:
class ActionSetBuilder { public static void BuildSet(Student s, OutFile logFile) { // Build up a set of numeric tags for Student s based on an analysis of his or her record // logFile may be used to record problems, results, anomalies (none demonstrated here). s.actions = new IntSet(); if (s.academicYear == 1) { if (s.fullTotalCredits < 2 && (s.rawAverage < 40.0 || s.DPRthisYear >= 2)) s.actions.Incl(10); if (s.lowestMark < 10) s.actions.Incl(10); if (s.rawAverage > 75 && s.lowestMark > 60 && s.yearFirstsThisYear >= 2 && s.failsThisYear == 0) s.actions.Incl(16); } if (s.academicYear == 3) { if (s.fullTotalCredits < 10.0 && s.yearsOfStudy > 3) s.actions.Incl(21); } { if (s.weightedAverage > 90.0 && s.failsThisYear == 0) s.actions.Incl(11); } } // ActionSetBuilder.BuildSet } // Class ActionSetBuilder
The method thus generated can then easily be compiled along with the rest of the source of the RAP (Result Analyser Program). If the criteria are augmented, or changed in some other way, then the revised specification file can easily be "recompiled" into C# code and then the complete RAP recompiled with the new method. Note that the Deans who design the criteria specifications do not have to be C# or Java programmers - they simply have to be able to write specifications in a simple, though rigid form, using identifiers that they would find published in a guide, with examples like the one just given. These identifiers, as can be seen from the example, correspond closely to fields in an object s of the Student class that will be populated from the university database as the the RAP is executed, passing each in turn to ActionSetBuilder.BuildSet(s, logFile).
The ASP can't complete this on his own in the time available. Only some 24 hours remain until the deadline, and so he has made you an Offer You Can't Refuse to join the team to help him.
It is suggested that, as an MYSP (Much Younger Skilled Programmer), you proceed as follows:
In adopting this approach you can (and should) make use of the files provided in the examination kit free1j.zip (Java version) or free1c.zip (C# version) which you will find on the course website. In particular, you will find
public class Student { public string faculty; // Faculty for this student public string name; // 30 character student name and initials public string studentNumber; // 7 character "student number" eg 63T0844 (genuine!) ... // overall progress for this and previous years public double fullPrevious; // Full credit score for all previous years of study public double fullCurrent; // Full credit score for current year of study public double fullTotalCredits; // Total number of full credits earned towards the degree so far // for the current year of study public int coursesThisYear; // Number of courses for which student is registered public int semCreditsThisYear; // Semester credits earned during current year public int yearCreditsThisYear; // Full year credits earned during current year public int passesThisYear; // Number of courses passed this year public int firstsThisYear; // Number of courses passed this year at 75% and above public int semFirstsThisYear; // Number of semester courses passed at 75% and above public int yearFirstsThisYear; // Number of full courses passed at 75% and above public int suppsThisYear; // Number of courses failed, but supplementary exam recommended ... public IntSet defined = new IntSet(); // A set of integers corresponding to actions that might be applied // to this student public IntSet actions = new IntSet(); // A set of integers corresponding to actions that are recommended // to the Dean when RAP deals with this student public string actionString; // Summary of recommendation actions as a string public Student () { // Empty constructor for testbed applications ... } // Student Constructor () public static void ReadStudent (InFile achievementsFile, Student s) { // Developed for testbed applications - read the details of a single student s from the achievementsFile. // Each record occupies a single (long) line of text. In a real application the student record would, // of course, be constructed after processing a much bigger file of raw results. // // This method will allow you to fake the analysis of a representative set of student results, one at // a time. Some sample "achievements" files are supplied in the exam kit. // ... } // Student.ReadStudent(InFile, s) public static void WriteStudent(OutFile output, Student s) { // Reflect the data for student s on an output text file - for testing applications ... } // Student.WriteStudent public static void WriteLabelledStudent(OutFile output, Student s) { // Reflect the data for student s on an output text file - for testing applications // (labelled for ease of lookup). ... } // Student.WriteLabelledStudent } // class Student
public class Types { // Identifier (and expression) types. // The usual named "magic numbers" approach. public const int noType = 0, intType = 1, boolType = 2, doubleType = 3, stringType = 4; } // end Types public class Kinds { // Identifier kinds public const int noKind = 0, faculty = 1, degree = 2, field = 3, action = 4; } // end Kinds public class Entry { // Entries in the symbol table are objects of this class. // Fields are all declared public for simplicity. // public bool declared; // true for all except sentinel entry public string name; public string codedName = ""; public int type = Types.noType; public int kind = Kinds.noKind; // Not all of the following are relavant to each kind of entry, but rather // than spawn a whole lot of subtypes, we simply introduce all of them. public int minYears = 0; public double minScore = 10.0; public int actionNumber = 99; } // end Entry public class Table { // The table might include a dummy sentinel entry to handle undeclared // entries in a simple familiar way. public static void Insert(Entry entry) { // Adds entry to symbol table // ... } // Table.Insert public static Entry Find(string name) { // Searches table for an entry matching name. If found then return that // entry; otherwise return the sentinel entry (marked as not declared). // ... return null; // dummy to be replaced // ... } // Table.Find public static void InitTable(InFile tableFile) { // Clears table, sets up sentinel entry, and fills the rest of the table from tableFile Entry sentinelEntry = new Entry(); sentinelEntry.name = ""; sentinelEntry.type = Types.noType; sentinelEntry.kind = Kinds.field; sentinelEntry.declared = false; Insert(sentinelEntry); // ... } // Table.InitTable } // end Table
# Dictionary of identifiers used in the examination results analyser prototype # # P.D. Terry, Rhodes University, 2013 # First release 2013/09/30 # Latest release 2013/10/25 # Lines starting with # in column 1 are treated as comments # Completely blank lines are not permitted # Lines are fixed format # # Each of the identifiers is associated with a Kind - one of # # (Faculty, Degree, Field, Action) # # and has an associated Type (just as identifiers in most programming # languages are of kinds like # # (Constant, Variable, Function, Class) # # and have a type as one of (int, bool, char, double, string)). # # Firstly, we have a list of Faculty names # (Width in parentheses, _ denotes a single space separator) # # Kind (14) _Name (28) _Type (9) _ Faculty Humanities string H Faculty Science string S ... # Next we have a list of degree codes. Each degree code is followed by # the minimum number of years it should take to get the degree, and the # score of credits that need to be earned, measured in "full credits". # In many cases completing a degree is more loosely defined in terms of a # score of "semester credits" that must be obtained. Naturally one # "full credit" for, say, ENG 3 contributes the equivalent of the two # "semester credits" for, say, CSC 301 + CSC 302. # # Kind (14) _Name (28) _Type (9) _Years MinScore # Degree BA string 3 10.0 Degree BSS string 3 10.0 ... # Next comes a list of field names. The first of these (in the # second column) gives names that the Dean could use in criteria like # # Ac_Yr = 1 and Passes < 2 # Year_Firsts > 2 and Weighted_Average > 69.5 # # which might be criteria for exclusion or congratulation, respectively. # # The third column gives the type of the field in the Student object and the fourth # column gives the name of that field as it would be found in the corresponding C# # or Java code for a Student object s. For the examples above this code would be # # s.academicYear == 1 && s.passesThisYear < 2 # s.yearFirstsThisYear > 2 && s.weightedAverage > 69.5 # # Kind (14) _Name (28) _Type (9) _s.Field # Field Ac_Yr int s.academicYear Field Passes int s.passesThisYear Field Year_Firsts int s.yearFirstsThisYear Field Weighted_Average double s.weightedAverage ... # Finally we have a list of possible actions that the program should select # for a student, based on conditions of the sort demonstrated previously. # None of these is pre-selected. The distinct numbers in the third column are # to be included in a set of the actions applicable to a given student. # Action Exclude bool 10 Action Congratulate bool 11 Action Probation bool 12 ... Action Weak bool 25 # Action INCONSISTENCY bool 0
CMAKE converter
will run Coco/R to create the Converter, Scanner and Parser sources and then invoke the C# or Java compiler to build the Converter program in the usual way. Secondly,
CONVERTER criteriaFile for example CONVERTER criteria.0
will run your converter program against the criteria file to create the ActionSetBuilder classs, the key component of which is the BuildSet method, which you should look at in an editor at least. Thirdly,
GoRAP fac for example GoRAP hum or GoRAP sci
will compile the test program RAP along with the Library, ActionSetBuilder and Student classes and then, if successful, run this program against the facStudent.txt to produce the summary of the suggested actions for affected students in the files fac.summary.txt and fac.summary.log, Lastly
FullTest Criteria.0 all
for example, will build the converter, compile it, build the RAP program and run it from scratch.
That should keep you busy for a few hours... In fact some of you may find that you RAP around the clock!
Have fun, and good luck!
(Although the actual system used by the Deans was written in C#, this whole exercise could all be done in Java or in C#, and you are free to experiment in either language).