comparison pyweek-upload.py @ 0:d0de8832774b

Import skellington-1.9 into the repo
author Neil Muller <drnlmuller+bitbucket@gmail.com>
date Sat, 05 May 2012 13:52:29 +0200
parents
children c90a6586cd66
comparison
equal deleted inserted replaced
-1:000000000000 0:d0de8832774b
1 '''
2 Upload script specifically engineered for the PyWeek challenge.
3
4 Handles authentication and gives upload progress feedback.
5 '''
6 import sys, os, httplib, cStringIO, socket, time, getopt
7
8 class Upload:
9 def __init__(self, filename):
10 self.filename = filename
11
12 boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
13 sep_boundary = '\n--' + boundary
14 end_boundary = sep_boundary + '--'
15
16 def 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
41 class 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
101 class 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
119 class 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
125 def 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
146 def usage():
147 print '''This program is to be used to upload files to the PyWeek system.
148 You may use it to upload screenshots or code submissions.
149
150 REQUIRED ARGUMENTS:
151 -u username
152 -p password
153 -d description of file
154 -c file to upload
155 -e entry short name
156
157 OPTIONAL 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
163 In order to qualify for judging at the end of the challenge, you MUST
164 upload your source and check the "Final Submission" checkbox.
165 '''
166
167
168 if __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