source: pyweek-upload.py @ 2:e057e9483488

Last change on this file since 2:e057e9483488 was 2:e057e9483488, checked in by Jeremy Thurgood <firxen@…>, 11 years ago

Added pyweek skellington.

File size: 6.1 KB
Line 
1'''
2Upload script specifically engineered for the PyWeek challenge.
3
4Handles authentication and gives upload progress feedback.
5'''
6import sys, os, httplib, cStringIO, socket, time, getopt
7
8class Upload:
9    def __init__(self, filename):
10        self.filename = filename
11
12boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
13sep_boundary = '\n--' + boundary
14end_boundary = sep_boundary + '--'
15
16def 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
40class 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
100class 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
118class 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
124def 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
145def usage():
146    print '''This program is to be used to upload files to the PyWeek system.
147You may use it to upload screenshots or code submissions.
148
149REQUIRED ARGUMENTS:
150 -u   username
151 -p   password
152 -d   description of file
153 -c   file to upload
154 -e   entry short name
155
156OPTIONAL 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
162In order to qualify for judging at the end of the challenge, you MUST
163upload your source and check the "Final Submission" checkbox.
164'''
165
166
167if __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
Note: See TracBrowser for help on using the repository browser.