source: pyweek_upload.py@ 244:93a20b51963f

Last change on this file since 244:93a20b51963f was 2:e30d192e9031, checked in by Simon Cross <hodgestar@…>, 8 years ago

Fix PEP8 errors.

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