 
         _finish_copy: (assignment, student, type, err) =>
             if student? and assignment?
+                store = get_store(); student = store.get_student(student); assignment = store.get_assignment(assignment)
                 where = {table:'assignments', assignment_id:assignment.get('assignment_id')}
                 x = syncdb.select_one(where:where)?[type] ? {}
                 x[student.get('student_id')] = {time: misc.mswalltime(), error:err}
                 @_update(set:{"#{type}":x}, where:where)
+
+        _start_copy: (assignment, student, type) =>
+            console.log("_start_copy")
+            if student? and assignment?
+                store = get_store(); student = store.get_student(student); assignment = store.get_assignment(assignment)
+                where = {table:'assignments', assignment_id:assignment.get('assignment_id')}
+                x = syncdb.select_one(where:where)?[type] ? {}
+                y = (x[student.get('student_id')]) ? {}
+                if y.start? and new Date() - y.start <= 15000
+                    console.log("_start_copy: bail")
+                    return true  # never retry a copy until at least 15 seconds later.
+                y.start = misc.mswalltime()
+                x[student.get('student_id')] = y
+                console.log("_start_copy: do it")
+                @_update(set:{"#{type}":x}, where:where)
+            return false
+
+        _stop_copy: (assignment, student, type) =>
+            if student? and assignment?
+                store = get_store(); student = store.get_student(student); assignment = store.get_assignment(assignment)
+                where = {table:'assignments', assignment_id:assignment.get('assignment_id')}
+                x = syncdb.select_one(where:where)?[type]
+                if not x?
+                    return
+                y = (x[student.get('student_id')])
+                if not y?
+                    return
+                if y.start?
+                    delete y.start
+                    x[student.get('student_id')] = y
+                    @_update(set:{"#{type}":x}, where:where)
 
         # Copy the files for the given assignment to the given student. If
         # the student project doesn't exist yet, it will be created.

        #
        # where time >= now is the current time in milliseconds.
        copy_assignment_from_student: (assignment, student) =>
            id = @set_activity(desc:"Copying assignment from a student")
            finish = (err) =>
                @clear_activity(id)

                    @return_assignment_to_student(assignment_id, student_id)
                else
                    @set_error("copy_assignment -- unknown type: #{type}")

        # This doesn't really stop it yet, since that's not supported by the backend.
        # It does stop the spinner and let the user try to restart the copy.
        stop_copying_assignment: (type, assignment_id, student_id) =>
            switch type
                when 'assigned'
                    type = 'last_assignment'
                when 'collected'
                    type = 'last_collect'
                when 'graded'
                    type = 'last_return_graded'
            @_stop_copy(assignment_id, student_id, type)

        open_assignment: (type, assignment_id, student_id) =>
            # type = assigned, collected, graded
