source: pyweek_upload.py

Last change on this file was 0:d6fd1a0b192a, checked in by Simon Cross <hodgestar@…>, 9 years ago

Commit skellington.

File size: 6.1 KB
Line 
1'''
2Upload script specifically engineered for the PyWeek challenge.
3
4Handles authentication and gives upload progress feedback.
5'''
6import sys, os, httplib, cStringIO, socket, time, getopt
7
8class Upload:
9    def __init__(self, filename):
10        self.filename = filename
11
12boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
13sep_boundary = '\n--' + boundary
14end_boundary = sep_boundary + '--'
15
16def mimeEncode(data, sep_boundary=sep_boundary, end_boundary=end_boundary):
17    '''Take the mapping of data and construct the body of a
18    multipart/form-data message with it using the indicated boundaries.
19    '''
20    ret = cStringIO.StringIO()
21    for key, value in data.items():
22        # handle multiple entries for the same name
23        if type(value) != type([]): value = [value]
24        for value in value:
25            ret.write(sep_boundary)
26            if isinstance(value, Upload):
27                ret.write('\nContent-Disposition: form-data; name="%s"'%key)
28                filename = os.path.basename(value.filename)
29                ret.write('; filename="%s"\n\n'%filename)
30                value = open(os.path.join(value.filename), "rb").read()
31            else:
32                ret.write('\nContent-Disposition: form-data; name="%s"'%key)
33                ret.write("\n\n")
34                value = str(value)
35            ret.write(str(value))
36            if value and value[-1] == '\r':
37                ret.write('\n')  # write an extra newline
38    ret.write(end_boundary)
39    return ret.getvalue()
40
41class Progress:
42    def __init__(self, info, data):
43        self.info = info
44        self.tosend = len(data)
45        self.total = self.tosend/1024
46        self.data = cStringIO.StringIO(data)
47        self.start = self.now = time.time()
48        self.sent = 0
49        self.num = 0
50        self.stepsize = self.total / 100 or 1
51        self.steptimes = []
52        self.display()
53
54    def __iter__(self): return self
55
56    def next(self):
57        self.num += 1
58        if self.sent >= self.tosend:
59            print self.info, 'done', ' '*(75-len(self.info)-6)
60            sys.stdout.flush()
61            raise StopIteration
62
63        chunk = self.data.read(1024)
64        self.sent += len(chunk)
65        #print (self.num, self.stepsize, self.total, self.sent, self.tosend)
66
67        if self.num % self.stepsize:
68            return chunk
69        self.display()
70        return chunk
71
72    def display(self):
73        # figure how long we've spent - guess how long to go
74        now = time.time()
75        steptime = now - self.now
76        self.steptimes.insert(0, steptime)
77        if len(self.steptimes) > 5:
78            self.steptimes.pop()
79        steptime = sum(self.steptimes) / len(self.steptimes)
80        self.now = now
81        eta = steptime * ((self.total - self.num)/self.stepsize)
82
83        # tell it like it is (or might be)
84        if now - self.start > 3:
85            M = eta / 60
86            H = M / 60
87            M = M % 60
88            S = eta % 60
89            if self.total:
90                s = '%s %2d%% (ETA %02d:%02d:%02d)'%(self.info,
91                    self.num * 100. / self.total, H, M, S)
92            else:
93                s = '%s 0%% (ETA %02d:%02d:%02d)'%(self.info, H, M, S)
94        elif self.total:
95            s = '%s %2d%%'%(self.info, self.num * 100. / self.total)
96        else:
97            s = '%s %d done'%(self.info, self.num)
98        sys.stdout.write(s + ' '*(75-len(s)) + '\r')
99        sys.stdout.flush()
100
101class progressHTTPConnection(httplib.HTTPConnection):
102    def progress_send(self, str):
103        """Send `str' to the server."""
104        if self.sock is None:
105            self.connect()
106
107        p = Progress('Uploading', str)
108        for chunk in p:
109            sent = 0
110            while sent != len(chunk):
111                try:
112                    sent += self.sock.send(chunk)
113                except socket.error, v:
114                    if v[0] == 32:      # Broken pipe
115                        self.close()
116                    raise
117                p.display()
118
119class progressHTTP(httplib.HTTP):
120    _connection_class = progressHTTPConnection
121    def _setup(self, conn):
122        httplib.HTTP._setup(self, conn)
123        self.progress_send = self._conn.progress_send
124
125def http_request(data, server, port, url):
126    h = progressHTTP(server, port)
127
128    data = mimeEncode(data)
129    h.putrequest('POST', url)
130    h.putheader('Content-type', 'multipart/form-data; boundary=%s'%boundary)
131    h.putheader('Content-length', str(len(data)))
132    h.putheader('Host', server)
133    h.endheaders()
134
135    h.progress_send(data)
136
137    errcode, errmsg, headers = h.getreply()
138
139    f = h.getfile()
140    response = f.read().strip()
141    f.close()
142
143    print '%s %s'%(errcode, errmsg)
144    if response: print response
145
146def usage():
147    print '''This program is to be used to upload files to the PyWeek system.
148You may use it to upload screenshots or code submissions.
149
150REQUIRED ARGUMENTS:
151 -u   username
152 -p   password
153 -d   description of file
154 -c   file to upload
155 -e   entry short name
156
157OPTIONAL ARGUMENTS:
158 -s   file is a screenshot
159 -f   file is FINAL submission
160 -h   override default host name (www.pyweek.org)
161 -P   override default host port (80)
162
163In order to qualify for judging at the end of the challenge, you MUST
164upload your source and check the "Final Submission" checkbox.
165'''
166
167
168if __name__ == '__main__':
169    try:
170        optlist, args = getopt.getopt(sys.argv[1:], 'e:u:p:sfd:h:P:c:')
171    except getopt.GetoptError, message:
172        print message
173        usage()
174        sys.exit(1)
175    host = 'www.pyweek.org'
176    port = 80
177    data = dict(version=2)
178    optional = {}
179    url = None
180    for opt, arg in optlist:
181        if opt == '-u': data['user'] = arg
182        elif opt == '-p': data['password'] = arg
183        elif opt == '-s': optional['is_screenshot'] = 'yes'
184        elif opt == '-f': optional['is_final'] = 'yes'
185        elif opt == '-d': data['description'] = arg
186        elif opt == '-c': data['content_file'] = Upload(arg)
187        elif opt == '-e': url = '/e/%s/oup/'%arg
188        elif opt == '-h': host = arg
189        elif opt == '-P': port = int(arg)
190
191    if len(data) < 4 or url is None:
192        print 'Required argument missing'
193        usage()
194        sys.exit(1)
195
196    data.update(optional)
197    http_request(data, host, port, url)
198
Note: See TracBrowser for help on using the repository browser.