0
|
1 '''
|
|
2 Upload script specifically engineered for the PyWeek challenge.
|
|
3
|
|
4 Handles authentication and gives upload progress feedback.
|
|
5 '''
|
2
|
6 import sys
|
|
7 import os
|
|
8 import httplib
|
|
9 import cStringIO
|
|
10 import socket
|
|
11 import time
|
|
12 import getopt
|
0
|
13
|
2
|
14
|
|
15 class Upload(object):
|
0
|
16 def __init__(self, filename):
|
|
17 self.filename = filename
|
|
18
|
|
19 boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
|
|
20 sep_boundary = '\n--' + boundary
|
|
21 end_boundary = sep_boundary + '--'
|
|
22
|
2
|
23
|
0
|
24 def 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
|
2
|
31 if not isinstance(value, list):
|
|
32 value = [value]
|
0
|
33 for value in value:
|
|
34 ret.write(sep_boundary)
|
|
35 if isinstance(value, Upload):
|
2
|
36 ret.write('\nContent-Disposition: form-data; name="%s"' % key)
|
0
|
37 filename = os.path.basename(value.filename)
|
2
|
38 ret.write('; filename="%s"\n\n' % filename)
|
0
|
39 value = open(os.path.join(value.filename), "rb").read()
|
|
40 else:
|
2
|
41 ret.write('\nContent-Disposition: form-data; name="%s"' % key)
|
0
|
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
|
2
|
50
|
|
51 class Progress(object):
|
0
|
52 def __init__(self, info, data):
|
|
53 self.info = info
|
|
54 self.tosend = len(data)
|
2
|
55 self.total = self.tosend / 1024
|
0
|
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
|
2
|
64 def __iter__(self):
|
|
65 return self
|
0
|
66
|
|
67 def next(self):
|
|
68 self.num += 1
|
|
69 if self.sent >= self.tosend:
|
2
|
70 print self.info, 'done', ' ' * (75 - len(self.info) - 6)
|
0
|
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
|
2
|
92 eta = steptime * ((self.total - self.num) / self.stepsize)
|
0
|
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:
|
2
|
101 s = '%s %2d%% (ETA %02d:%02d:%02d)' % (self.info,
|
0
|
102 self.num * 100. / self.total, H, M, S)
|
|
103 else:
|
2
|
104 s = '%s 0%% (ETA %02d:%02d:%02d)' % (self.info, H, M, S)
|
0
|
105 elif self.total:
|
2
|
106 s = '%s %2d%%' % (self.info, self.num * 100. / self.total)
|
0
|
107 else:
|
2
|
108 s = '%s %d done' % (self.info, self.num)
|
|
109 sys.stdout.write(s + ' ' * (75 - len(s)) + '\r')
|
0
|
110 sys.stdout.flush()
|
|
111
|
2
|
112
|
0
|
113 class 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
|
2
|
131
|
0
|
132 class progressHTTP(httplib.HTTP):
|
|
133 _connection_class = progressHTTPConnection
|
2
|
134
|
0
|
135 def _setup(self, conn):
|
|
136 httplib.HTTP._setup(self, conn)
|
|
137 self.progress_send = self._conn.progress_send
|
|
138
|
2
|
139
|
0
|
140 def http_request(data, server, port, url):
|
|
141 h = progressHTTP(server, port)
|
|
142
|
|
143 data = mimeEncode(data)
|
|
144 h.putrequest('POST', url)
|
2
|
145 h.putheader('Content-type', 'multipart/form-data; boundary=%s' % boundary)
|
0
|
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
|
2
|
158 print '%s %s' % (errcode, errmsg)
|
|
159 if response:
|
|
160 print response
|
|
161
|
0
|
162
|
|
163 def usage():
|
|
164 print '''This program is to be used to upload files to the PyWeek system.
|
|
165 You may use it to upload screenshots or code submissions.
|
|
166
|
|
167 REQUIRED ARGUMENTS:
|
|
168 -u username
|
|
169 -p password
|
|
170 -d description of file
|
|
171 -c file to upload
|
|
172 -e entry short name
|
|
173
|
|
174 OPTIONAL 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
|
|
180 In order to qualify for judging at the end of the challenge, you MUST
|
|
181 upload your source and check the "Final Submission" checkbox.
|
|
182 '''
|
|
183
|
|
184
|
|
185 if __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:
|
2
|
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)
|
0
|
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)
|