comparison pyweek-upload.py @ 2:e057e9483488

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