~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to bin/ivle-fetchsubmissions

  • Committer: William Grant
  • Date: 2009-12-08 03:50:24 UTC
  • mfrom: (1294.2.143 ui-the-third)
  • Revision ID: grantw@unimelb.edu.au-20091208035024-wjx8zp54gth15ph8
Merge ui-the-third. This is another UI revamp.

The header is now thin! Thin! The yellow bar is gone. The tabs are gone.
Breadcrumbs are here. Routes is replaced (with an object publishing
mechanism). Views are less repetitive. etc.

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
# Script to retrieve all submissions for a particular project.
23
23
# Requires root to run.
24
24
 
 
25
from __future__ import with_statement
 
26
 
25
27
import sys
26
28
import os
27
29
import shutil
28
30
import datetime
 
31
import codecs
29
32
import optparse
30
33
import zipfile
31
34
import traceback
38
41
 
39
42
import ivle.config
40
43
import ivle.database
 
44
import ivle.util
41
45
 
42
46
from ivle.database import Project, ProjectSet, Offering, Subject
43
47
 
44
 
def fetch_submission(submission, target, svnclient, config, zip=False):
 
48
# Is Python version 2.6 or higher?
 
49
PYTHON26 = map(int, sys.version[:3].split('.')) >= [2, 6]
 
50
 
 
51
def fetch_submission(submission, target, svnclient, config, zip=False,
 
52
    txt=False, verbose=False):
45
53
    """Fetch a submission from a user's repository, and dump it in a given
46
54
    directory.
47
55
    @param submission: Submission object, detailing the submission.
50
58
    @param svnclient: pysvn.Client object.
51
59
    @param config: Config object.
52
60
    @param zip: If True, zips up the submission.
 
61
    @param txt: If True, writes an extra text file with metadata about the
 
62
        submission.
 
63
    @param verbose: If True, writes the name of each submission to stdout.
53
64
    """
54
65
    # submission_name is the name of the user or group who owns the repo
55
 
    submission_name = submission.assessed.principal.name
 
66
    submission_name = submission.assessed.principal.short_name
56
67
    # target_final is the directory to place the files in
57
 
    target_final = os.path.join(target, submission.assessed.principal.name)
58
 
    if not os.path.exists(target_final):
59
 
        os.makedirs(target_final)
 
68
    target_final = os.path.join(target,
 
69
                                submission.assessed.principal.short_name)
 
70
    target_final = target_final.encode('utf-8')
 
71
    if os.path.exists(target_final):
 
72
        # Remove the existing file or directory before re-checking out
 
73
        if os.path.isdir(target_final):
 
74
            ivle.util.safe_rmtree(target_final)
 
75
        else:
 
76
            os.remove(target_final)
60
77
    url = get_repo_url(submission, config)
61
78
    revision = pysvn.Revision(pysvn.opt_revision_kind.number,
62
79
                              submission.revision)
63
80
    svnclient.export(url, target_final, force=True,
64
81
        revision=revision, recurse=True)
65
82
 
 
83
    if txt:
 
84
        # info_final is the directory to place the metadata info files in
 
85
        info_final = target_final + ".txt"
 
86
        write_submission_info(info_final, submission)
 
87
 
66
88
    # If required, zip up the directory
67
89
    if zip:
68
90
        make_zip(target_final, target_final + ".zip")
69
91
        # Remove the target tree
70
 
        shutil.rmtree(target_final)
 
92
        if os.path.isdir(target_final):
 
93
            ivle.util.safe_rmtree(target_final)
 
94
        else:
 
95
            os.remove(target_final)
 
96
 
 
97
    if verbose:
 
98
        print "Exported submission by: %s (%s)" % (
 
99
            submission.assessed.principal.short_name,
 
100
            submission.assessed.principal.display_name)
71
101
 
72
102
def get_repo_url(submission, config):
73
103
    """Gets a local (file:) URL to the repository for a given submission.
80
110
    if submission.assessed.is_group:
81
111
        # The offering this group is in
82
112
        offering = submission.assessed.project.project_set.offering
83
 
        groupname = submission.assessed.principal.name
 
113
        groupname = submission.assessed.principal.short_name
84
114
        # The name of the repository directory within 'groups' is
85
115
        # SUBJECT_YEAR_SEMESTER_GROUP
86
116
        namespace = "_".join([offering.subject.short_name,
89
119
                                'groups', namespace)
90
120
    else:
91
121
        # The name of the repository directory within 'users' is the username
92
 
        username = submission.assessed.principal.name
 
122
        username = submission.assessed.principal.short_name
93
123
        repo_path = os.path.join(config['paths']['svn']['repo_path'],
94
124
                                'users', username)
95
125
 
104
134
def make_zip(source, dest):
105
135
    """Zip up a directory tree or file. The ZIP file will always contain just
106
136
    a single directory or file at the top level (it will not be a ZIP bomb).
 
137
    XXX In Python 2.5 and earlier, this does NOT create empty directories
 
138
    (it's not possible with the Python2.5 version of zipfile).
107
139
    @param source: Path to a directory or file to zip up.
108
140
    @param dest: Path to a zip file to create.
109
141
    """
112
144
 
113
145
    # Write the source file/directory itself
114
146
    # (If this is a directory it will NOT recurse)
115
 
    zip.write(source, os.path.basename(source))
 
147
    if PYTHON26 or not os.path.isdir(source):
 
148
        # Python < 2.6 errors if you add a directory
 
149
        # (This means you can't add an empty directory)
 
150
        zip.write(source, os.path.basename(source))
116
151
 
117
152
    if os.path.isdir(source):
118
153
        # All paths within the zip file are relative to relativeto
123
158
            raise OSError("Could not access a file (zipping)")
124
159
        for (dirpath, dirnames, filenames) in \
125
160
            os.walk(source, onerror=error):
126
 
            arc_dirpath = os.path.relpath(dirpath, relativeto)
127
 
            for filename in dirnames + filenames:
 
161
            arc_dirpath = ivle.util.relpath(dirpath, relativeto)
 
162
            # Python < 2.6 errors if you add a directory
 
163
            # (This means you can't add an empty directory)
 
164
            if PYTHON26:
 
165
                filenames = dirnames + filenames
 
166
            for filename in filenames:
128
167
                zip.write(os.path.join(dirpath, filename),
129
168
                            os.path.join(arc_dirpath, filename))
130
169
 
 
170
    if not PYTHON26:
 
171
        # XXX Write this _didModify attribute of zip, to trick it into writing
 
172
        # footer bytes even if there are no files in the archive (otherwise it
 
173
        # writes a 0-byte archive which is invalid).
 
174
        # Note that in Python2.6 we avoid this by always writing the top-level
 
175
        # file or directory, at least.
 
176
        zip._didModify = True
131
177
    zip.close()
132
178
 
 
179
def write_submission_info(filename, submission):
 
180
    """Write human-readable meta-data about a submission to a file.
 
181
    @param filename: Filename to write to.
 
182
    @param submission: Submission object.
 
183
    """
 
184
    with codecs.open(filename, 'w', 'utf-8') as f:
 
185
        if submission.assessed.is_group:
 
186
            # A group project
 
187
            print >>f, "Group: %s (%s)" % (
 
188
                submission.assessed.principal.short_name,
 
189
                submission.assessed.principal.display_name)
 
190
        else:
 
191
            # A solo project
 
192
            # Only show the two fields if they are different (only in rare
 
193
            # circumstances)
 
194
            if submission.assessed.principal != submission.submitter:
 
195
                print >>f, "Author: %s (%s)" % (
 
196
                    submission.assessed.principal.short_name,
 
197
                    submission.assessed.principal.display_name)
 
198
        print >>f, "Submitter: %s (%s)" % (
 
199
            submission.submitter.short_name,
 
200
            submission.submitter.display_name)
 
201
        print >>f, "Date: %s" % (
 
202
            submission.date_submitted.strftime("%Y-%m-%d %H:%M:%S"))
 
203
        print >>f, "SVN Revision: %s" % submission.revision
 
204
        print >>f, "SVN Path: %s" % submission.path
 
205
 
133
206
def main(argv=None):
134
207
    global store
135
208
    if argv is None:
159
232
        action="store_true", dest="zip",
160
233
        help="Store each submission in a Zip file.",
161
234
        default=False)
 
235
    parser.add_option("-v", "--verbose",
 
236
        action="store_true", dest="verbose",
 
237
        help="Print out the name of each submission as it is extracted.",
 
238
        default=False)
 
239
    parser.add_option("--no-txt",
 
240
        action="store_false", dest="txt",
 
241
        help="Disable writing a text file with data about each submission.",
 
242
        default=True)
162
243
    (options, args) = parser.parse_args(argv[1:])
163
244
 
164
245
    if len(args) < 2:
234
315
    for submission in project.latest_submissions:
235
316
        try:
236
317
            fetch_submission(submission, target_dir, svnclient, config,
237
 
                             zip=options.zip)
 
318
                             zip=options.zip, txt=options.txt,
 
319
                             verbose=options.verbose)
238
320
        except Exception, e:
239
321
            # Catch all exceptions (to ensure if one student has a problem, it
240
322
            # is reported, and we can continue)
241
323
            print >>sys.stderr, "ERROR on submission for %s:" % (
242
324
                submission.assessed.principal.display_name)
243
 
            traceback.print_exc(e)
 
325
            traceback.print_exc()
244
326
 
245
327
if __name__ == "__main__":
246
328
    sys.exit(main(sys.argv))