X-Git-Url: https://git.ctpug.org.za/?a=blobdiff_plain;f=pyweek_upload.py;fp=pyweek_upload.py;h=f9201bede9f0f78e2691a6fd79c50a64cc6bbbc3;hb=39f49f9579bdee2ad449246d1370a0be27186ed9;hp=0000000000000000000000000000000000000000;hpb=2869b1c5d2babac462a5e2a1309c0370a987b121;p=naja.git diff --git a/pyweek_upload.py b/pyweek_upload.py new file mode 100644 index 0000000..f9201be --- /dev/null +++ b/pyweek_upload.py @@ -0,0 +1,223 @@ +''' +Upload script specifically engineered for the PyWeek challenge. + +Handles authentication and gives upload progress feedback. +''' +import sys +import os +import httplib +import cStringIO +import socket +import time +import getopt + + +class Upload(object): + def __init__(self, filename): + self.filename = filename + +boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' +sep_boundary = '\n--' + boundary +end_boundary = sep_boundary + '--' + + +def mimeEncode(data, sep_boundary=sep_boundary, end_boundary=end_boundary): + '''Take the mapping of data and construct the body of a + multipart/form-data message with it using the indicated boundaries. + ''' + ret = cStringIO.StringIO() + for key, value in data.items(): + # handle multiple entries for the same name + if not isinstance(value, list): + value = [value] + for value in value: + ret.write(sep_boundary) + if isinstance(value, Upload): + ret.write('\nContent-Disposition: form-data; name="%s"' % key) + filename = os.path.basename(value.filename) + ret.write('; filename="%s"\n\n' % filename) + value = open(os.path.join(value.filename), "rb").read() + else: + ret.write('\nContent-Disposition: form-data; name="%s"' % key) + ret.write("\n\n") + value = str(value) + ret.write(str(value)) + if value and value[-1] == '\r': + ret.write('\n') # write an extra newline + ret.write(end_boundary) + return ret.getvalue() + + +class Progress(object): + def __init__(self, info, data): + self.info = info + self.tosend = len(data) + self.total = self.tosend / 1024 + self.data = cStringIO.StringIO(data) + self.start = self.now = time.time() + self.sent = 0 + self.num = 0 + self.stepsize = self.total / 100 or 1 + self.steptimes = [] + self.display() + + def __iter__(self): + return self + + def next(self): + self.num += 1 + if self.sent >= self.tosend: + print self.info, 'done', ' ' * (75 - len(self.info) - 6) + sys.stdout.flush() + raise StopIteration + + chunk = self.data.read(1024) + self.sent += len(chunk) + #print (self.num, self.stepsize, self.total, self.sent, self.tosend) + + if self.num % self.stepsize: + return chunk + self.display() + return chunk + + def display(self): + # figure how long we've spent - guess how long to go + now = time.time() + steptime = now - self.now + self.steptimes.insert(0, steptime) + if len(self.steptimes) > 5: + self.steptimes.pop() + steptime = sum(self.steptimes) / len(self.steptimes) + self.now = now + eta = steptime * ((self.total - self.num) / self.stepsize) + + # tell it like it is (or might be) + if now - self.start > 3: + M = eta / 60 + H = M / 60 + M = M % 60 + S = eta % 60 + if self.total: + s = '%s %2d%% (ETA %02d:%02d:%02d)' % (self.info, + self.num * 100. / self.total, H, M, S) + else: + s = '%s 0%% (ETA %02d:%02d:%02d)' % (self.info, H, M, S) + elif self.total: + s = '%s %2d%%' % (self.info, self.num * 100. / self.total) + else: + s = '%s %d done' % (self.info, self.num) + sys.stdout.write(s + ' ' * (75 - len(s)) + '\r') + sys.stdout.flush() + + +class progressHTTPConnection(httplib.HTTPConnection): + def progress_send(self, str): + """Send `str' to the server.""" + if self.sock is None: + self.connect() + + p = Progress('Uploading', str) + for chunk in p: + sent = 0 + while sent != len(chunk): + try: + sent += self.sock.send(chunk) + except socket.error, v: + if v[0] == 32: # Broken pipe + self.close() + raise + p.display() + + +class progressHTTP(httplib.HTTP): + _connection_class = progressHTTPConnection + + def _setup(self, conn): + httplib.HTTP._setup(self, conn) + self.progress_send = self._conn.progress_send + + +def http_request(data, server, port, url): + h = progressHTTP(server, port) + + data = mimeEncode(data) + h.putrequest('POST', url) + h.putheader('Content-type', 'multipart/form-data; boundary=%s' % boundary) + h.putheader('Content-length', str(len(data))) + h.putheader('Host', server) + h.endheaders() + + h.progress_send(data) + + errcode, errmsg, headers = h.getreply() + + f = h.getfile() + response = f.read().strip() + f.close() + + print '%s %s' % (errcode, errmsg) + if response: + print response + + +def usage(): + print '''This program is to be used to upload files to the PyWeek system. +You may use it to upload screenshots or code submissions. + +REQUIRED ARGUMENTS: + -u username + -p password + -d description of file + -c file to upload + -e entry short name + +OPTIONAL ARGUMENTS: + -s file is a screenshot + -f file is FINAL submission + -h override default host name (www.pyweek.org) + -P override default host port (80) + +In order to qualify for judging at the end of the challenge, you MUST +upload your source and check the "Final Submission" checkbox. +''' + + +if __name__ == '__main__': + try: + optlist, args = getopt.getopt(sys.argv[1:], 'e:u:p:sfd:h:P:c:') + except getopt.GetoptError, message: + print message + usage() + sys.exit(1) + host = 'www.pyweek.org' + port = 80 + data = dict(version=2) + optional = {} + url = None + for opt, arg in optlist: + if opt == '-u': + data['user'] = arg + elif opt == '-p': + data['password'] = arg + elif opt == '-s': + optional['is_screenshot'] = 'yes' + elif opt == '-f': + optional['is_final'] = 'yes' + elif opt == '-d': + data['description'] = arg + elif opt == '-c': + data['content_file'] = Upload(arg) + elif opt == '-e': + url = '/e/%s/oup/' % arg + elif opt == '-h': + host = arg + elif opt == '-P': + port = int(arg) + + if len(data) < 4 or url is None: + print 'Required argument missing' + usage() + sys.exit(1) + + data.update(optional) + http_request(data, host, port, url)