File:
[LON-CAPA] /
modules /
gci /
timeontask.pl
Revision
1.1:
download - view:
text,
annotated -
select for diffs
Sat Apr 24 15:17:45 2010 UTC (14 years, 5 months ago) by
gci
Branches:
MAIN
CVS tags:
HEAD
- Extract times between problem display and answer submission for
problems in GCI concept tests.
- Output in CSV files:
1. status_$cnum.csv: columns are individual questions.
rows are student submission status (1 =correct)
2. timeontask_$cnum.csv: columns are individual questions.
rows are times between display and submission
3. names_$cnum.csv: map row numbers in files 1&2 to student usernames
(for debugging only; files 1 & 2 should be the ones
provided to researchers).
1: #!/usr/bin/perl
2:
3: #
4: # Stuart Raeburn, 04/23/2010
5: #
6:
7: use strict;
8: use DBI;
9: use URI::Escape;
10: use POSIX qw(strftime mktime);
11: use lib '/home/httpd/lib/perl';
12: use LONCAPA;
13: use Apache::loncommon();
14: use Apache::lonnet;
15: use Apache::lonuserstate();
16: use Apache::lonnavmaps();
17: use Apache::loncoursedata();
18:
19: my $dbh;
20: unless ($dbh = DBI->connect("DBI:mysql:loncapa","www",'localhostkey',
21: { RaiseError =>0,PrintError=>0})) {
22: print "Cannot connect to database!\n";
23: exit;
24: }
25:
26: my @courses;
27: if (!@ARGV) {
28: print "Usage: timeontask.pl <coursenum1> <coursenum2> ... \n".
29: "where <coursenumN> is a course number in the gcitest domain, e.g.,1z283940760524b64gcil1, for which you wish to extract data. More than one coursenumber can be entered.\n";
30: } else {
31: @courses = @ARGV;
32: }
33:
34: my $cdom = 'gcitest';
35: my $folder = 'default_1261144274.sequence';
36: my $role = 'st';
37: my $secidx = &Apache::loncoursedata::CL_SECTION();
38:
39: foreach my $cnum (@courses) {
40: my $cid = $cdom.'_'.$cnum;
41: my $act_table = $cnum.'_'.$cdom.'_activity';
42: my $res_table = $cnum.'_'.$cdom.'_resource';
43: my $user_table = $cnum.'_'.$cdom.'_students';
44: my %coursepersonnel = &Apache::lonnet::get_course_adv_roles($cid,1);
45: my %allstaff;
46: foreach my $role (keys(%coursepersonnel)) {
47: my @staff = split(',',$coursepersonnel{$role});
48: foreach my $person (@staff) {
49: $allstaff{$person} = 1;
50: }
51: }
52: my ($fh,$statusfh,$namesfh);
53: unless(open $fh,'>/root/timeontask_'.$cnum.'.csv') {
54: print "Could not open /root/timeontask_'.$cnum.'.csv for writing\n";
55: next;
56: }
57: unless(open $statusfh,'>/root/status_'.$cnum.'.csv') {
58: print "Could not open /root/status_'.$cnum.'.csv for writing\n";
59: next;
60: }
61: unless(open $namesfh,'>/root/names_'.$cnum.'.csv') {
62: print "Could not open /root/names_'.$cnum.'.csv for writing\n";
63: next;
64: }
65: my %position;
66: my $subdir = &propath($cnum);
67: unless (open my $seqfh,"</home/httpd/lonUsers/$cdom/$subdir/userfiles/$folder") {
68: while (<$seqfh>) {
69: chomp();
70: if (m{^<resource id=\"\d+\" src=\"/res/([^"]+)\" type=\"start\" title=\"Problem (\d+)\" />$}) {
71: $position{$1} = $2;
72: }
73: }
74: }
75: my %users;
76: my (%post,%cstore,%ordered,%unsubmitted,%unviewed,%times,%hists,%status,%parts);
77:
78: my $classlist = &Apache::loncoursedata::get_classlist($cdom,$cnum);
79:
80: my $sth=$dbh->prepare("SELECT student,student_id FROM $user_table");
81: $sth->execute();
82: while ( my($user,$pid) = $sth->fetchrow_array ) {
83: unless ($user eq 'raeburn:gci' || $user eq 'emgward@gmail.com:gci' || $user eq 'libarkin:gci' || $user eq 'moc:gcitest' || $user eq 'mariannec:gcitest' || $user eq 'mcaldwell@hccfl.edu:gcitest' || $user eq ' mcaldwell:gcitest' || $user eq 'mariC:gcitest') {
84: next if ($allstaff{$user});
85: $users{$pid} = $user;
86: }
87: }
88: $sth->finish;
89: if (keys(%users) == 0) {
90: print "No students found in user table: $user_table. You may need to have LON-CAPA create the table by logging into the course, and viewing activity for one student.\n";
91: next;
92: }
93:
94: $sth = $dbh->prepare("SELECT resource FROM $res_table WHERE resource LIKE 'uploaded/$cdom/$cnum/$folder%'");
95: $sth->execute();
96: while (my $resource = $sth->fetchrow_array ) {
97: if ($resource =~ /sequence___(\d+)___/) {
98: $ordered{$1} = $resource;
99: }
100: }
101:
102: print $fh ',';
103: print $statusfh ',';
104: foreach my $res (sort { $a <=> $b } (keys(%ordered))) {
105: my $resource = $ordered{$res};
106: my ($name) = ($resource =~ m{([^/]+)\.problem$});
107: print $fh $name.',';
108: print $statusfh $name.',';
109: }
110: print $fh "\n";
111: print $statusfh "\n";
112:
113: foreach my $pid (sort(keys(%users))) {
114: my $query = "SELECT r.resource,a.time from $act_table a, $res_table r where a.student_id = '$pid' AND a.action = 'POST' AND a.action_values LIKE 'symb%' AND a.res_id=r.res_id ORDER by a.time";
115: $sth = $dbh->prepare($query);
116: $sth->execute();
117: while ( my($resource,$time) = $sth->fetchrow_array ) {
118: my $timestamp = &unsqltime($time);
119: push(@{$post{$users{$pid}}{$resource}},$timestamp);
120: }
121: $sth->finish;
122: $query = "SELECT r.resource,a.time,a.action_values from $act_table a, $res_table r where a.student_id = '$pid' AND a.action = 'CSTORE' AND a.res_id=r.res_id ORDER by a.time";
123: $sth = $dbh->prepare($query);
124: $sth->execute();
125: while ( my($resource,$time,$action) = $sth->fetchrow_array ) {
126: next if ($action =~ /regrader=emgward/);
127: $resource = &URI::Escape::uri_unescape($resource);
128: my $timestamp = &unsqltime($time);
129: push(@{$cstore{$users{$pid}}{$resource}},$timestamp);
130: }
131: $sth->finish;
132: }
133:
134: foreach my $user (sort(keys(%post))) {
135: $status{$user} = {};
136: $parts{$user} = {};
137: my ($uname,$udom) = split(':',$user);
138: my $home = &Apache::lonnet::homeserver($uname,$udom);
139: my $sec;
140: if (ref($classlist) eq 'HASH') {
141: if (ref($classlist->{$user}) eq 'ARRAY') {
142: $sec=$classlist->{$user}->[$secidx];
143: }
144: }
145: my @symbs = &walk_course($user,$cid,$home,$folder,$role,$sec,$parts{$user});
146: my ($lastcstore,$lastpost);
147: foreach my $resource (@symbs) {
148: my (@posts,@cstores);
149: if (ref($post{$user}{$resource}) eq 'ARRAY') {
150: @posts = @{$post{$user}{$resource}};
151: }
152: if (ref($cstore{$user}{$resource}) eq 'ARRAY') {
153: @cstores = @{$cstore{$user}{$resource}};
154: }
155: if (!@cstores) {
156: unless (ref($hists{$user}) eq 'HASH') {
157: my ($uname,$udom) = split(':',$user);
158: my $subdir = &propath($uname);
159: if (open(my $histfh,"</home/httpd/lonUsers/$udom/$subdir/".$cdom.'_'.$cnum.'.hist')) {
160: while(<$histfh>) {
161: chomp();
162: my ($action,$stamp,$res,$submission) = split(/:/);
163: if (($action eq 'S') && ($submission =~ /tries=1/)) {
164: $res = &URI::Escape::uri_unescape($res);
165: push(@{$hists{$user}{$res}},$stamp);
166: }
167: }
168: close($histfh);
169: }
170: }
171: if (ref($hists{$user}) eq 'HASH') {
172: if (ref($hists{$user}{$resource}) eq 'ARRAY') {
173: push(@{$times{$user}{$resource}},$hists{$user}{$resource});
174: @cstores = @{$hists{$user}{$resource}};
175: }
176: }
177: }
178: if (@cstores) {
179: my %record=&Apache::lonnet::restore($resource,$cid,$udom,$uname);
180: if (ref($parts{$user}) eq 'HASH') {
181: if (ref($parts{$user}{$resource}) eq 'ARRAY') {
182: my $numcorrect = 0;
183: my $partscount = 0;
184: foreach my $part (@{$parts{$user}{$resource}}) {
185: if ($record{'resource.'.$part.'.solved'} =~ /^correct/) {
186: $numcorrect ++ ;
187: }
188: $partscount ++;
189: }
190: if (($numcorrect) && ($numcorrect == $partscount)) {
191: $status{$user}{$resource} = 1;
192: }
193: } elsif ($record{'resource.0.solved'} =~ /^correct/) {
194: $status{$user}{$resource} = 1;
195: }
196: } elsif ($record{'resource.0.solved'} =~ /^correct/) {
197: $status{$user}{$resource} = 1;
198: }
199: my $count = 0;
200: foreach my $cstore (@cstores) {
201: $count ++;
202: if (@posts) {
203: my $gotpost = 0;
204: foreach my $post (@posts) {
205: my $diff = $cstore-$post;
206: if ($diff >= 0) {
207: push(@{$times{$resource}{$user}},$diff);
208: $gotpost = 1;
209: } else {
210: my $showpost = &sqltime($post);
211: my $showcstore = &sqltime($cstore);
212: # print "$user $resource $diff FROM $showpost AND $showcstore\n";
213: }
214: }
215: unless ($gotpost) {
216: if ($lastcstore) {
217: my $diff = $cstore - $lastcstore;
218: if ($diff > 0) {
219: push(@{$times{$resource}{$user}},$diff);
220: }
221: } elsif ($lastpost) {
222: my $diff = $cstore - $lastpost;
223: if ($diff > 0) {
224: push(@{$times{$resource}{$user}},$diff);
225: }
226: }
227: }
228: } else {
229: if ($lastcstore) {
230: my $diff = $cstore - $lastcstore;
231: if ($diff > 0) {
232: push(@{$times{$resource}{$user}},$diff);
233: }
234: } elsif ($lastpost) {
235: my $diff = $cstore - $lastpost;
236: if ($diff > 0) {
237: push(@{$times{$resource}{$user}},$diff);
238: }
239: }
240: }
241: $lastpost = $posts[-1];
242: }
243: $lastcstore = $cstores[-1];
244: } else {
245: if (!@posts) {
246: $unviewed{$user}{$resource} = 1;
247: } else {
248: $unsubmitted{$user}{$resource} = 1;
249: }
250: }
251: }
252: }
253:
254: my $num = 0;
255: foreach my $user (keys(%post)) {
256: $num ++;
257: print $namesfh $num.','.$user."\n";
258: print $fh $num.',';
259: print $statusfh $num.',';
260: foreach my $res (sort { $a <=> $b } (keys(%ordered))) {
261: my $resource = $ordered{$res};
262: if (ref($times{$resource}) eq 'HASH') {
263: if (ref($times{$resource}{$user}) eq 'ARRAY') {
264: print $fh $times{$resource}{$user}[-1];
265: # print $fh join(':',@{$times{$resource}{$user}});
266: }
267: }
268: print $fh ',';
269: if (ref($status{$user}) eq 'HASH') {
270: print $statusfh $status{$user}{$resource};
271: }
272: print $statusfh ',';
273: }
274: print $fh "\n";
275: print $statusfh "\n";
276: }
277: close($fh);
278: close($statusfh);
279: close($namesfh);
280: }
281:
282: sub sqltime {
283: my ($timestamp) = @_;
284: my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
285: localtime($timestamp);
286: $mon++; $year+=1900;
287: return "$year-$mon-$mday $hour:$min:$sec";
288: }
289:
290: sub unsqltime {
291: my $timestamp=shift;
292: if ($timestamp=~/^(\d+)\-(\d+)\-(\d+)\s+(\d+)\:(\d+)\:(\d+)$/) {
293: $timestamp=&maketime('year'=>$1,'month'=>$2,'day'=>$3,
294: 'hours'=>$4,'minutes'=>$5,'seconds'=>$6);
295: }
296: return $timestamp;
297: }
298:
299: sub maketime {
300: my %th=@_;
301: return POSIX::mktime(($th{'seconds'},$th{'minutes'},$th{'hours'},
302: $th{'day'},$th{'month'}-1,
303: $th{'year'}-1900,0,0,$th{'dlsav'}));
304: }
305:
306: sub propath {
307: my ($cnum)=@_;
308: my $subdir=$cnum.'__';
309: $subdir =~ s/(.)(.)(.).*/$1\/$2\/$3/;
310: my $proname="$subdir/$cnum";
311: return $proname;
312: }
313:
314: sub walk_course {
315: my ($user,$cid,$home,$folder,$role,$sec,$parts) = @_;
316: my ($uname,$udom) = split(':',$user);
317: my $cookie =
318: &Apache::loncommon::init_user_environment(undef, $uname, $udom,
319: $home, undef,
320: {'robot' => 'walkcourse',});
321: my @symbs;
322: if ($cookie) {
323: if (keys(%env) > 0) {
324: my ($cdom,$cnum) = split('_',$cid);
325: my ($furl,$ferr) =
326: &Apache::lonuserstate::readmap($cdom.'/'.$cnum);
327: return if ($ferr ne '');
328: my $rolecode = $role.'./'.$cdom.'/'.$cnum;
329: if ($sec ne '') {
330: $rolecode .= '/'.$sec;
331: }
332: &Apache::lonnet::appenv(
333: {
334: 'request.course.id' => $cid,
335: 'request.role' => $rolecode,
336: 'request.role.domain' => $cdom,
337: });
338: if ($sec ne '') {
339: &Apache::lonnet::appenv(
340: {'request.course.sec' => $sec,
341: 'user.priv.'.$rolecode.'./'.$cdom.'/'.$cnum.'/'.$sec => ':bre&RXL',
342: },[$role]);
343: } else {
344: &Apache::lonnet::appenv(
345: {'user.priv.'.$rolecode.'./'.$cdom.'/'.$cnum => ':bre&RXL',
346: },[$role]);
347: }
348: my $navmap = Apache::lonnavmaps::navmap->new();
349: if (ref($navmap)) {
350: my $mapurl = '/uploaded/'.$cdom.'/'.$cnum.'/'.$folder;
351: my $map = $navmap->getResourceByUrl($mapurl);
352: my $firstResource = $map->map_start();
353: my $finishResource = $map->map_finish();
354: if (ref($firstResource) && ref($finishResource)) {
355: my $it = $navmap->getIterator($firstResource, $finishResource,undef,1);
356: my $curRes;
357: while ($curRes = $it->next()) {
358: if (ref($curRes)) {
359: unless ($curRes->is_sequence() || $curRes->is_page()) {
360: my $symb = $curRes->symb();
361: if (ref($parts) eq 'HASH') {
362: $parts->{$symb} = $curRes->parts();
363: }
364: unless ($curRes->randomout()) {
365: if ($symb) {
366: push(@symbs,$symb);
367: }
368: }
369: }
370: }
371: }
372: undef($navmap);
373: } else {
374: print "No navmap object\n";
375: }
376: }
377: }
378: }
379: undef(%env);
380: return @symbs;
381: }
382:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>