Index: openacs-4/packages/proctoring-support/proctoring-support.info
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/proctoring-support/proctoring-support.info,v
diff -u -r1.1.2.25 -r1.1.2.26
--- openacs-4/packages/proctoring-support/proctoring-support.info 10 Feb 2022 09:32:58 -0000 1.1.2.25
+++ openacs-4/packages/proctoring-support/proctoring-support.info 10 Feb 2022 13:16:36 -0000 1.1.2.26
@@ -10,7 +10,7 @@
f
proctoring
-
+
Antonio Pisano
Set of tools to implement proctoring of user interaction
Wirtschaftsuniversität Wien
@@ -21,7 +21,7 @@
No real UI is provided by the package itself. Other packages must integrate the provided includes.
0
-
+
Index: openacs-4/packages/proctoring-support/lib/proctoring-upload.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/proctoring-support/lib/proctoring-upload.tcl,v
diff -u -r1.1.2.13 -r1.1.2.14
--- openacs-4/packages/proctoring-support/lib/proctoring-upload.tcl 10 Feb 2022 09:32:58 -0000 1.1.2.13
+++ openacs-4/packages/proctoring-support/lib/proctoring-upload.tcl 10 Feb 2022 13:16:36 -0000 1.1.2.14
@@ -75,21 +75,15 @@
}
set timestamp [clock seconds]
- set file_path $proctoring_dir/${name}-${type}-$timestamp.$extension
- file mkdir -- $proctoring_dir
- file rename -force -- ${file.tmpfile} $file_path
+ set artifact [::proctoring::artifact::store \
+ -object_id $object_id \
+ -user_id $user_id \
+ -timestamp $timestamp \
+ -name $name \
+ -type $type \
+ -file ${file.tmpfile}]
- # Create an entry in the database for the file we have just
- # collected, so that we can further enrich it with metadata in
- # later postprocessing phases.
- ::xo::dc dml -prepare {integer integer integer text text text} init_artifact {
- insert into proctoring_object_artifacts
- (object_id, user_id, timestamp, name, type, file)
- values
- (:object_id, :user_id, to_timestamp(:timestamp), :name, :type, :file_path)
- }
-
# Notify a websocket about the upload so that e.g. a UI can be updated
# in real time.
if {$notify_p} {
@@ -102,7 +96,7 @@
"name": "$name",
"type": "$type",
"timestamp": "$timestamp",
- "file": "$file_path"
+ "file": "[dict get $artifact file]"
}
}]
Index: openacs-4/packages/proctoring-support/tcl/proctoring-callback-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/proctoring-support/tcl/proctoring-callback-procs.tcl,v
diff -u -r1.1.2.3 -r1.1.2.4
--- openacs-4/packages/proctoring-support/tcl/proctoring-callback-procs.tcl 9 Feb 2022 14:41:36 -0000 1.1.2.3
+++ openacs-4/packages/proctoring-support/tcl/proctoring-callback-procs.tcl 10 Feb 2022 13:16:36 -0000 1.1.2.4
@@ -67,3 +67,20 @@
@return dict with fields 'object_id' and 'object_url'
} -
+
+namespace eval ::proctoring::callback {}
+namespace eval ::proctoring::callback::artifact {}
+
+ad_proc -public -callback ::proctoring::callback::artifact::postprocess {
+ -artifact_id:required
+} {
+ Implementations of this hook can apply custom postprocessing to a
+ proctoring artifact.
+
+ Be aware that this callback is invoked as soon as the artifact is
+ created, for instance, at upload. Every callback implementation
+ should defer to background processing every operation that would
+ block a connection thread for a long time.
+
+ @param artifact_id id of the artifact
+} -
Index: openacs-4/packages/proctoring-support/tcl/proctoring-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/proctoring-support/tcl/proctoring-procs.tcl,v
diff -u -r1.1.2.14 -r1.1.2.15
--- openacs-4/packages/proctoring-support/tcl/proctoring-procs.tcl 9 Feb 2022 14:50:42 -0000 1.1.2.14
+++ openacs-4/packages/proctoring-support/tcl/proctoring-procs.tcl 10 Feb 2022 13:16:36 -0000 1.1.2.15
@@ -256,3 +256,84 @@
return $already_received_p
}
+namespace eval ::proctoring {}
+namespace eval ::proctoring::artifact {}
+
+ad_proc ::proctoring::artifact::store {
+ -object_id:required
+ -user_id:required
+ -timestamp
+ -name:required
+ -type:required
+ -file:required
+} {
+ Stores a file as a new artifacts and invoke the postprocessing
+ callbacks.
+
+ @param object_id id of the proctored object
+ @param user_id id of the user this artifact was created for
+ @param timestamp epoch in seconds. Defaults to clock seconds when
+ not specified
+ @param name name of the source for this artifact (e.g. 'camera' or 'desktop')
+ @param type type of artifact (e.g. 'image' or 'audio')
+ @param file absolute path to the artifact file. The file will be
+ moved inside of the proctoring folde r, so this can be
+ a tempfile from a request.
+
+ @return dict of fields 'artifact_id' and 'file'. File is the final
+ path of the file in the proctoring folder.
+} {
+ if {![file exists $file]} {
+ error "File does not exist"
+ }
+
+ if {![info exists timestamp]} {
+ set timestamp [clock seconds]
+ }
+
+ set proctoring_dir [::proctoring::folder \
+ -object_id $object_id \
+ -user_id $user_id]
+
+ set file_path $proctoring_dir/${name}-${type}-${timestamp}[file extension $file]
+ file rename -force -- $file $file_path
+
+ # Create an entry in the database for the file we have just
+ # collected, so that we can further enrich it with metadata in
+ # later postprocessing phases.
+ set artifact_id [::xo::dc get_value -prepare {
+ integer integer integer text text text
+ } store {
+ with insert as (
+ insert into proctoring_object_artifacts
+ (
+ artifact_id,
+ object_id,
+ user_id,
+ timestamp,
+ name,
+ type,
+ file
+ )
+ values
+ (
+ default,
+ :object_id,
+ :user_id,
+ to_timestamp(:timestamp),
+ :name,
+ :type,
+ :file_path
+ )
+ returning artifact_id
+ )
+ select artifact_id from insert
+ }]
+
+ callback ::proctoring::callback::artifact::postprocess \
+ -artifact_id $artifact_id
+
+ return [list \
+ artifact_id $artifact_id \
+ file $file_path]
+}
Index: openacs-4/packages/proctoring-support/tcl/test/proctoring-test-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/proctoring-support/tcl/test/proctoring-test-procs.tcl,v
diff -u -r1.1.2.12 -r1.1.2.13
--- openacs-4/packages/proctoring-support/tcl/test/proctoring-test-procs.tcl 16 Jun 2021 11:26:55 -0000 1.1.2.12
+++ openacs-4/packages/proctoring-support/tcl/test/proctoring-test-procs.tcl 10 Feb 2022 13:16:36 -0000 1.1.2.13
@@ -264,6 +264,130 @@
}
}
+aa_register_case \
+ -cats {api smoke} \
+ -procs {
+ ::proctoring::artifact::store
+ } \
+ proctoring_artifact_store {
+ Test ::proctoring::artifact::store
+ } {
+ set file [ad_tmpnam].test
+ set wfd [open $file w]
+ puts $wfd 1234
+ close $wfd
+
+ set object_id [::xo::dc get_value get_object_id {
+ select max(object_id) from acs_objects
+ }]
+ set user_id [::xo::dc get_value get_user_id {
+ select max(user_id) from users
+ }]
+ set name test
+ set type code
+ set timestamp [clock scan "2016-09-07" -format %Y-%m-%d]
+
+ aa_section "Initial cleanup"
+ ::xo::dc dml cleanup {
+ delete from proctoring_object_artifacts
+ where name = :name
+ and type = :type
+ and timestamp = to_timestamp(:timestamp)
+ and user_id = :user_id
+ and object_id = :object_id
+ }
+
+ aa_section "Storing an artifact correctly"
+ set artifact [::proctoring::artifact::store \
+ -object_id $object_id \
+ -user_id $user_id \
+ -timestamp $timestamp \
+ -name $name \
+ -type $type \
+ -file $file]
+
+ set artifact_id [dict get $artifact artifact_id]
+ set file [dict get $artifact file]
+ aa_true "Artifact '$artifact_id' on file '$file' was created" [::xo::dc 0or1row check {
+ select 1 from proctoring_object_artifacts
+ where artifact_id = :artifact_id
+ and file = :file
+ and name = :name
+ and type = :type
+ and timestamp = to_timestamp(:timestamp)
+ and user_id = :user_id
+ and object_id = :object_id
+ }]
+ aa_true "File exists" [file exists $file]
+ aa_equals "File has the original extension" .test [file extension $file]
+
+ aa_section "Cleanup"
+ ::xo::dc dml cleanup {
+ delete from proctoring_object_artifacts
+ where artifact_id = :artifact_id
+ }
+ aa_equals "Row was deleted" [db_resultrows] 1
+ file delete -- $file
+ aa_false "File was removed" [file exists $file]
+
+ aa_section "Try to store for an invalid object"
+ set broken_object_id [::xo::dc get_value get_broken_object_id {
+ select min(object_id) - 1 from acs_objects
+ }]
+ aa_true "Saving for an invalid object fails" [catch {
+ ::proctoring::artifact::store \
+ -object_id $broken_object_id \
+ -user_id $user_id \
+ -timestamp $timestamp \
+ -name $name \
+ -type $type \
+ -file $file
+ }]
+ aa_false "No row can be found for broken object '$broken_object_id'" [::xo::dc 0or1row check {
+ select 1 from proctoring_object_artifacts
+ where object_id = :broken_object_id
+ fetch first 1 rows only
+ }]
+
+ aa_section "Try to store for an invalid user"
+ set broken_user_id [::xo::dc get_value get_broken_user_id {
+ select min(user_id) - 1 from users
+ }]
+ aa_true "Saving for an invalid user fails" [catch {
+ ::proctoring::artifact::store \
+ -object_id $object_id \
+ -user_id $broken_user_id \
+ -timestamp $timestamp \
+ -name $name \
+ -type $type \
+ -file $file
+ }]
+ aa_false "No row can be found for broken user '$broken_user_id'" [::xo::dc 0or1row check {
+ select 1 from proctoring_object_artifacts
+ where user_id = :broken_user_id
+ fetch first 1 rows only
+ }]
+
+ aa_section "Try to store a non-existing file"
+ set broken_file [ad_tmpnam]
+ aa_true "Saving a non-existing file fails" [catch {
+ ::proctoring::artifact::store \
+ -object_id $object_id \
+ -user_id $user_id \
+ -timestamp $timestamp \
+ -name $name \
+ -type $type \
+ -file $broken_file
+ }]
+ aa_false "No row can be found for non-existing file '$broken_file'" [::xo::dc 0or1row check {
+ select 1 from proctoring_object_artifacts
+ where file = :broken_file
+ and object_id = :object_id
+ and user_id = :user_id
+ fetch first 1 rows only
+ }]
+ }
+
# Local variables:
# mode: tcl
# tcl-indent-level: 4