|
1 # Copyright (C) 2010 Google Inc. All rights reserved. |
|
2 # |
|
3 # Redistribution and use in source and binary forms, with or without |
|
4 # modification, are permitted provided that the following conditions are |
|
5 # met: |
|
6 # |
|
7 # * Redistributions of source code must retain the above copyright |
|
8 # notice, this list of conditions and the following disclaimer. |
|
9 # * Redistributions in binary form must reproduce the above |
|
10 # copyright notice, this list of conditions and the following disclaimer |
|
11 # in the documentation and/or other materials provided with the |
|
12 # distribution. |
|
13 # * Neither the name of Google Inc. nor the names of its |
|
14 # contributors may be used to endorse or promote products derived from |
|
15 # this software without specific prior written permission. |
|
16 # |
|
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
28 |
|
29 import logging |
|
30 import urllib |
|
31 |
|
32 from google.appengine.api import users |
|
33 from google.appengine.ext import blobstore |
|
34 from google.appengine.ext import webapp |
|
35 from google.appengine.ext.webapp import blobstore_handlers |
|
36 from google.appengine.ext.webapp import template |
|
37 |
|
38 from model.testfile import TestFile |
|
39 |
|
40 PARAM_BUILDER = "builder" |
|
41 PARAM_DIR = "dir" |
|
42 PARAM_FILE = "file" |
|
43 PARAM_NAME = "name" |
|
44 PARAM_KEY = "key" |
|
45 PARAM_TEST_TYPE = "testtype" |
|
46 |
|
47 |
|
48 class DeleteFile(webapp.RequestHandler): |
|
49 """Delete test file for a given builder and name from datastore (metadata) and blobstore (file data).""" |
|
50 |
|
51 def get(self): |
|
52 key = self.request.get(PARAM_KEY) |
|
53 builder = self.request.get(PARAM_BUILDER) |
|
54 test_type = self.request.get(PARAM_TEST_TYPE) |
|
55 name = self.request.get(PARAM_NAME) |
|
56 |
|
57 logging.debug( |
|
58 "Deleting File, builder: %s, test_type: %s, name: %s, blob key: %s.", |
|
59 builder, test_type, name, key) |
|
60 |
|
61 TestFile.delete_file(key, builder, test_type, name, 100) |
|
62 |
|
63 # Display file list after deleting the file. |
|
64 self.redirect("/testfile?builder=%s&testtype=%s&name=%s" |
|
65 % (builder, test_type, name)) |
|
66 |
|
67 |
|
68 class GetFile(blobstore_handlers.BlobstoreDownloadHandler): |
|
69 """Get file content or list of files for given builder and name.""" |
|
70 |
|
71 def _get_file_list(self, builder, test_type, name): |
|
72 """Get and display a list of files that matches builder and file name. |
|
73 |
|
74 Args: |
|
75 builder: builder name |
|
76 test_type: type of the test |
|
77 name: file name |
|
78 """ |
|
79 |
|
80 files = TestFile.get_files(builder, test_type, name, 100) |
|
81 if not files: |
|
82 logging.info("File not found, builder: %s, test_type: %s, name: %s.", |
|
83 builder, test_type, name) |
|
84 self.response.out.write("File not found") |
|
85 return |
|
86 |
|
87 template_values = { |
|
88 "admin": users.is_current_user_admin(), |
|
89 "builder": builder, |
|
90 "test_type": test_type, |
|
91 "name": name, |
|
92 "files": files, |
|
93 } |
|
94 self.response.out.write(template.render("templates/showfilelist.html", |
|
95 template_values)) |
|
96 |
|
97 def _get_file_content(self, builder, test_type, name): |
|
98 """Return content of the file that matches builder and file name. |
|
99 |
|
100 Args: |
|
101 builder: builder name |
|
102 test_type: type of the test |
|
103 name: file name |
|
104 """ |
|
105 |
|
106 files = TestFile.get_files(builder, test_type, name, 1) |
|
107 if not files: |
|
108 logging.info("File not found, builder: %s, test_type: %s, name: %s.", |
|
109 builder, test_type, name) |
|
110 return |
|
111 |
|
112 blob_key = files[0].blob_key |
|
113 blob_info = blobstore.get(blob_key) |
|
114 if blob_info: |
|
115 self.send_blob(blob_info, "text/plain") |
|
116 |
|
117 def get(self): |
|
118 builder = self.request.get(PARAM_BUILDER) |
|
119 test_type = self.request.get(PARAM_TEST_TYPE) |
|
120 name = self.request.get(PARAM_NAME) |
|
121 dir = self.request.get(PARAM_DIR) |
|
122 |
|
123 logging.debug( |
|
124 "Getting files, builder: %s, test_type: %s, name: %s.", |
|
125 builder, test_type, name) |
|
126 |
|
127 # If parameter "dir" is specified or there is no builder or filename |
|
128 # specified in the request, return list of files, otherwise, return |
|
129 # file content. |
|
130 if dir or not builder or not name: |
|
131 return self._get_file_list(builder, test_type, name) |
|
132 else: |
|
133 return self._get_file_content(builder, test_type, name) |
|
134 |
|
135 |
|
136 class GetUploadUrl(webapp.RequestHandler): |
|
137 """Get an url for uploading file to blobstore. A special url is required for each blobsotre upload.""" |
|
138 |
|
139 def get(self): |
|
140 upload_url = blobstore.create_upload_url("/testfile/upload") |
|
141 logging.info("Getting upload url: %s.", upload_url) |
|
142 self.response.out.write(upload_url) |
|
143 |
|
144 |
|
145 class Upload(blobstore_handlers.BlobstoreUploadHandler): |
|
146 """Upload file to blobstore.""" |
|
147 |
|
148 def post(self): |
|
149 uploaded_files = self.get_uploads("file") |
|
150 if not uploaded_files: |
|
151 return self._upload_done([("Missing upload file field.")]) |
|
152 |
|
153 builder = self.request.get(PARAM_BUILDER) |
|
154 if not builder: |
|
155 for blob_info in uploaded_files: |
|
156 blob_info.delete() |
|
157 |
|
158 return self._upload_done([("Missing builder parameter in upload request.")]) |
|
159 |
|
160 test_type = self.request.get(PARAM_TEST_TYPE) |
|
161 |
|
162 logging.debug( |
|
163 "Processing upload request, builder: %s, test_type: %s.", |
|
164 builder, test_type) |
|
165 |
|
166 errors = [] |
|
167 for blob_info in uploaded_files: |
|
168 tf = TestFile.update_file(builder, test_type, blob_info) |
|
169 if not tf: |
|
170 errors.append( |
|
171 "Upload failed, builder: %s, test_type: %s, name: %s." % |
|
172 (builder, test_type, blob_info.filename)) |
|
173 blob_info.delete() |
|
174 |
|
175 return self._upload_done(errors) |
|
176 |
|
177 def _upload_done(self, errors): |
|
178 logging.info("upload done.") |
|
179 |
|
180 error_messages = [] |
|
181 for error in errors: |
|
182 logging.info(error) |
|
183 error_messages.append("error=%s" % urllib.quote(error)) |
|
184 |
|
185 if error_messages: |
|
186 redirect_url = "/uploadfail?%s" % "&".join(error_messages) |
|
187 else: |
|
188 redirect_url = "/uploadsuccess" |
|
189 |
|
190 logging.info(redirect_url) |
|
191 # BlobstoreUploadHandler requires redirect at the end. |
|
192 self.redirect(redirect_url) |
|
193 |
|
194 |
|
195 class UploadForm(webapp.RequestHandler): |
|
196 """Show a form so user can submit a file to blobstore.""" |
|
197 |
|
198 def get(self): |
|
199 upload_url = blobstore.create_upload_url("/testfile/upload") |
|
200 template_values = { |
|
201 "upload_url": upload_url, |
|
202 } |
|
203 self.response.out.write(template.render("templates/uploadform.html", |
|
204 template_values)) |
|
205 |
|
206 class UploadStatus(webapp.RequestHandler): |
|
207 """Return status of file uploading""" |
|
208 |
|
209 def get(self): |
|
210 logging.debug("Update status") |
|
211 |
|
212 if self.request.path == "/uploadsuccess": |
|
213 self.response.set_status(200) |
|
214 self.response.out.write("OK") |
|
215 else: |
|
216 errors = self.request.params.getall("error") |
|
217 if errors: |
|
218 messages = "FAIL: " + "; ".join(errors) |
|
219 logging.warning(messages) |
|
220 self.response.set_status(500, messages) |
|
221 self.response.out.write("FAIL") |