source: pyweek_upload.py

Last change on this file was 2:e30d192e9031, checked in by Simon Cross <hodgestar@…>, 7 years ago

Fix PEP8 errors.

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