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

« back to all changes in this revision

Viewing changes to bin/ivle-fetchsubmissions

Better instructions in the usage message.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
# IVLE - Informatics Virtual Learning Environment
 
3
# Copyright (C) 2007-2009 The University of Melbourne
 
4
#
 
5
# This program is free software; you can redistribute it and/or modify
 
6
# it under the terms of the GNU General Public License as published by
 
7
# the Free Software Foundation; either version 2 of the License, or
 
8
# (at your option) any later version.
 
9
#
 
10
# This program is distributed in the hope that it will be useful,
 
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
# GNU General Public License for more details.
 
14
#
 
15
# You should have received a copy of the GNU General Public License
 
16
# along with this program; if not, write to the Free Software
 
17
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
18
 
 
19
# Program: Fetch Submissions
 
20
# Author:  Matt Giuca
 
21
 
 
22
# Script to retrieve all submissions for a particular project.
 
23
# Requires root to run.
 
24
 
 
25
import sys
 
26
import os
 
27
import datetime
 
28
import optparse
 
29
 
 
30
import pysvn
 
31
 
 
32
if os.getuid() != 0:
 
33
    print >>sys.stderr, "Must run %s as root." % os.path.basename(sys.argv[0])
 
34
    sys.exit()
 
35
 
 
36
import ivle.config
 
37
import ivle.database
 
38
 
 
39
from ivle.database import Project, ProjectSet, Offering, Subject
 
40
 
 
41
def fetch_submission(submission, target, svnclient, config):
 
42
    """Fetch a submission from a user's repository, and dump it in a given
 
43
    directory.
 
44
    @param submission: Submission object, detailing the submission.
 
45
    @param target: Target directory for the project (will create a
 
46
        subdirectory for each submission).
 
47
    @param config: Config object.
 
48
    """
 
49
    # submission_name is the name of the user or group who owns the repo
 
50
    submission_name = submission.assessed.principal.name
 
51
    # target_final is the directory to place the files in
 
52
    target_final = os.path.join(target, submission.assessed.principal.name)
 
53
    if not os.path.exists(target_final):
 
54
        os.makedirs(target_final)
 
55
    url = get_repo_url(submission, config)
 
56
    revision = pysvn.Revision(pysvn.opt_revision_kind.number,
 
57
                              submission.revision)
 
58
    svnclient.export(url, target_final, force=True,
 
59
        revision=revision, recurse=True)
 
60
 
 
61
def get_repo_url(submission, config):
 
62
    """Gets a local (file:) URL to the repository for a given submission.
 
63
    This will consult submission.path to find the path within the repository
 
64
    to check out.
 
65
    @param submission: Submission object, detailing the submission.
 
66
    @param config: Config object.
 
67
    """
 
68
    # NOTE: This code is mostly copied from services/usrmgt-server
 
69
    if submission.assessed.is_group:
 
70
        # The offering this group is in
 
71
        offering = submission.assessed.project.project_set.offering
 
72
        groupname = submission.assessed.principal.name
 
73
        # The name of the repository directory within 'groups' is
 
74
        # SUBJECT_YEAR_SEMESTER_GROUP
 
75
        namespace = "_".join([offering.subject.short_name,
 
76
            offering.semester.year, offering.semester.semester, groupname])
 
77
        repo_path = os.path.join(config['paths']['svn']['repo_path'],
 
78
                                'groups', namespace)
 
79
    else:
 
80
        # The name of the repository directory within 'users' is the username
 
81
        username = submission.assessed.principal.name
 
82
        repo_path = os.path.join(config['paths']['svn']['repo_path'],
 
83
                                'users', username)
 
84
 
 
85
    path_in_repo = submission.path
 
86
    # Change an absolute path into a relative one (to the top of SVN)
 
87
    if path_in_repo[:1] == os.sep or path_in_repo[:1] == os.altsep:
 
88
        path_in_repo = path_in_repo[1:]
 
89
 
 
90
    # Attach "file://" to the front of the absolute path, to make it a URL
 
91
    return "file://" + os.path.join(os.path.abspath(repo_path), path_in_repo)
 
92
 
 
93
def main(argv=None):
 
94
    global store
 
95
    if argv is None:
 
96
        argv = sys.argv
 
97
 
 
98
    usage = """usage: %prog [options] subject projname
 
99
    (requires root)
 
100
    Retrieves all submissions for a given project. Places each submission in
 
101
    its own directory, in a subdirectory of '.'. Any errors are reported to
 
102
    stderr (otherwise is silent).
 
103
    subject/projname is the subject/project's short name.
 
104
    """
 
105
 
 
106
    # Parse arguments
 
107
    parser = optparse.OptionParser(usage)
 
108
    parser.add_option("-s", "--semester",
 
109
        action="store", dest="semester", metavar="YEAR/SEMESTER",
 
110
        help="Semester of the subject's offering (eg. 2009/1). "
 
111
             "Defaults to the currently active semester.",
 
112
        default=None)
 
113
    parser.add_option("-c", "--cutoff",
 
114
        action="store", dest="cutoff", metavar="DATE",
 
115
        help="Cutoff date (retrieve the submissions as of this date)."
 
116
             "YYYY-MM-DD H:M:S.",
 
117
        default=None)
 
118
    parser.add_option("-d", "--dest",
 
119
        action="store", dest="dest", metavar="PATH",
 
120
        help="Destination directory (default to '.', creates a subdirectory, "
 
121
            "so will not pollute PATH).",
 
122
        default=".")
 
123
    (options, args) = parser.parse_args(argv[1:])
 
124
 
 
125
    if len(args) < 2:
 
126
        parser.print_help()
 
127
        parser.exit()
 
128
 
 
129
    subject_name = unicode(args[0])
 
130
    project_name = unicode(args[1])
 
131
 
 
132
    if options.semester is None:
 
133
        year, semester = None, None
 
134
    else:
 
135
        try:
 
136
            year, semester = options.semester.split('/')
 
137
            if len(year) == 0 or len(semester) == 0:
 
138
                raise ValueError()
 
139
        except ValueError:
 
140
            parser.error('Invalid semester (must have form "year/semester")')
 
141
 
 
142
    if options.cutoff is not None:
 
143
        try:
 
144
            cutoff = datetime.datetime.strptime(options.cutoff,
 
145
                                                "%Y-%m-%d %H:%M:%S")
 
146
        except ValueError:
 
147
            parser.error("Invalid date format: '%s' "
 
148
                         "(must be YYYY-MM-DD H:M:S)." % options.cutoff)
 
149
    else:
 
150
        cutoff = None
 
151
 
 
152
    svnclient = pysvn.Client()
 
153
    config = ivle.config.Config(plugins=False)
 
154
    store = ivle.database.get_store(config)
 
155
 
 
156
    # Get the subject from the DB
 
157
    subject = store.find(Subject,
 
158
                     Subject.short_name == subject_name).one()
 
159
    if subject is None:
 
160
        print >>sys.stderr, "No subject with short name '%s'" % subject_name
 
161
        return 1
 
162
 
 
163
    # Get the offering from the DB
 
164
    if semester is None:
 
165
        # None specified - get the current offering from the DB
 
166
        offerings = list(subject.active_offerings())
 
167
        if len(offerings) == 0:
 
168
            print >>sys.stderr, ("No active offering for subject '%s'"
 
169
                                 % subject_name)
 
170
            return 1
 
171
        elif len(offerings) > 1:
 
172
            print >>sys.stderr, ("Multiple active offerings for subject '%s':"
 
173
                                 % subject_name)
 
174
            print >>sys.stderr, "Please use one of:"
 
175
            for offering in offerings:
 
176
                print >>sys.stderr, ("    --semester=%s/%s"
 
177
                    % (offering.semester.year, offering.semester.semester))
 
178
            return 1
 
179
        else:
 
180
            offering = offerings[0]
 
181
    else:
 
182
        # Get the offering for the specified semester
 
183
        offering = subject.offering_for_semester(year, semester)
 
184
        if offering is None:
 
185
            print >>sys.stderr, (
 
186
                "No offering for subject '%s' in semester %s/%s"
 
187
                % (subject_name, year, semester))
 
188
            return 1
 
189
 
 
190
    # Get the project from the DB
 
191
    project = store.find(Project,
 
192
                         Project.project_set_id == ProjectSet.id,
 
193
                         ProjectSet.offering == offering,
 
194
                         Project.short_name == project_name).one()
 
195
    if project is None:
 
196
        print >>sys.stderr, "No project with short name '%s'" % project_name
 
197
        return 1
 
198
 
 
199
    # Target directory is DEST/subject/year/semester/project
 
200
    target_dir = os.path.join(options.dest, subject_name,
 
201
        offering.semester.year, offering.semester.semester, project_name)
 
202
    if not os.path.exists(target_dir):
 
203
        os.makedirs(target_dir)
 
204
 
 
205
    for submission in project.latest_submissions:
 
206
        try:
 
207
            fetch_submission(submission, target_dir, svnclient, config)
 
208
        except Exception, e:
 
209
            # Catch all exceptions (to ensure if one student has a problem, it
 
210
            # is reported, and we can continue)
 
211
            print >>sys.stderr, "%s: %s: %s" % (
 
212
                submission.assessed.principal.display_name,
 
213
                type(e).__name__, e)
 
214
 
 
215
if __name__ == "__main__":
 
216
    sys.exit(main(sys.argv))