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

« back to all changes in this revision

Viewing changes to bin/ivle-fetchsubmissions

Merge from submissions-admin.

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))