Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F92598574
lib_amcstats_new.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Thu, Nov 21, 21:03
Size
31 KB
Mime Type
text/x-php
Expires
Sat, Nov 23, 21:03 (1 d, 23 h)
Engine
blob
Format
Raw Data
Handle
22466728
Attached To
R1066 amc-cape
lib_amcstats_new.php
View Options
<?php
# some functions for array_maps
function
clean_array
(
$string
)
{
return
preg_replace
(
'/"/'
,
''
,
$string
);
}
function
decimal_conversion
(
$string
)
{
return
preg_replace
(
'/,/'
,
'.'
,
$string
);
}
function
square
(
$n
)
{
return
(
$n
*
$n
);
}
# standard deviation function (if PECL stats not available)
if
(!
function_exists
(
'stats_standard_deviation'
))
{
function
stats_standard_deviation
(
array
$a
,
$sample
=
false
)
{
$n
=
count
(
$a
);
if
(
$n
===
0
)
{
trigger_error
(
"The array has zero elements"
,
E_USER_WARNING
);
return
false
;
}
if
(
$sample
&&
$n
===
1
)
{
trigger_error
(
"The array has only 1 element"
,
E_USER_WARNING
);
return
false
;
}
$mean
=
array_sum
(
$a
)/
$n
;
$carry
=
0.0
;
foreach
(
$a
as
$val
)
{
$d
=
((
double
)
$val
)
-
$mean
;
$carry
+=
$d
*
$d
;
}
if
(
$sample
)
--
$n
;
return
sqrt
(
$carry
/
$n
);
}
}
class
AmcReader
{
protected
$filename
=
null
;
protected
$teacher
=
null
;
protected
$raw_data
=
null
;
protected
$columns
=
null
;
protected
$students
=
null
;
protected
$exam_points
=
null
;
public
function
__construct
(
$filename
,
$teacher
,
$exam_points
,
$only_questions
=
null
,
$inverse_filter
=
false
,
$external
=
null
)
{
if
(!
file_exists
(
$filename
))
throw
new
Exception
(
'File not found: '
.
$filename
);
$this
->
filename
=
$filename
;
$this
->
teacher
=
$teacher
;
$this
->
exam_points
=
$exam_points
;
$raw_data
=
file
(
$this
->
filename
,
FILE_IGNORE_NEW_LINES
);
$this
->
raw_data
=
array
();
foreach
(
$raw_data
as
$line
)
{
$line
=
array_map
(
"clean_array"
,
explode
(
';'
,
$line
));
$line
=
array_map
(
"decimal_conversion"
,
$line
);
$this
->
raw_data
[]
=
$line
;
}
$this
->
parseHeader
();
$this
->
parseStudents
(
$only_questions
,
$inverse_filter
,
$external
);
}
public
function
getStudents
()
{
return
$this
->
students
;
}
protected
function
parseHeader
()
{
if
(
isset
(
$this
->
raw_data
[
0
]))
//is_array($this->raw_data[0]) || is_object($this->raw_data[0]))
{
foreach
(
$this
->
raw_data
[
0
]
as
$col_id
=>
$value
)
{
// Analyse header from CSV file, based on content
$item
=
array
();
switch
(
$value
)
{
case
"ID"
:
case
"NAME"
:
case
"EMAIL"
:
case
"SECTION"
:
case
"Mark"
:
$item
[
'name'
]
=
$value
;
$item
[
'type'
]
=
"info"
;
break
;
case
"SCIPER"
:
$item
[
'name'
]
=
$value
;
$item
[
'type'
]
=
"unique_id"
;
break
;
default
:
$item
[
'name'
]
=
$value
;
if
(
preg_match
(
'/^TICKED:/'
,
$value
))
{
$item
[
'name'
]
=
preg_replace
(
'/^TICKED:/'
,
''
,
$value
);
$item
[
'type'
]
=
"ticked"
;
}
else
{
$item
[
'type'
]
=
"question"
;
$item
[
'subtype'
]
=
$this
->
guessSubtype_new
(
$col_id
);
}
}
// Stats will be computed at a later stage
$item
[
'stats'
]
=
null
;
$this
->
columns
[]
=
$item
;
}
}
}
protected
function
guessSubtype
(
$col_id
)
{
$subtype
=
null
;
$min_points
=
0
;
$max_points
=
0
;
$decimal
=
false
;
foreach
(
$this
->
raw_data
as
$line
)
{
//echo $line[$col_id];
if
(
preg_match
(
'/
\.
/'
,
$line
[
$col_id
]))
$decimal
=
true
;
if
(
$line
[
$col_id
]
>
$max_points
)
$max_points
=
$line
[
$col_id
];
}
//Tmp Global Issues
//if ($max_points == 1)return 'mc'; else return 'tf';
//Tmp Euler
//if ($max_points == 1)return 'mc'; else return 'open';
//return 'open';
// This only works for MATHS exams...
//Tmp mc 5 points
//if ($max_points == 5) return 'mc';
//K. Mulleners
//if ($max_points == 1) return 'mc';
//return 'open';
//Droit archi
//return 'mc';
if
(
$decimal
or
$max_points
>
3
)
{
// Only open questions have decimal points
return
'open'
;
}
//if ($max_points == 2) return 'mc';
if
(
$max_points
==
3
)
return
'mc'
;
if
(
$max_points
==
1
)
return
'tf'
;
return
'open'
;
}
protected
function
guessSubtype_new
(
$col_id
)
{
$subtype
=
null
;
$min_points
=
0
;
$max_points
=
0
;
$decimal
=
false
;
//get first line header and question ids
$line
=
$this
->
raw_data
[
0
];
//find question tex file
$dir
=
dirname
(
getcwd
()).
"/*.tex"
;
foreach
(
glob
(
$dir
)
as
$tex_file
)
{
if
(
!
empty
(
$line
[
$col_id
])
&&
strpos
(
file_get_contents
(
$tex_file
),
$line
[
$col_id
])
!==
false
)
{
$tex_lines
=
file
(
$tex_file
);
$lineNum
=
0
;
$questionSearchTxt
=
"{"
.
$line
[
$col_id
].
"}"
;
foreach
(
$tex_lines
as
$tex_line
)
{
if
(
strpos
(
$tex_line
,
$questionSearchTxt
)
!==
false
)
{
if
(
strpos
(
$tex_line
,
"{questionmult}"
)
!==
false
)
{
return
'mcm'
;
}
else
if
(
strpos
(
$tex_line
,
"
\c
orrector"
)
!==
false
)
{
return
'open'
;
}
else
{
$tmp_file
=
fopen
(
$tex_file
,
"r"
);
$tmp_lineNum
=
0
;
while
(!
feof
(
$tmp_file
))
{
$tmp_line
=
fgets
(
$tmp_file
);
//echo "\n".$line[$col_id].";";
if
(
$tmp_lineNum
>=
$lineNum
)
{
if
(
strpos
(
$tmp_line
,
"
\F
ALSE"
)
!==
false
||
strpos
(
$tmp_line
,
"
\T
RUE"
)
!==
FALSE
){
return
'tf'
;
}
}
$tmp_lineNum
++;
}
fclose
(
$tmp_file
);
return
'mc'
;
}
}
$lineNum
++;
}
//echo basename($tex_file);
}
}
echo
"
\n
"
;
}
protected
function
getColIdByName
(
$name
)
{
foreach
(
$this
->
columns
as
$id
=>
$col
)
if
(
$col
[
'name'
]
==
$name
)
return
$id
;
throw
new
Exception
(
'Column not found: '
.
$name
);
}
protected
function
getColIdsByType
(
$type
,
$name
=
null
)
{
$ids
=
array
();
if
(
is_null
(
$name
))
{
foreach
(
$this
->
columns
as
$id
=>
$col
)
if
(
$col
[
'type'
]
==
$type
)
$ids
[]
=
$id
;
}
else
{
foreach
(
$this
->
columns
as
$id
=>
$col
)
if
(
$col
[
'type'
]
==
$type
and
$col
[
'name'
]
==
$name
)
$ids
[]
=
$id
;
}
if
(
count
(
$ids
)
==
0
)
{
if
(
is_null
(
$name
))
throw
new
Exception
(
'Column type not found: '
.
$type
);
throw
new
Exception
(
'Column type not found: '
.
$type
.
'/'
.
$name
);
}
if
(
count
(
$ids
)
==
1
)
return
$ids
[
0
];
return
$ids
;
}
protected
function
getQuestionNameByColId
(
$id
)
{
if
(
array_key_exists
(
$id
,
$this
->
columns
)
and
$this
->
columns
[
$id
][
'type'
]
==
'question'
)
return
$this
->
columns
[
$id
][
'name'
];
throw
new
Exception
(
'Column not found, or is not a question: '
.
$id
);
}
protected
function
getMaximumPointsByColId
(
$id
)
{
$maximum
=
0.0
;
foreach
(
$this
->
raw_data
as
$student
)
if
((
float
)
$student
[
$id
]
>
$maximum
)
$maximum
=
(
float
)
$student
[
$id
];
return
$maximum
;
}
protected
function
parseStudents
(
$only_questions
,
$inverse_filter
,
$external
)
{
foreach
(
$this
->
raw_data
as
$line
=>
$student
)
{
if
(
$line
==
0
)
continue
;
// skip header
$data
=
array
(
'teacher'
=>
$this
->
teacher
);
foreach
(
array
(
'ID'
,
'SCIPER'
,
'NAME'
,
'EMAIL'
,
'SECTION'
)
as
$key
)
{
$data
[
$key
]
=
$student
[
$this
->
getColIdByName
(
$key
)];
}
// Get points
$points
=
array
();
$data
[
'items'
]
=
array
();
foreach
(
$this
->
getColIdsByType
(
'question'
)
as
$col
)
{
$item
=
array
();
$item
[
'name'
]
=
$this
->
getQuestionNameByColId
(
$col
);
// Should we filter this question ?
if
(
is_null
(
$only_questions
)
or
(!
$inverse_filter
and
in_array
(
$item
[
'name'
],
$only_questions
))
or
(
$inverse_filter
and
!
in_array
(
$item
[
'name'
],
$only_questions
))
)
{
// Take this question into account
$item
[
'points'
]
=
(
float
)
$student
[
$col
];
$item
[
'max_points'
]
=
$this
->
getMaximumPointsByColId
(
$col
);
if
(
$item
[
'max_points'
]
==
0
)
{
// Cancelled question ? Count it right for everyone
$item
[
'right'
]
=
1
;
}
else
{
$item
[
'right'
]
=
max
((
float
)(
$item
[
'points'
]/
$item
[
'max_points'
]),
0.0
);
}
$points
[]
=
$item
[
'points'
];
$item
[
'ticked'
]
=
$student
[
$this
->
getColIdsByType
(
'ticked'
,
$item
[
'name'
])];
$item
[
'type'
]
=
$this
->
columns
[
$col
][
'type'
];
$item
[
'subtype'
]
=
$this
->
columns
[
$col
][
'subtype'
];
$data
[
'items'
][]
=
$item
;
}
}
$data
[
'total'
]
=
array_sum
(
$points
);
$data
[
'present'
]
=
(
int
)(
array_sum
(
array_map
(
"square"
,
$points
))>
0
);
$data
[
'exam_points'
]
=
$this
->
exam_points
;
// Compute marks
if
(
$data
[
'present'
])
{
$data
[
'positive_total'
]
=
(
float
)
max
(
$data
[
'total'
],
0.0
);
if
(
is_null
(
$external
))
{
$data
[
'mark6'
]
=
(
float
)
min
(
$data
[
'positive_total'
]/(
$this
->
exam_points
)*
5.0
+
1
,
6.0
);
}
else
{
$output
=
array
();
exec
(
"./"
.
$external
.
" "
.
$data
[
'total'
],
$output
);
$data
[
'mark6'
]
=
(
float
)
trim
(
$output
[
0
]);
}
$data
[
'quarter_mark6'
]
=
(
float
)
round
(
$data
[
'mark6'
]*
4.0
,
0
)/
4.0
;
}
else
{
$data
[
'positive_total'
]
=
'n/a'
;
$data
[
'mark6'
]
=
'abs'
;
$data
[
'quarter_mark6'
]
=
'abs'
;
}
if
(
preg_match
(
'/^FAKE/'
,
$data
[
'SCIPER'
]))
{
if
(
$data
[
'present'
])
$data
[
'type'
]
=
'unregistered'
;
else
$data
[
'type'
]
=
'unused'
;
}
else
$data
[
'type'
]
=
'student'
;
$this
->
students
[]
=
$data
;
}
}
}
// Compare students on total (higher to lower)
function
cmp_total
(
$a
,
$b
)
{
if
(
$a
[
'total'
]
==
$b
[
'total'
])
{
return
0
;
}
return
(
$a
[
'total'
]
<
$b
[
'total'
])
?
1
:
-
1
;
}
class
ExamCalcs
{
protected
$dataset
=
null
;
protected
$tmp_dataset
=
null
;
public
function
__construct
(
$dataset
=
null
)
{
$this
->
dataset
=
array
();
if
(!
is_null
(
$dataset
))
$this
->
dataset
=
$dataset
;
}
public
function
addFile
(
$teacher
,
$teacher_file
,
$max_points
,
$only_questions
=
null
,
$inverse_filter
=
false
,
$external
=
null
)
{
#echo "Adding $teacher ($teacher_file) to the dataset ($max_points points).\n";
$AR
=
new
AmcReader
(
$teacher_file
,
$teacher
,
$max_points
,
$only_questions
,
$inverse_filter
,
$external
);
$this
->
addDataSet
(
$AR
->
getStudents
());
}
public
function
addDataSet
(
$data
)
{
if
(
is_array
(
$data
)
||
is_object
(
$data
))
{
foreach
(
$data
as
$student
)
$this
->
dataset
[]
=
$student
;
}
}
public
function
filterBySections
(
$sections
,
$update
=
true
)
{
$dataset
=
array
();
foreach
(
$this
->
dataset
as
$student
)
{
if
(
is_array
(
$sections
))
{
if
(
in_array
(
$student
[
'SECTION'
],
$sections
))
$dataset
[]
=
$student
;
}
else
{
if
(
$student
[
'SECTION'
]
==
$sections
)
$dataset
[]
=
$student
;
}
}
if
(
$update
)
$this
->
dataset
=
$dataset
;
return
$dataset
;
}
public
function
filterByTeachers
(
$teachers
,
$update
=
true
)
{
$dataset
=
array
();
foreach
(
$this
->
dataset
as
$student
)
{
if
(
is_array
(
$teachers
))
{
if
(
in_array
(
$student
[
'teacher'
],
$teachers
))
$dataset
[]
=
$student
;
}
else
{
if
(
$student
[
'teacher'
]
==
$teachers
)
$dataset
[]
=
$student
;
}
}
if
(
$update
)
$this
->
dataset
=
$dataset
;
return
$dataset
;
}
public
function
getTeachers
()
{
$teachers
=
array
();
foreach
(
$this
->
dataset
as
$student
)
{
if
(!
in_array
(
$student
[
'teacher'
],
$teachers
))
$teachers
[]
=
$student
[
'teacher'
];
}
return
$teachers
;
}
public
function
getSections
()
{
$sections
=
array
();
foreach
(
$this
->
dataset
as
$student
)
{
$section
=
$student
[
'SECTION'
];
if
(!
in_array
(
$section
,
$sections
)
and
$section
!=
'XXX'
)
$sections
[]
=
$section
;
}
return
$sections
;
}
public
function
getQuestionsTxt
()
{
$questions
=
array
();
$items
=
array
();
foreach
(
$this
->
dataset
[
0
][
'items'
]
as
$item
){
$questions
[]
=
$item
[
'name'
];
echo
$item
[
'name'
];
}
return
$questions
;
}
public
function
printStatsOnCommonItems
()
{
// Sort dataset by points
$dataset
=
$this
->
sortByTotalPoints
(
false
);
if
(
count
(
$dataset
)
<
3
)
throw
new
Exception
(
'Dataset is too small.'
);
// Get items from the first student
$items
=
array
();
foreach
(
$this
->
dataset
[
0
][
'items'
]
as
$item
)
$items
[]
=
$item
[
'name'
];
foreach
(
$dataset
as
$student
)
{
// Get items for current student
$tmp_items
=
array
();
foreach
(
$student
[
'items'
]
as
$item
)
$tmp_items
[]
=
$item
[
'name'
];
// Keep only items in both '$items' AND '$tmp_items'
$items
=
array_intersect
(
$items
,
$tmp_items
);
}
// Now, filter items in the dataset
$filtered_dataset
=
array
();
foreach
(
$dataset
as
$student
)
{
if
(!
$student
[
'present'
])
continue
;
$filtered_items
=
array
();
foreach
(
$student
[
'items'
]
as
$item
)
{
if
(
in_array
(
$item
[
'name'
],
$items
))
$filtered_items
[]
=
$item
;
}
if
(
count
(
$filtered_items
))
{
$student
[
'items'
]
=
$filtered_items
;
$filtered_dataset
[]
=
$student
;
}
}
$dataset
=
$filtered_dataset
;
if
(
count
(
$dataset
)
<
3
)
throw
new
Exception
(
'Dataset is too small.'
);
// Compute limits
$nb_students
=
count
(
$dataset
);
$twenty_seven
=
(
int
)(
$nb_students
*
27.0
/
100
);
$upper_stop
=
$twenty_seven
-
1
;
$lower_start
=
$nb_students
-
$twenty_seven
+
1
;
#echo "$nb_students / $twenty_seven / 0 -> $upper_stop / $lower_start -> $nb_students \n";
$stats
=
array
();
foreach
(
$dataset
as
$i
=>
$student
)
{
foreach
(
$student
[
'items'
]
as
$item
)
{
$name
=
$item
[
'name'
];
if
(!
array_key_exists
(
$name
,
$stats
))
$stats
[
$name
]
=
array
(
'27%'
=>
$twenty_seven
,
'upper'
=>
0
,
'lower'
=>
0
,
'valid'
=>
null
,
'ticked'
=>
null
,
'ticked_count'
=>
0
,
'empty_count'
=>
0
,
'type'
=>
null
,
'subtype'
=>
null
,
'max_points'
=>
null
,
);
$stats
[
$name
][
'max_points'
]
=
$item
[
'max_points'
];
$stats
[
$name
][
'type'
]
=
$item
[
'type'
];
$stats
[
$name
][
'subtype'
]
=
$item
[
'subtype'
];
$stats
[
$name
][
'question_id'
]
=
$name
;
// Initialise 'ticked' table
if
(
is_null
(
$stats
[
$name
][
'ticked'
]))
{
switch
(
$stats
[
$name
][
'subtype'
])
{
case
'mc'
:
$stats
[
$name
][
'ticked'
]
=
array
(
'A'
=>
0
,
'B'
=>
0
,
'C'
=>
0
,
'D'
=>
0
,
'multiple'
=>
0
);
break
;
case
'mcm'
:
$stats
[
$name
][
'ticked'
]
=
array
();
//array( 'A' => 0, 'B' => 0, 'C' => 0, 'D' => 0, 'E' => 0, 'F' => 0, 'G' => 0 );
break
;
case
'tf'
:
$stats
[
$name
][
'ticked'
]
=
array
(
'TRUE'
=>
0
,
'FALSE'
=>
0
,
'multiple'
=>
0
);
break
;
default
:
$stats
[
$name
][
'ticked'
]
=
array
();
break
;
}
}
// Count right answers
if
(
$item
[
'right'
]
>
0
)
{
// Save valid answer
if
(
is_null
(
$stats
[
$name
][
'valid'
]))
{
switch
(
$stats
[
$name
][
'subtype'
])
{
case
'tf'
:
if
(
$item
[
'ticked'
]
==
'A'
)
$stats
[
$name
][
'valid'
]
=
'TRUE'
;
else
$stats
[
$name
][
'valid'
]
=
'FALSE'
;
break
;
case
'mc'
:
$stats
[
$name
][
'valid'
]
=
$item
[
'ticked'
];
break
;
case
'mcm'
:
$stats
[
$name
][
'valid'
]
=
'n/a'
;;
break
;
case
'open'
:
$stats
[
$name
][
'valid'
]
=
'n/a'
;
break
;
default
:
$stats
[
$name
][
'valid'
]
=
'n/a'
;
break
;
}
}
// 'upper 27%' and 'lower 27%' counters
switch
(
$stats
[
$name
][
'subtype'
])
{
case
'mcm'
:
if
(
$i
<=
$upper_stop
)
$stats
[
$name
][
'upper'
]
+=
$item
[
'points'
];
if
(
$i
>=
$lower_start
)
$stats
[
$name
][
'lower'
]
+=
$item
[
'points'
];
break
;
case
'mc'
:
case
'tf'
:
if
(
$i
<=
$upper_stop
)
$stats
[
$name
][
'upper'
]++;
if
(
$i
>=
$lower_start
)
$stats
[
$name
][
'lower'
]++;
break
;
case
'open'
:
if
(
$i
<=
$upper_stop
)
$stats
[
$name
][
'upper'
]
+=
$item
[
'points'
];
if
(
$i
>=
$lower_start
)
$stats
[
$name
][
'lower'
]
+=
$item
[
'points'
];
break
;
}
}
// Count empty answers
if
(
empty
(
$item
[
'ticked'
]))
{
$stats
[
$name
][
'empty_count'
]++;
}
else
{
// Stats on non-empty answers
$stats
[
$name
][
'ticked_count'
]++;
if
(
strlen
(
$item
[
'ticked'
])
>
1
&&
$item
[
'subtype'
]
!=
'mcm'
)
{
$stats
[
$name
][
'ticked'
]
=
$this
->
createAndIncrement
(
$stats
[
$name
][
'ticked'
],
'multiple'
);
}
else
{
switch
(
$item
[
'subtype'
])
{
case
'tf'
:
if
(
$item
[
'ticked'
]
==
'A'
)
$field
=
'TRUE'
;
if
(
$item
[
'ticked'
]
==
'B'
)
$field
=
'FALSE'
;
$stats
[
$name
][
'ticked'
]
=
$this
->
createAndIncrement
(
$stats
[
$name
][
'ticked'
],
$field
);
break
;
case
'mc'
:
$stats
[
$name
][
'ticked'
]
=
$this
->
createAndIncrement
(
$stats
[
$name
][
'ticked'
],
$item
[
'ticked'
]);
break
;
case
'mcm'
:
$stats
[
$name
][
'ticked'
]
=
$this
->
createAndIncrement
(
$stats
[
$name
][
'ticked'
],
$item
[
'ticked'
]);
break
;
default
:
$stats
[
$name
][
'ticked'
]
=
$this
->
createAndIncrement
(
$stats
[
$name
][
'ticked'
],
$item
[
'ticked'
]);
break
;
}
}
}
}
}
// Compute more stats
$tmp
=
array
();
foreach
(
$stats
as
$name
=>
$stat
)
{
// Discrimination index
// NA if stat 27% is zero
if
(
$stat
[
'27%'
]
==
0
){
$stat
[
'upper'
]
=
"NA"
;
$stat
[
'lower'
]
=
"NA"
;
$stat
[
'27%'
]
=
"NA"
;
$stat
[
'DI'
]
=
"NA"
;
}
else
{
// For open and mcm questions, change the '27%' value.
if
(
$stat
[
'subtype'
]
==
'open'
||
$stat
[
'subtype'
]
==
'mcm'
){
$stat
[
'upper'
]
=
$stat
[
'upper'
]/
$stat
[
'27%'
];
$stat
[
'lower'
]
=
$stat
[
'lower'
]/
$stat
[
'27%'
];
$stat
[
'27%'
]
=
$stat
[
'max_points'
];
//$stat['27%']*$stat['max_points'];
}
$stat
[
'DI'
]
=
(
$stat
[
'upper'
]-
$stat
[
'lower'
])/(
1.0
*
$stat
[
'27%'
]);
}
// Calculate percentages
$ticked_percentage
=
array
();
foreach
(
$stat
[
'ticked'
]
as
$t
=>
$n
)
{
$ticked_percentage
[
$t
]
=
array
(
'n'
=>
$n
,
'%'
=>
(
float
)(
100.0
*
$n
/
$stat
[
'ticked_count'
]),
'valid'
=>
(
int
)(
$t
==
$stat
[
'valid'
]));
}
$stat
[
'ticked'
]
=
$ticked_percentage
;
$tmp
[
$name
]
=
$stat
;
}
$stats
=
$tmp
;
// Order array by question type, name
uasort
(
$stats
,
function
(
$a
,
$b
){
if
(
$a
[
"subtype"
]
==
$b
[
"subtype"
]){
return
strcmp
(
$a
[
"question_id"
],
$b
[
"question_id"
]);
}
return
strcmp
(
$a
[
"subtype"
],
$b
[
"subtype"
]);
});
// Print CSV
$previous_subtype
=
null
;
$header
=
'"question_id","subtype","27 %","upper","lower","DI","count","valid"'
;
$headerOpenMcm
=
'"question_id","subtype","question pts %","avg pts upper","avg pts lower","DI","count"'
;
$lastStatTickedSize
=
0
;
foreach
(
$stats
as
$name
=>
$stat
)
{
if
(
$stat
[
'subtype'
]
!=
$previous_subtype
||
count
(
$stat
[
'ticked'
])
!=
$lastStatTickedSize
)
{
#if (!is_null($previous_subtype)) echo "\n";
if
(
$stat
[
'subtype'
]
==
'open'
||
$stat
[
'subtype'
]
==
'mcm'
){
echo
"
\n
"
.
$headerOpenMcm
;
}
else
{
echo
"
\n
"
.
$header
;
}
if
(
$stat
[
'subtype'
]
!=
'open'
)
{
if
(
$stat
[
'subtype'
]
!=
'tf'
){
ksort
(
$stat
[
'ticked'
]);
}
foreach
(
$stat
[
'ticked'
]
as
$answer
=>
$data
)
echo
",
\"
[$answer] count
\"
"
;
foreach
(
$stat
[
'ticked'
]
as
$answer
=>
$data
)
echo
",
\"
[$answer] %
\"
"
;
}
echo
"
\n
"
;
$previous_subtype
=
$stat
[
'subtype'
];
}
$lastStatTickedSize
=
count
(
$stat
[
'ticked'
]);
echo
"$name,{$stat['subtype']},{$stat['27%']},{$stat['upper']},{$stat['lower']},{$stat['DI']},{$stat['ticked_count']}"
;
if
(
$stat
[
'subtype'
]
!=
'open'
&&
$stat
[
'subtype'
]
!=
'mcm'
){
foreach
(
$stat
[
'ticked'
]
as
$answer
=>
$data
)
if
(
$data
[
'valid'
]
==
1
)
echo
",
\"
$answer
\"
"
;
foreach
(
$stat
[
'ticked'
]
as
$answer
=>
$data
)
echo
",{$data['n']}"
;
foreach
(
$stat
[
'ticked'
]
as
$answer
=>
$data
)
echo
",{$data['%']}"
;
}
else
if
(
$stat
[
'subtype'
]
==
'mcm'
){
ksort
(
$stat
[
'ticked'
]);
foreach
(
$stat
[
'ticked'
]
as
$answer
=>
$data
)
echo
",{$data['n']}"
;
foreach
(
$stat
[
'ticked'
]
as
$answer
=>
$data
)
echo
",{$data['%']}"
;
}
echo
"
\n
"
;
}
echo
"
\n\n
"
;
}
public
function
printLatexHistoCommandsOnCommonItems
()
{
// Sort dataset by points
$dataset
=
$this
->
sortByTotalPoints
(
false
);
if
(
count
(
$dataset
)
<
3
)
throw
new
Exception
(
'Dataset is too small.'
);
// Get items from the first student
$items
=
array
();
foreach
(
$this
->
dataset
[
0
][
'items'
]
as
$item
)
$items
[]
=
$item
[
'name'
];
foreach
(
$dataset
as
$student
)
{
// Get items for current student
$tmp_items
=
array
();
foreach
(
$student
[
'items'
]
as
$item
)
$tmp_items
[]
=
$item
[
'name'
];
// Keep only items in both '$items' AND '$tmp_items'
$items
=
array_intersect
(
$items
,
$tmp_items
);
}
// Now, filter items in the dataset
$filtered_dataset
=
array
();
foreach
(
$dataset
as
$student
)
{
if
(!
$student
[
'present'
])
continue
;
$filtered_items
=
array
();
foreach
(
$student
[
'items'
]
as
$item
)
{
if
(
in_array
(
$item
[
'name'
],
$items
))
$filtered_items
[]
=
$item
;
}
if
(
count
(
$filtered_items
))
{
$student
[
'items'
]
=
$filtered_items
;
$filtered_dataset
[]
=
$student
;
}
}
$dataset
=
$filtered_dataset
;
if
(
count
(
$dataset
)
<
3
)
throw
new
Exception
(
'Dataset is too small.'
);
// Compute limits
$nb_students
=
count
(
$dataset
);
$twenty_seven
=
(
int
)(
$nb_students
*
27.0
/
100
);
$upper_stop
=
$twenty_seven
-
1
;
$lower_start
=
$nb_students
-
$twenty_seven
+
1
;
#echo "$nb_students / $twenty_seven / 0 -> $upper_stop / $lower_start -> $nb_students \n";
$stats
=
array
();
foreach
(
$dataset
as
$i
=>
$student
)
{
foreach
(
$student
[
'items'
]
as
$item
)
{
$name
=
$item
[
'name'
];
$teacher
=
$student
[
'teacher'
];
$name
=
$name
.
";"
.
$teacher
;
if
(!
array_key_exists
(
$name
,
$stats
))
$stats
[
$name
]
=
array
(
'teacher'
=>
null
,
'valid'
=>
null
,
'ticked'
=>
null
,
'ticked_count'
=>
0
,
'empty_count'
=>
0
,
'type'
=>
null
,
'subtype'
=>
null
,
);
$stats
[
$name
][
'type'
]
=
$item
[
'type'
];
$stats
[
$name
][
'subtype'
]
=
$item
[
'subtype'
];
$stats
[
$name
][
'teacher'
]
=
$student
[
'teacher'
];
$stats
[
$name
][
'question_id'
]
=
$name
;
// Initialise 'ticked' table
if
(
is_null
(
$stats
[
$name
][
'ticked'
]))
{
switch
(
$stats
[
$name
][
'subtype'
])
{
case
'mc'
:
$stats
[
$name
][
'ticked'
]
=
array
(
'A'
=>
0
,
'B'
=>
0
,
'C'
=>
0
,
'D'
=>
0
);
break
;
case
'mcm'
:
$stats
[
$name
][
'ticked'
]
=
array
(
'A'
=>
0
,
'B'
=>
0
,
'C'
=>
0
,
'D'
=>
0
,
'E'
=>
0
,
'F'
=>
0
,
'G'
=>
0
);
break
;
case
'tf'
:
$stats
[
$name
][
'ticked'
]
=
array
(
'TRUE'
=>
0
,
'FALSE'
=>
0
);
break
;
default
:
$stats
[
$name
][
'ticked'
]
=
array
();
break
;
}
}
// Count right answers
if
(
$item
[
'right'
]
>
0
)
{
// Save valid answer
if
(
is_null
(
$stats
[
$name
][
'valid'
]))
{
switch
(
$stats
[
$name
][
'subtype'
])
{
case
'tf'
:
if
(
$item
[
'ticked'
]
==
'A'
)
$stats
[
$name
][
'valid'
]
=
'TRUE'
;
else
$stats
[
$name
][
'valid'
]
=
'FALSE'
;
break
;
case
'mc'
:
$stats
[
$name
][
'valid'
]
=
$item
[
'ticked'
];
break
;
case
'mcm'
:
$stats
[
$name
][
'valid'
]
=
'n/a'
;
break
;
case
'open'
:
$stats
[
$name
][
'valid'
]
=
'n/a'
;
break
;
default
:
$stats
[
$name
][
'valid'
]
=
'n/a'
;
break
;
}
}
}
// Count empty answers
if
(
empty
(
$item
[
'ticked'
]))
{
$stats
[
$name
][
'empty_count'
]++;
}
else
{
// Stats on non-empty answers
$stats
[
$name
][
'ticked_count'
]++;
if
(
strlen
(
$item
[
'ticked'
])
==
1
)
{
switch
(
$item
[
'subtype'
])
{
case
'tf'
:
if
(
$item
[
'ticked'
]
==
'A'
)
$field
=
'TRUE'
;
if
(
$item
[
'ticked'
]
==
'B'
)
$field
=
'FALSE'
;
$stats
[
$name
][
'ticked'
]
=
$this
->
createAndIncrement
(
$stats
[
$name
][
'ticked'
],
$field
);
break
;
case
'mc'
:
$stats
[
$name
][
'ticked'
]
=
$this
->
createAndIncrement
(
$stats
[
$name
][
'ticked'
],
$item
[
'ticked'
]);
break
;
case
'mcm'
:
$stats
[
$name
][
'ticked'
]
=
$this
->
createAndIncrement
(
$stats
[
$name
][
'ticked'
],
$item
[
'ticked'
]);
break
;
default
:
$stats
[
$name
][
'ticked'
]
=
$this
->
createAndIncrement
(
$stats
[
$name
][
'ticked'
],
$item
[
'ticked'
]);
break
;
}
}
}
}
}
// Compute more stats
$tmp
=
array
();
foreach
(
$stats
as
$name
=>
$stat
)
{
// Calculate percentages
$ticked_percentage
=
array
();
foreach
(
$stat
[
'ticked'
]
as
$t
=>
$n
)
{
$ticked_percentage
[
$t
]
=
array
(
'n'
=>
$n
,
'%'
=>
(
float
)(
100.0
*
$n
/
$stat
[
'ticked_count'
]),
'valid'
=>
(
int
)(
$t
==
$stat
[
'valid'
]));
}
$stat
[
'ticked'
]
=
$ticked_percentage
;
$tmp
[
$name
]
=
$stat
;
}
$stats
=
$tmp
;
// Print CSV, by question, then teacher
//histo colors
$i
=
0
;
foreach
(
$this
->
getTeachers
()
as
$teacher
){
$i
++;
switch
(
$i
)
{
case
1
:
echo
"
\\
newcommand{
\\
"
.
$teacher
.
"}{
\\
textbf{"
.
$teacher
.
"}}
\n
"
;
break
;
case
2
:
echo
"
\\
newcommand{
\\
"
.
$teacher
.
"}{{
\c
olor{red}
\\
textbf{"
.
$teacher
.
"}}}
\n
"
;
break
;
case
3
:
echo
"
\\
newcommand{
\\
"
.
$teacher
.
"}{{
\c
olor{blue}
\\
textbf{"
.
$teacher
.
"}}}
\n
"
;
break
;
case
4
:
echo
"
\\
newcommand{
\\
"
.
$teacher
.
"}{{
\c
olor{magenta}
\\
textbf{"
.
$teacher
.
"}}}
\n
"
;
break
;
case
5
:
echo
"
\\
newcommand{
\\
"
.
$teacher
.
"}{{
\c
olor{cyan}
\\
textbf{"
.
$teacher
.
"}}}
\n
"
;
break
;
case
6
:
echo
"
\\
newcommand{
\\
"
.
$teacher
.
"}{{
\c
olor{olive}
\\
textbf{"
.
$teacher
.
"}}}
\n
"
;
break
;
case
7
:
echo
"
\\
newcommand{
\\
"
.
$teacher
.
"}{{
\c
olor{orange}
\\
textbf{"
.
$teacher
.
"}}}
\n
"
;
break
;
case
8
:
echo
"
\\
newcommand{
\\
"
.
$teacher
.
"}{{
\c
olor{black}
\\
textbf{"
.
$teacher
.
"}}}
\n
"
;
break
;
}
}
echo
"
\n
"
;
//histo teachers present students
foreach
(
$this
->
getTeachers
()
as
$teacher
){
echo
"
\\
newcommand{
\N
"
.
$teacher
.
"}{"
.
$this
->
getTotStudentsByTeacher
(
$teacher
)
.
"}
\n
"
;
}
echo
"
\n
"
;
$items
=
array
();
foreach
(
$this
->
dataset
[
0
][
'items'
]
as
$item
){
if
(
$item
[
'subtype'
]
!=
'open'
){
echo
"
\i
nputFR{"
.
$item
[
'name'
]
.
".tex}
\n
"
;
foreach
(
$this
->
getTeachers
()
as
$teacher
){
foreach
(
$stats
as
$name
=>
$stat
)
{
// if($stat['teacher'] == $teacher){
// echo $name . " - " . $item['name'];
// }
//echo "[" . $stat['teacher'] . " - " . $teacher . " | " . $name . " - " . $item['name'] . "]";
if
(
$stat
[
'teacher'
]
==
$teacher
&&
explode
(
';'
,
$name
)[
0
]
==
$item
[
'name'
]){
if
(
strpos
(
$item
[
'name'
],
'TF'
)
!==
false
)
{
echo
"
\h
istoTF{
\\
"
.
$teacher
.
"}{
\N
"
.
$teacher
.
"}{"
.
$stat
[
'ticked_count'
]
.
"}"
;
}
else
{
echo
"
\h
istoQCM{
\\
"
.
$teacher
.
"}{
\N
"
.
$teacher
.
"}{"
.
$stat
[
'ticked_count'
]
.
"}"
;
}
foreach
(
$stat
[
'ticked'
]
as
$answer
=>
$data
)
echo
"{{$data['n']}}"
;
}
}
echo
"
\n
"
;
}
echo
"
\n
"
;
echo
"
\n
"
;
}
}
}
public
function
sortByTotalPoints
(
$update
=
true
)
{
if
(
$update
)
{
usort
(
$this
->
dataset
,
"cmp_total"
);
return
$this
->
dataset
;
}
else
{
$dataset
=
$this
->
dataset
;
usort
(
$dataset
,
"cmp_total"
);
return
$dataset
;
}
}
public
function
getDataSet
()
{
return
$this
->
dataset
;
}
protected
function
createAndIncrement
(
$table
,
$field
,
$increment
=
1
)
{
if
(!
array_key_exists
(
$field
,
$table
))
{
$table
[
$field
]
=
0
;
}
$table
[
$field
]
+=
$increment
;
return
$table
;
}
public
function
getMarks
()
{
$marks
=
array
();
foreach
(
$this
->
dataset
as
$student
)
{
$tmp
=
array
();
$tmp
[
'teacher'
]
=
$student
[
'teacher'
];
$tmp
[
'ID'
]
=
$student
[
'ID'
];
$tmp
[
'SECTION'
]
=
$student
[
'SECTION'
];
$tmp
[
'exam_points'
]
=
$student
[
'exam_points'
];
$tmp
[
'total'
]
=
$student
[
'total'
];
$tmp
[
'present'
]
=
$student
[
'present'
];
$tmp
[
'SCIPER'
]
=
$student
[
'SCIPER'
];
$tmp
[
'quarter_mark6'
]
=
$student
[
'quarter_mark6'
];
$marks
[]
=
$tmp
;
}
return
$marks
;
}
public
function
getTotStudentsByTeacher
(
$teacher
){
$nb_students
=
0
;
foreach
(
$this
->
dataset
as
$student
){
if
(
$student
[
'teacher'
]
==
$teacher
&&
$student
[
'present'
]){
$nb_students
++;
}
}
return
$nb_students
;
}
public
function
getQuestions
()
{
$questions
=
array
();
foreach
(
$this
->
dataset
as
$student
)
{
//print_r($student['items']);
$tmp
=
array
();
$tmp
[
'teacher'
]
=
$student
[
'teacher'
];
$tmp
[
'ID'
]
=
$student
[
'ID'
];
$tmp
[
'SECTION'
]
=
$student
[
'SECTION'
];
$tmp
[
'SCIPER'
]
=
$student
[
'SCIPER'
];
if
(!
$student
[
'present'
])
{
$tmp
[
'present'
]
=
"ABS"
;
}
else
{
$tmp
[
'present'
]
=
1
;
}
$tmp
[
'exam_points'
]
=
$student
[
'exam_points'
];
$tmp
[
'total'
]
=
$student
[
'total'
];
# Stats on questions
$tmp
[
'nb_questions'
]
=
count
(
$student
[
'items'
]);
$tmp
[
'not_answered'
]
=
0
;
$tmp
[
'right'
]
=
0
;
$tmp
[
'wrong'
]
=
0
;
foreach
(
$student
[
'items'
]
as
$question
)
{
if
(
empty
(
$question
[
'ticked'
]))
{
// Question was not answered
$tmp
[
'not_answered'
]
+=
1
;
}
else
{
if
(
$question
[
'right'
])
$tmp
[
'right'
]
+=
1
;
else
$tmp
[
'wrong'
]
+=
1
;
}
}
$questions
[]
=
$tmp
;
}
return
$questions
;
}
public
function
getStats
()
{
$stats
=
array
();
# Presence
$stats
[
'presence'
]
=
array
();
$marks
=
array
();
$stats
[
'quarter_mark6'
]
=
array
(
'n'
=>
0
,
'tot'
=>
0
,
'average'
=>
null
,
'stddev'
=>
null
,
'median'
=>
null
);
foreach
(
$this
->
dataset
as
$student
)
{
// Presence
$stats
[
'presence'
]
=
$this
->
createAndIncrement
(
$stats
[
'presence'
],
'total'
);
switch
(
$student
[
'type'
])
{
case
'student'
:
if
(
$student
[
'present'
])
{
$stats
[
'presence'
]
=
$this
->
createAndIncrement
(
$stats
[
'presence'
],
'present'
);
}
else
{
$stats
[
'presence'
]
=
$this
->
createAndIncrement
(
$stats
[
'presence'
],
'absent'
);
}
break
;
case
'unused'
:
$stats
[
'presence'
]
=
$this
->
createAndIncrement
(
$stats
[
'presence'
],
'unsused'
);
break
;
default
:
$stats
[
'presence'
]
=
$this
->
createAndIncrement
(
$stats
[
'presence'
],
'unknown'
);
}
// Average
if
(
$student
[
'present'
])
$marks
[]
=
$student
[
'quarter_mark6'
];
}
$stats
[
'quarter_mark6'
][
'n'
]
=
count
(
$marks
);
if
(
$stats
[
'quarter_mark6'
][
'n'
]
>
0
)
{
$stats
[
'quarter_mark6'
][
'tot'
]
=
array_sum
(
$marks
);
$stats
[
'quarter_mark6'
][
'average'
]
=
$stats
[
'quarter_mark6'
][
'tot'
]/
$stats
[
'quarter_mark6'
][
'n'
];
$stats
[
'quarter_mark6'
][
'stddev'
]
=
stats_standard_deviation
(
$marks
);
if
(
count
(
$marks
)
>=
3
)
{
sort
(
$marks
);
$stats
[
'quarter_mark6'
][
'median'
]
=
$marks
[
round
(
count
(
$marks
)/
2
)];
}
else
{
$stats
[
'quarter_mark6'
][
'median'
]
=
'n/a'
;
}
}
else
{
$stats
[
'quarter_mark6'
][
'tot'
]
=
0
;
$stats
[
'quarter_mark6'
][
'average'
]
=
0
;
$stats
[
'quarter_mark6'
][
'stddev'
]
=
0
;
$stats
[
'quarter_mark6'
][
'median'
]
=
0
;
}
// Distribution (of marks)
$distribution
=
array
();
for
(
$m
=
1.0
;
$m
<=
6.0
;
$m
+=
0.25
)
$distribution
[(
string
)
$m
]
=
0
;
$stats
[
'distribution_total'
]
=
0
;
foreach
(
$marks
as
$mark
)
{
$distribution
[(
string
)
$mark
]++;
$stats
[
'distribution_total'
]++;
}
$stats
[
'distribution'
]
=
$distribution
;
$stats
[
'distribution_percentage'
]
=
array
();
if
(
$stats
[
'quarter_mark6'
][
'n'
])
{
foreach
(
$stats
[
'distribution'
]
as
$mark
=>
$count
)
$stats
[
'distribution_percentage'
][
$mark
]
=
$count
*
100.0
/
$stats
[
'distribution_total'
];
}
else
{
$stats
[
'distribution_percentage'
]
=
$stats
[
'distribution'
];
}
return
(
$stats
);
}
public
function
array_orderby
()
{
$args
=
func_get_args
();
$data
=
array_shift
(
$args
);
foreach
(
$args
as
$n
=>
$field
)
{
if
(
is_string
(
$field
))
{
$tmp
=
array
();
foreach
(
$data
as
$key
=>
$row
)
$tmp
[
$key
]
=
$row
[
$field
];
$args
[
$n
]
=
$tmp
;
}
}
$args
[]
=
&
$data
;
call_user_func_array
(
'array_multisort'
,
$args
);
return
array_pop
(
$args
);
}
}
?>
Event Timeline
Log In to Comment