view pyweek-upload.py @ 267:a534629f490f default tip

Fix urls
author Neil Muller <drnlmuller@gmail.com>
date Tue, 17 Mar 2020 22:39:54 +0200
parents c90a6586cd66
children
line wrap: on
line source

'''
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:
    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 type(value) != type([]):
            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:
    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)