Merge lp:~canonical-platform-qa/ubuntu-community-testing/initial-reporting into lp:ubuntu-community-testing
- initial-reporting
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 30 | ||||
Proposed branch: | lp:~canonical-platform-qa/ubuntu-community-testing/initial-reporting | ||||
Merge into: | lp:ubuntu-community-testing | ||||
Diff against target: |
546 lines (+427/-11) 13 files modified
ubuntu_pt_community/__init__.py (+5/-1) ubuntu_pt_community/api/v1.py (+4/-8) ubuntu_pt_community/db/__init__.py (+5/-2) ubuntu_pt_community/db/db.py (+6/-0) ubuntu_pt_community/pages/__init__.py (+27/-0) ubuntu_pt_community/pages/pages.py (+69/-0) ubuntu_pt_community/pages/reports.py (+167/-0) ubuntu_pt_community/templates/base.html (+30/-0) ubuntu_pt_community/templates/results/all_testsuites.html (+23/-0) ubuntu_pt_community/templates/results/index.html (+10/-0) ubuntu_pt_community/templates/results/latest_uploads.html (+27/-0) ubuntu_pt_community/tests/__init__.py (+17/-0) ubuntu_pt_community/tests/test_reports.py (+37/-0) |
||||
To merge this branch: | bzr merge lp:~canonical-platform-qa/ubuntu-community-testing/initial-reporting | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Brendan Donegan (community) | Approve | ||
Nicholas Skaggs (community) | Approve | ||
Review via email: mp+270480@code.launchpad.net |
Commit message
Initial lot of reporting.
Description of the change
Initial reporting incl.
- "Latest Uploads" - list of upload date with testsuites run and the users email
- "All testsuites" - simple stats for each testsuite that we have upload details for (runs, pass/fail numbers, success rates).
These are the first run and we can build on it from here (incl. making it look better :-P as well as navigation etc.).
Christopher Lee (veebers) wrote : | # |
Nicholas Skaggs (nskaggs) wrote : | # |
This looks good from my perspective. Chris, it looks like only the results from today will display in the 'latest results?'. I didn't actually build and deploy this, so I might be mistaken :-)
If so however, I might see this as a problem when days rollover. When the day switches to a new day, I might not see my results, even though I submitted them say within the last hour. Could we just simply display the last X number of results? Or if you wish limit it to a more reasonable number of days, perhaps 3 instead of just 1. If you do use days, it might still be wise to limit the number of results displayed so we don't have a long list on the page.
Christopher Lee (veebers) wrote : | # |
@balloons at the moment there is no limit to how many it displays and just shows all uploads with the latest at the top.
We can iterate on doing smarter things with this (i.e. pagination (using JIT requests), just show X days etc.)
Nicholas Skaggs (nskaggs) wrote : | # |
Ack, sounds fine to me.
Brendan Donegan (brendan-donegan) wrote : | # |
Mostly looks great - some questions about use of docstrings though
- 41. By Christopher Lee
-
Better and additional docstrings.
- 42. By Christopher Lee
-
Fix flake8 errors.
Christopher Lee (veebers) wrote : | # |
> Mostly looks great - some questions about use of docstrings though
Good catch with the docstrings. Have added more and improved the existing.
Regarding docstrings for 'private' methods; I see no issue with having them, They are a well known format which can streamline a developer reading them and contain details regarding the intention of the method itself.
Brendan Donegan (brendan-donegan) wrote : | # |
Ok looks better now. +1
Preview Diff
1 | === modified file 'ubuntu_pt_community/__init__.py' | |||
2 | --- ubuntu_pt_community/__init__.py 2015-08-18 00:55:26 +0000 | |||
3 | +++ ubuntu_pt_community/__init__.py 2015-09-17 05:33:50 +0000 | |||
4 | @@ -18,7 +18,10 @@ | |||
5 | 18 | import logging | 18 | import logging |
6 | 19 | import sys | 19 | import sys |
7 | 20 | 20 | ||
9 | 21 | from ubuntu_pt_community import api | 21 | from ubuntu_pt_community import ( |
10 | 22 | api, | ||
11 | 23 | pages, | ||
12 | 24 | ) | ||
13 | 22 | 25 | ||
14 | 23 | from flask import Flask | 26 | from flask import Flask |
15 | 24 | 27 | ||
16 | @@ -40,3 +43,4 @@ | |||
17 | 40 | app = Flask(__name__) | 43 | app = Flask(__name__) |
18 | 41 | 44 | ||
19 | 42 | api.define_api_routes(app) | 45 | api.define_api_routes(app) |
20 | 46 | pages.define_page_routes(app) | ||
21 | 43 | 47 | ||
22 | === modified file 'ubuntu_pt_community/api/v1.py' | |||
23 | --- ubuntu_pt_community/api/v1.py 2015-08-18 10:58:22 +0000 | |||
24 | +++ ubuntu_pt_community/api/v1.py 2015-09-17 05:33:50 +0000 | |||
25 | @@ -28,7 +28,9 @@ | |||
26 | 28 | from qakit.practitest.report_checkbox_results_to_practitest import ( | 28 | from qakit.practitest.report_checkbox_results_to_practitest import ( |
27 | 29 | upload_results | 29 | upload_results |
28 | 30 | ) | 30 | ) |
30 | 31 | from ubuntu_pt_community import auth, db | 31 | from ubuntu_pt_community import auth |
31 | 32 | from ubuntu_pt_community.db import get_results_collection | ||
32 | 33 | |||
33 | 32 | 34 | ||
34 | 33 | logger = logging.getLogger(__name__) | 35 | logger = logging.getLogger(__name__) |
35 | 34 | 36 | ||
36 | @@ -86,7 +88,7 @@ | |||
37 | 86 | ) | 88 | ) |
38 | 87 | 89 | ||
39 | 88 | try: | 90 | try: |
41 | 89 | collection = get_results_database() | 91 | collection = get_results_collection() |
42 | 90 | insert_id = collection.insert(details) | 92 | insert_id = collection.insert(details) |
43 | 91 | logger.info( | 93 | logger.info( |
44 | 92 | 'Inserted details for {} with id {}'.format( | 94 | 'Inserted details for {} with id {}'.format( |
45 | @@ -103,12 +105,6 @@ | |||
46 | 103 | logger.error('Failed to get database connection: ', e) | 105 | logger.error('Failed to get database connection: ', e) |
47 | 104 | 106 | ||
48 | 105 | 107 | ||
49 | 106 | def get_results_database(): | ||
50 | 107 | database_name = 'community_practitest' | ||
51 | 108 | collection_name = 'uploaded_results' | ||
52 | 109 | return db.get_collection(database_name, collection_name) | ||
53 | 110 | |||
54 | 111 | |||
55 | 112 | def get_user_email_address(request): | 108 | def get_user_email_address(request): |
56 | 113 | try: | 109 | try: |
57 | 114 | return request.form['uploader_email'] | 110 | return request.form['uploader_email'] |
58 | 115 | 111 | ||
59 | === modified file 'ubuntu_pt_community/db/__init__.py' | |||
60 | --- ubuntu_pt_community/db/__init__.py 2015-08-13 05:52:38 +0000 | |||
61 | +++ ubuntu_pt_community/db/__init__.py 2015-09-17 05:33:50 +0000 | |||
62 | @@ -16,6 +16,9 @@ | |||
63 | 16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | 16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
64 | 17 | # | 17 | # |
65 | 18 | 18 | ||
67 | 19 | from ubuntu_pt_community.db.db import get_collection | 19 | from ubuntu_pt_community.db.db import ( |
68 | 20 | get_collection, | ||
69 | 21 | get_results_collection, | ||
70 | 22 | ) | ||
71 | 20 | 23 | ||
73 | 21 | __all__ = ['get_collection'] | 24 | __all__ = ['get_collection', 'get_results_collection'] |
74 | 22 | 25 | ||
75 | === modified file 'ubuntu_pt_community/db/db.py' | |||
76 | --- ubuntu_pt_community/db/db.py 2015-08-17 06:19:29 +0000 | |||
77 | +++ ubuntu_pt_community/db/db.py 2015-09-17 05:33:50 +0000 | |||
78 | @@ -48,6 +48,12 @@ | |||
79 | 48 | return db[collection_name] | 48 | return db[collection_name] |
80 | 49 | 49 | ||
81 | 50 | 50 | ||
82 | 51 | def get_results_collection(): | ||
83 | 52 | database_name = 'community_practitest' | ||
84 | 53 | collection_name = 'uploaded_results' | ||
85 | 54 | return get_collection(database_name, collection_name) | ||
86 | 55 | |||
87 | 56 | |||
88 | 51 | def get_config_file_path(): | 57 | def get_config_file_path(): |
89 | 52 | try: | 58 | try: |
90 | 53 | return os.path.join( | 59 | return os.path.join( |
91 | 54 | 60 | ||
92 | === added directory 'ubuntu_pt_community/pages' | |||
93 | === added file 'ubuntu_pt_community/pages/__init__.py' | |||
94 | --- ubuntu_pt_community/pages/__init__.py 1970-01-01 00:00:00 +0000 | |||
95 | +++ ubuntu_pt_community/pages/__init__.py 2015-09-17 05:33:50 +0000 | |||
96 | @@ -0,0 +1,27 @@ | |||
97 | 1 | # | ||
98 | 2 | # Ubuntu PractiTest Community results processor | ||
99 | 3 | # Copyright (C) 2015 Canonical | ||
100 | 4 | # | ||
101 | 5 | # This program is free software: you can redistribute it and/or modify | ||
102 | 6 | # it under the terms of the GNU General Public License as published by | ||
103 | 7 | # the Free Software Foundation, either version 3 of the License, or | ||
104 | 8 | # (at your option) any later version. | ||
105 | 9 | # | ||
106 | 10 | # This program is distributed in the hope that it will be useful, | ||
107 | 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
108 | 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
109 | 13 | # GNU General Public License for more details. | ||
110 | 14 | # | ||
111 | 15 | # You should have received a copy of the GNU General Public License | ||
112 | 16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
113 | 17 | # | ||
114 | 18 | |||
115 | 19 | from ubuntu_pt_community.pages import pages | ||
116 | 20 | |||
117 | 21 | """Prepare the available api routes.""" | ||
118 | 22 | |||
119 | 23 | __all__ = ['define_page_routes'] | ||
120 | 24 | |||
121 | 25 | |||
122 | 26 | def define_page_routes(webapp): | ||
123 | 27 | pages.define_routes(webapp) | ||
124 | 0 | 28 | ||
125 | === added file 'ubuntu_pt_community/pages/pages.py' | |||
126 | --- ubuntu_pt_community/pages/pages.py 1970-01-01 00:00:00 +0000 | |||
127 | +++ ubuntu_pt_community/pages/pages.py 2015-09-17 05:33:50 +0000 | |||
128 | @@ -0,0 +1,69 @@ | |||
129 | 1 | # | ||
130 | 2 | # Ubuntu PractiTest Community results processor | ||
131 | 3 | # Copyright (C) 2015 Canonical | ||
132 | 4 | # | ||
133 | 5 | # This program is free software: you can redistribute it and/or modify | ||
134 | 6 | # it under the terms of the GNU General Public License as published by | ||
135 | 7 | # the Free Software Foundation, either version 3 of the License, or | ||
136 | 8 | # (at your option) any later version. | ||
137 | 9 | # | ||
138 | 10 | # This program is distributed in the hope that it will be useful, | ||
139 | 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
140 | 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
141 | 13 | # GNU General Public License for more details. | ||
142 | 14 | # | ||
143 | 15 | # You should have received a copy of the GNU General Public License | ||
144 | 16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
145 | 17 | # | ||
146 | 18 | |||
147 | 19 | import logging | ||
148 | 20 | from flask import render_template | ||
149 | 21 | |||
150 | 22 | from ubuntu_pt_community.pages import reports | ||
151 | 23 | |||
152 | 24 | |||
153 | 25 | logger = logging.getLogger(__name__) | ||
154 | 26 | |||
155 | 27 | |||
156 | 28 | class PageDefinition: | ||
157 | 29 | |||
158 | 30 | def __init__(self, name, route_name, route_func): | ||
159 | 31 | """Encapsulate the report view details.""" | ||
160 | 32 | self.name = name | ||
161 | 33 | self.route_name = route_name | ||
162 | 34 | self.route_func = route_func | ||
163 | 35 | |||
164 | 36 | @property | ||
165 | 37 | def route_function_name(self): | ||
166 | 38 | """Return the name of the route function for use_url.""" | ||
167 | 39 | return self.route_func.__name__ | ||
168 | 40 | |||
169 | 41 | |||
170 | 42 | # Keep a list of report pages so we don't have to duplicate things when listing | ||
171 | 43 | # them and adding routes. | ||
172 | 44 | Report_Pages = [ | ||
173 | 45 | PageDefinition( | ||
174 | 46 | 'All Results', | ||
175 | 47 | '/reports/all_results', | ||
176 | 48 | reports.view_all_results | ||
177 | 49 | ), | ||
178 | 50 | |||
179 | 51 | PageDefinition( | ||
180 | 52 | 'Latest Uploads', | ||
181 | 53 | '/reports/latest', | ||
182 | 54 | reports.view_latest_uploads | ||
183 | 55 | ), | ||
184 | 56 | ] | ||
185 | 57 | |||
186 | 58 | |||
187 | 59 | def define_routes(webapp): | ||
188 | 60 | """Setup all routes for available reports (incl. list of reports.""" | ||
189 | 61 | webapp.add_url_rule('/reports', view_func=view_reports) | ||
190 | 62 | |||
191 | 63 | for page in Report_Pages: | ||
192 | 64 | webapp.add_url_rule(page.route_name, view_func=page.route_func) | ||
193 | 65 | |||
194 | 66 | |||
195 | 67 | def view_reports(): | ||
196 | 68 | """Render a simple list of links to available reports.""" | ||
197 | 69 | return render_template('results/index.html', reports=Report_Pages) | ||
198 | 0 | 70 | ||
199 | === added file 'ubuntu_pt_community/pages/reports.py' | |||
200 | --- ubuntu_pt_community/pages/reports.py 1970-01-01 00:00:00 +0000 | |||
201 | +++ ubuntu_pt_community/pages/reports.py 2015-09-17 05:33:50 +0000 | |||
202 | @@ -0,0 +1,167 @@ | |||
203 | 1 | # | ||
204 | 2 | # Ubuntu PractiTest Community results processor | ||
205 | 3 | |||
206 | 4 | # Copyright (C) 2015 Canonical | ||
207 | 5 | # | ||
208 | 6 | # This program is free software: you can redistribute it and/or modify | ||
209 | 7 | # it under the terms of the GNU General Public License as published by | ||
210 | 8 | # the Free Software Foundation, either version 3 of the License, or | ||
211 | 9 | # (at your option) any later version. | ||
212 | 10 | # | ||
213 | 11 | # This program is distributed in the hope that it will be useful, | ||
214 | 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
215 | 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
216 | 14 | # GNU General Public License for more details. | ||
217 | 15 | # | ||
218 | 16 | # You should have received a copy of the GNU General Public License | ||
219 | 17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
220 | 18 | # | ||
221 | 19 | |||
222 | 20 | import json | ||
223 | 21 | import logging | ||
224 | 22 | from collections import ( | ||
225 | 23 | defaultdict, | ||
226 | 24 | namedtuple, | ||
227 | 25 | ) | ||
228 | 26 | from flask import render_template | ||
229 | 27 | |||
230 | 28 | from ubuntu_pt_community import db | ||
231 | 29 | |||
232 | 30 | logger = logging.getLogger(__name__) | ||
233 | 31 | |||
234 | 32 | |||
235 | 33 | def view_all_results(): | ||
236 | 34 | """Render report displaying all uploaded testsuites with stats. | ||
237 | 35 | |||
238 | 36 | Stats displayed are number of runs, number of passes and fails and the % of | ||
239 | 37 | successful runs. | ||
240 | 38 | """ | ||
241 | 39 | results_collection = db.get_results_collection() | ||
242 | 40 | all_uploads = results_collection.find() | ||
243 | 41 | simple_report_data = _get_simple_report_data(all_uploads) | ||
244 | 42 | return render_template( | ||
245 | 43 | 'results/all_testsuites.html', | ||
246 | 44 | results=simple_report_data | ||
247 | 45 | ) | ||
248 | 46 | |||
249 | 47 | |||
250 | 48 | def _get_simple_report_data(all_uploads): | ||
251 | 49 | """Produce a list of suite results. | ||
252 | 50 | |||
253 | 51 | :param all_uploads: list of dicts containing uploaded data details. | ||
254 | 52 | :returns: a list of TestsuiteResult objects. | ||
255 | 53 | """ | ||
256 | 54 | TestsuiteResult = namedtuple( | ||
257 | 55 | 'TestsuiteResult', | ||
258 | 56 | ['name', 'runs', 'passes', 'fails', 'success_rate'] | ||
259 | 57 | ) | ||
260 | 58 | |||
261 | 59 | # all_uploads will be a list of dicts | ||
262 | 60 | # dict will have keys: | ||
263 | 61 | # - results: a json string with the result details. | ||
264 | 62 | # - user_email: email of the user who uploaded the results | ||
265 | 63 | # - uploaded: a date of uploading | ||
266 | 64 | testsuites = defaultdict(list) | ||
267 | 65 | for upload in all_uploads: | ||
268 | 66 | upload_id = str(upload['_id']) | ||
269 | 67 | |||
270 | 68 | try: | ||
271 | 69 | results = _result_dict_from_document(upload) | ||
272 | 70 | except KeyError as e: | ||
273 | 71 | logger.warning(e) | ||
274 | 72 | continue | ||
275 | 73 | |||
276 | 74 | only_tests = _get_only_testcases(results) | ||
277 | 75 | |||
278 | 76 | for testname in only_tests: | ||
279 | 77 | # outcome will be 'pass' or 'fail' | ||
280 | 78 | try: | ||
281 | 79 | testsuites[testname].append(only_tests[testname]['outcome']) | ||
282 | 80 | except KeyError: | ||
283 | 81 | logger.error( | ||
284 | 82 | 'Testsuite has not outcome. Document ID: ', | ||
285 | 83 | upload_id | ||
286 | 84 | ) | ||
287 | 85 | |||
288 | 86 | results = [] | ||
289 | 87 | for suite in testsuites: | ||
290 | 88 | runs = len(testsuites[suite]) | ||
291 | 89 | passes = len([t for t in testsuites[suite] if t == 'pass']) | ||
292 | 90 | fails = runs - passes | ||
293 | 91 | success_rate = format(passes / runs * 100, '.2f') | ||
294 | 92 | |||
295 | 93 | results.append( | ||
296 | 94 | TestsuiteResult(suite, runs, passes, fails, success_rate) | ||
297 | 95 | ) | ||
298 | 96 | return results | ||
299 | 97 | |||
300 | 98 | |||
301 | 99 | def view_latest_uploads(): | ||
302 | 100 | """Render report displaying a list of uploaded results, latest first.""" | ||
303 | 101 | results_collection = db.get_results_collection() | ||
304 | 102 | # Sorted by latest first. . . | ||
305 | 103 | all_uploads = results_collection.find().sort('uploaded', -1) | ||
306 | 104 | |||
307 | 105 | # slight shim over the returned data to prepare it for presention. | ||
308 | 106 | sanitised_uploads = [] | ||
309 | 107 | for upload in all_uploads: | ||
310 | 108 | upload_date = upload['uploaded'].strftime('%Y-%b-%d %H:%M:%S') | ||
311 | 109 | uploader_email = upload.get('user_email') or 'Anonymous' | ||
312 | 110 | |||
313 | 111 | try: | ||
314 | 112 | results = _result_dict_from_document(upload) | ||
315 | 113 | except KeyError as e: | ||
316 | 114 | logger.warning(e) | ||
317 | 115 | continue | ||
318 | 116 | |||
319 | 117 | testsuite_details = _get_only_testcases(results) | ||
320 | 118 | uploaded_testsuite_names = testsuite_details.keys() | ||
321 | 119 | |||
322 | 120 | sanitised_uploads.append( | ||
323 | 121 | dict( | ||
324 | 122 | upload_date=upload_date, | ||
325 | 123 | testsuites=uploaded_testsuite_names, | ||
326 | 124 | uploader_email=uploader_email | ||
327 | 125 | ) | ||
328 | 126 | ) | ||
329 | 127 | |||
330 | 128 | return render_template( | ||
331 | 129 | 'results/latest_uploads.html', | ||
332 | 130 | all_uploads=sanitised_uploads | ||
333 | 131 | ) | ||
334 | 132 | |||
335 | 133 | |||
336 | 134 | def _result_dict_from_document(upload): | ||
337 | 135 | """Return a dict of result details. | ||
338 | 136 | |||
339 | 137 | Parses the json result string and constructs a dict containing the details. | ||
340 | 138 | |||
341 | 139 | :param upload: dict containing upload details. Must contain the key | ||
342 | 140 | 'results' to be successful. | ||
343 | 141 | :raises KeyError: if no 'results' are found in the upload dict. | ||
344 | 142 | """ | ||
345 | 143 | try: | ||
346 | 144 | results_json = upload['results'] | ||
347 | 145 | return json.loads(results_json.decode('utf-8')) | ||
348 | 146 | except KeyError: | ||
349 | 147 | upload_id = str(upload.get('_id'), 'No ID.') | ||
350 | 148 | logger.warning( | ||
351 | 149 | 'Skipping document. No results or result_map available', | ||
352 | 150 | upload_id, | ||
353 | 151 | ) | ||
354 | 152 | |||
355 | 153 | |||
356 | 154 | def _get_only_testcases(upload_details): | ||
357 | 155 | """Return details only for testsuites. | ||
358 | 156 | |||
359 | 157 | Uploaded results can contain detials that aren't testsuite related | ||
360 | 158 | (i.e. system details tests were run on.) Extract only the details that are | ||
361 | 159 | for testsuitse. | ||
362 | 160 | |||
363 | 161 | :param uploaded_details: dict containing keys 'resource_map' and | ||
364 | 162 | 'result_map'. | ||
365 | 163 | """ | ||
366 | 164 | removal_keys = upload_details['resource_map'].keys() | ||
367 | 165 | |||
368 | 166 | return {k: v for k, v in upload_details['result_map'].items() | ||
369 | 167 | if k not in removal_keys} | ||
370 | 0 | 168 | ||
371 | === added directory 'ubuntu_pt_community/templates' | |||
372 | === added file 'ubuntu_pt_community/templates/base.html' | |||
373 | --- ubuntu_pt_community/templates/base.html 1970-01-01 00:00:00 +0000 | |||
374 | +++ ubuntu_pt_community/templates/base.html 2015-09-17 05:33:50 +0000 | |||
375 | @@ -0,0 +1,30 @@ | |||
376 | 1 | <!doctype html> | ||
377 | 2 | <html lang="en"> | ||
378 | 3 | <head> | ||
379 | 4 | <meta charset="utf-8"> | ||
380 | 5 | <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||
381 | 6 | <meta name="viewport" content="width=device-width, initial-scale=1"> | ||
382 | 7 | |||
383 | 8 | <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"> | ||
384 | 9 | |||
385 | 10 | {% block head %} | ||
386 | 11 | <title>{% block title %}{% endblock %}</title> | ||
387 | 12 | {% endblock %} | ||
388 | 13 | </head> | ||
389 | 14 | <body> | ||
390 | 15 | |||
391 | 16 | <div id="content" class="container"> | ||
392 | 17 | {% block content %}{% endblock %} | ||
393 | 18 | </div> | ||
394 | 19 | |||
395 | 20 | <footer class="footer"> | ||
396 | 21 | <div class="container"> | ||
397 | 22 | <p class="text-muted"> | ||
398 | 23 | {% block footer %} | ||
399 | 24 | © Copyright 2015 <a href="https://canonical.com">Canonical Ltd.</a> | ||
400 | 25 | {% endblock %} | ||
401 | 26 | </p> | ||
402 | 27 | </div> | ||
403 | 28 | </footer> | ||
404 | 29 | </body> | ||
405 | 30 | </html> | ||
406 | 0 | 31 | ||
407 | === added directory 'ubuntu_pt_community/templates/results' | |||
408 | === added file 'ubuntu_pt_community/templates/results/all_testsuites.html' | |||
409 | --- ubuntu_pt_community/templates/results/all_testsuites.html 1970-01-01 00:00:00 +0000 | |||
410 | +++ ubuntu_pt_community/templates/results/all_testsuites.html 2015-09-17 05:33:50 +0000 | |||
411 | @@ -0,0 +1,23 @@ | |||
412 | 1 | {% extends "base.html" %} | ||
413 | 2 | {% block title %}All Result Details{% endblock %} | ||
414 | 3 | |||
415 | 4 | {% block content %} | ||
416 | 5 | <h1 class="page-header">All testsuite statistics</h1> | ||
417 | 6 | |||
418 | 7 | <!-- Could probably also add a list of users/email address that have done this report. --> | ||
419 | 8 | <table class="table table-striped table-bordered"> | ||
420 | 9 | <tr> | ||
421 | 10 | <th>Testsuite</th> | ||
422 | 11 | <th>Total Runs</th> | ||
423 | 12 | <th>Success Rate</th> | ||
424 | 13 | </tr> | ||
425 | 14 | {% for report in results %} | ||
426 | 15 | <tr> | ||
427 | 16 | <td>{{report.name}}</td> | ||
428 | 17 | <td>{{report.runs}}</td> | ||
429 | 18 | <td>{{report.success_rate}}%</td> | ||
430 | 19 | </tr> | ||
431 | 20 | {% endfor %} | ||
432 | 21 | </table> | ||
433 | 22 | |||
434 | 23 | {% endblock %} | ||
435 | 0 | 24 | ||
436 | === added file 'ubuntu_pt_community/templates/results/index.html' | |||
437 | --- ubuntu_pt_community/templates/results/index.html 1970-01-01 00:00:00 +0000 | |||
438 | +++ ubuntu_pt_community/templates/results/index.html 2015-09-17 05:33:50 +0000 | |||
439 | @@ -0,0 +1,10 @@ | |||
440 | 1 | {% extends "base.html" %} | ||
441 | 2 | {% block title %}Community Testing Reports{% endblock %} | ||
442 | 3 | {% block content %} | ||
443 | 4 | <h1 class="page-header">List of available reports</h1> | ||
444 | 5 | <ul> | ||
445 | 6 | {% for report in reports %} | ||
446 | 7 | <li><a href="{{ url_for(report.route_function_name) }}">{{ report.name }}</a></li> | ||
447 | 8 | {% endfor %} | ||
448 | 9 | </ul> | ||
449 | 10 | {% endblock %} | ||
450 | 0 | 11 | ||
451 | === added file 'ubuntu_pt_community/templates/results/latest_uploads.html' | |||
452 | --- ubuntu_pt_community/templates/results/latest_uploads.html 1970-01-01 00:00:00 +0000 | |||
453 | +++ ubuntu_pt_community/templates/results/latest_uploads.html 2015-09-17 05:33:50 +0000 | |||
454 | @@ -0,0 +1,27 @@ | |||
455 | 1 | {% extends "base.html" %} | ||
456 | 2 | {% block title %}Latest Uploads{% endblock %} | ||
457 | 3 | |||
458 | 4 | {% block content %} | ||
459 | 5 | <h1 class="page-header">Latest uploads</h1> | ||
460 | 6 | |||
461 | 7 | <table class="table table-striped table-bordered"> | ||
462 | 8 | <tr> | ||
463 | 9 | <th>Date Uploaded</th> | ||
464 | 10 | <th>Testsuites run</th> | ||
465 | 11 | <th>Uploader</th> | ||
466 | 12 | </tr> | ||
467 | 13 | {% for upload in all_uploads %} | ||
468 | 14 | <tr> | ||
469 | 15 | <td>{{ upload.upload_date }}</td> | ||
470 | 16 | <td> | ||
471 | 17 | <ul> | ||
472 | 18 | {% for testsuite in upload.testsuites %} | ||
473 | 19 | <li>{{ testsuite }}</li> | ||
474 | 20 | {% endfor %} | ||
475 | 21 | </ul> | ||
476 | 22 | </td> | ||
477 | 23 | <td>{{ upload.uploader_email }}</td> | ||
478 | 24 | </tr> | ||
479 | 25 | {% endfor%} | ||
480 | 26 | </table> | ||
481 | 27 | {% endblock %} | ||
482 | 0 | 28 | ||
483 | === added directory 'ubuntu_pt_community/tests' | |||
484 | === added file 'ubuntu_pt_community/tests/__init__.py' | |||
485 | --- ubuntu_pt_community/tests/__init__.py 1970-01-01 00:00:00 +0000 | |||
486 | +++ ubuntu_pt_community/tests/__init__.py 2015-09-17 05:33:50 +0000 | |||
487 | @@ -0,0 +1,17 @@ | |||
488 | 1 | # | ||
489 | 2 | # Ubuntu PractiTest Community results processor | ||
490 | 3 | # Copyright (C) 2015 Canonical | ||
491 | 4 | # | ||
492 | 5 | # This program is free software: you can redistribute it and/or modify | ||
493 | 6 | # it under the terms of the GNU General Public License as published by | ||
494 | 7 | # the Free Software Foundation, either version 3 of the License, or | ||
495 | 8 | # (at your option) any later version. | ||
496 | 9 | # | ||
497 | 10 | # This program is distributed in the hope that it will be useful, | ||
498 | 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
499 | 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
500 | 13 | # GNU General Public License for more details. | ||
501 | 14 | # | ||
502 | 15 | # You should have received a copy of the GNU General Public License | ||
503 | 16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
504 | 17 | # | ||
505 | 0 | 18 | ||
506 | === added file 'ubuntu_pt_community/tests/test_reports.py' | |||
507 | --- ubuntu_pt_community/tests/test_reports.py 1970-01-01 00:00:00 +0000 | |||
508 | +++ ubuntu_pt_community/tests/test_reports.py 2015-09-17 05:33:50 +0000 | |||
509 | @@ -0,0 +1,37 @@ | |||
510 | 1 | # | ||
511 | 2 | # Ubuntu PractiTest Community results processor | ||
512 | 3 | # Copyright (C) 2015 Canonical | ||
513 | 4 | # | ||
514 | 5 | # This program is free software: you can redistribute it and/or modify | ||
515 | 6 | # it under the terms of the GNU General Public License as published by | ||
516 | 7 | # the Free Software Foundation, either version 3 of the License, or | ||
517 | 8 | # (at your option) any later version. | ||
518 | 9 | # | ||
519 | 10 | # This program is distributed in the hope that it will be useful, | ||
520 | 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
521 | 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
522 | 13 | # GNU General Public License for more details. | ||
523 | 14 | # | ||
524 | 15 | # You should have received a copy of the GNU General Public License | ||
525 | 16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
526 | 17 | # | ||
527 | 18 | |||
528 | 19 | import testtools | ||
529 | 20 | |||
530 | 21 | from ubuntu_pt_community.pages import reports | ||
531 | 22 | |||
532 | 23 | |||
533 | 24 | class ReportHelpersTestCase(testtools.TestCase): | ||
534 | 25 | |||
535 | 26 | def test_get_only_testcases_returns_only_testcases(self): | ||
536 | 27 | testdict = dict( | ||
537 | 28 | result_map=dict( | ||
538 | 29 | testcase1=True, | ||
539 | 30 | testcase2=True, | ||
540 | 31 | resourcedetails1=False, | ||
541 | 32 | ), | ||
542 | 33 | resource_map=dict(resourcedetails1=False) | ||
543 | 34 | ) | ||
544 | 35 | |||
545 | 36 | results = reports._get_only_testcases(testdict) | ||
546 | 37 | self.assertDictEqual(results, dict(testcase1=True, testcase2=True)) |
Just marked as WIP as I notice that somethings are incorrect. Adding tests and sorting out these issues.