diff --git a/.gitignore b/.gitignore index 6323f10..5d999e7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ node_modules build app/js/templates.js app/data -*.py[cod] # C extensions *.so diff --git a/manage.py b/manage.py deleted file mode 100755 index 322ea59..0000000 --- a/manage.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env python -import os -import sys - -if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "stackviz.settings") - - from django.core.management import execute_from_command_line - - execute_from_command_line(sys.argv) diff --git a/requirements.txt b/requirements.txt index 4ec368e..f44e604 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,3 @@ -Django<1.8,>=1.4.2 -DjangoRestless>=0.0.10 python-subunit>=0.0.18 testtools>=0.9.30 testrepository>=0.0.18 diff --git a/stackviz/export.py b/stackviz/export.py index e176f56..ebffa6f 100644 --- a/stackviz/export.py +++ b/stackviz/export.py @@ -14,163 +14,171 @@ from __future__ import print_function -import django +import datetime import gzip +import json import os import shutil from argparse import ArgumentParser -from django.http import Http404 - -from django.core.urlresolvers import resolve -from django.test import RequestFactory +from functools import partial from stackviz.parser import tempest_subunit -from stackviz import settings - - -EXPORT_PATHS = [ - '/index.html', - '/tempest_aggregate.html' -] - _base = os.path.dirname(os.path.abspath(__file__)) +_tempest_count = 0 -def fake_render_view(path): - factory = RequestFactory() - request = factory.get(path) - - match = resolve(path) - response = match.func(request, *match.args, **match.kwargs) - - if hasattr(response, "render"): - response.render() - - return response - - -def export_single_page(path, dest_dir, use_gzip=False): - dest_file = path - if dest_file.startswith('/'): - dest_file = dest_file[1:] - - open_func = open - if use_gzip: +def open_compressed(output_dir, file_name, compress): + if compress: + file_name += ".gz" open_func = gzip.open - dest_file += ".gz" + else: + open_func = open - try: - content = fake_render_view(path).content - - with open_func(os.path.join(dest_dir, dest_file), 'wb') as f: - f.write(content) - except Http404 as ex: - print("Warning: skipping %s due to error: %s" % (path, ex.message)) + return open_func(os.path.join(output_dir, file_name), 'wb'), file_name -def init_django(args): - # remove leading / from static URL to give them correct filesystem paths - settings.STATIC_URL = settings.STATIC_URL[1:] - settings.USE_GZIP = args.gzip - settings.OFFLINE = True +def json_date_handler(object): + if isinstance(object, (datetime.datetime, datetime.date)): + return object.isoformat() - if args.repository or args.stream_file or args.stdin: - settings.TEST_REPOSITORIES = [] - settings.TEST_STREAMS = [] - settings.TEST_STREAM_STDIN = False + return None - if args.repository: - settings.TEST_REPOSITORIES = args.repository - if args.stream_file: - settings.TEST_STREAMS = args.stream_file +def export_tempest_tree(stream, output_stream): + converted = tempest_subunit.convert_stream(stream, strip_details=True) + tree = tempest_subunit.reorganize(converted) + json.dump(tree, output_stream, default=json_date_handler) + output_stream.close() - if args.stdin: - settings.TEST_STREAM_STDIN = True - if args.dstat: - settings.DSTAT_CSV = args.dstat +def export_tempest_raw(stream, output_stream): + converted = tempest_subunit.convert_stream(stream, strip_details=True) + json.dump(converted, output_stream, default=json_date_handler) + output_stream.close() - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "stackviz.settings") - django.setup() + +def export_tempest_details(stream, output_stream): + converted = tempest_subunit.convert_stream(stream, strip_details=True) + + output = {} + for entry in converted: + output[entry['name']] = entry['details'] + + json.dump(output, output_stream, default=json_date_handler) + output_stream.close() + + +def export_tempest(provider, output_dir, dstat, compress): + global _tempest_count + + ret = [] + + for i in range(provider.count): + path_base = 'tempest_%s_%d' % (provider.name, i) + if provider.count > 1: + name = '%s (%d)' % (provider.description, i) + else: + name = provider.description + + open_ = partial(open_compressed, + output_dir=output_dir, + compress=compress) + + stream_raw, path_raw = open_(file_name=path_base + '_raw.json') + export_tempest_raw(provider.get_stream(i), stream_raw) + + stream_tree, path_tree = open_(file_name=path_base + '_tree.json') + export_tempest_tree(provider.get_stream(i), stream_tree) + + stream_details, path_details = open_( + file_name=path_base + '_details.json') + export_tempest_details(provider.get_stream(i), stream_details) + + entry = { + 'id': _tempest_count, + 'name': name, + 'raw': path_raw, + 'tree': path_tree, + 'details': path_details + } + entry.update({'dstat': dstat} if dstat else {}) + + ret.append(entry) + _tempest_count += 1 + + return ret + + +def export_dstat(path, output_dir, compress): + f = open(path, 'rb') + out_stream, out_file = open_compressed( + output_dir, + 'dstat_log.csv', + compress) + + shutil.copyfileobj(f, out_stream) + + f.close() + out_stream.close() + + return out_file def main(): - parser = ArgumentParser(description="Generates a self-contained, static " - "StackViz site at the given path.") + parser = ArgumentParser(description="Generates JSON data files for a " + "StackViz site.") parser.add_argument("path", help="The output directory. Will be created if it " "doesn't already exist.") - parser.add_argument("--ignore-bower", - help="Ignore missing Bower components.", - action="store_true") parser.add_argument("-z", "--gzip", help="Enable gzip compression for data files.", action="store_true") parser.add_argument("-f", "--stream-file", action="append", - help="Include the given direct subunit stream.") + help="Include the given direct subunit stream; can be " + "used multiple times.") parser.add_argument("-r", "--repository", action="append", help="A directory containing a `.testrepository` to " - "include. If not provided, the `settings.py` " - "configured values will be used.") + "include; can be used multiple times.") parser.add_argument("-i", "--stdin", help="Read a direct subunit stream from standard " "input.", action="store_true") parser.add_argument("--dstat", help="The path to the DStat log file (CSV-formatted) " - "to include. If not provided, the `settings.py` " - "configured value will be used.") + "to include.") args = parser.parse_args() - if not args.ignore_bower: - if not os.listdir(os.path.join(_base, 'static', 'components')): - print("Bower components have not been installed, please run " - "`bower install`") - return 1 - - if os.path.exists(args.path): - if os.listdir(args.path): - print("Destination exists and is not empty, cannot continue") - return 1 - else: + if not os.path.exists(args.path): os.mkdir(args.path) - init_django(args) + dstat = None + if args.dstat: + print("Exporting DStat log") + dstat = export_dstat(args.dstat, args.path, args.gzip) - print("Copying static files ...") - shutil.copytree(os.path.join(_base, 'static'), - os.path.join(args.path, 'static')) + providers = tempest_subunit.get_providers( + args.repository, + args.stream_file, + args.stdin) - for path in EXPORT_PATHS: - print("Rendering:", path) - export_single_page(path, args.path) + tempest_config_entries = [] - for provider in tempest_subunit.get_providers().values(): - for i in range(provider.count): - param = (provider.name, i) + for provider in providers.values(): + print("Exporting Tempest provider: %s (%d)" % (provider.description, + provider.count)) + tempest_config_entries.extend( + export_tempest(provider, args.path, dstat, args.gzip) + ) - print("Rendering views for tempest run %s #%d" % param) - export_single_page('/tempest_timeline_%s_%d.html' % param, - args.path) - export_single_page('/tempest_results_%s_%d.html' % param, - args.path) - - print("Exporting data for tempest run %s #%d" % param) - export_single_page('/tempest_api_tree_%s_%d.json' % param, - args.path, args.gzip) - export_single_page('/tempest_api_raw_%s_%d.json' % param, - args.path, args.gzip) - export_single_page('/tempest_api_details_%s_%d.json' % param, - args.path, args.gzip) - - print("Exporting DStat log: dstat_log.csv") - export_single_page('/dstat_log.csv', args.path, args.gzip) + with open(os.path.join(args.path, 'config.json'), 'w') as f: + json.dump({ + 'tempest': tempest_config_entries + }, f) if __name__ == '__main__': diff --git a/stackviz/global_template_injector.py b/stackviz/global_template_injector.py deleted file mode 100644 index 44a46a6..0000000 --- a/stackviz/global_template_injector.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from stackviz.parser.tempest_subunit import get_providers -from stackviz.settings import OFFLINE -from stackviz.settings import USE_GZIP - - -def inject_extra_context(request): - ret = { - 'use_gzip': USE_GZIP, - 'offline': OFFLINE - } - - providers = get_providers() - if providers: - default = providers.values()[0] - - ret.update({ - 'tempest_providers': providers.values(), - 'tempest_default_provider': default, - }) - - return ret diff --git a/stackviz/parser/tempest_subunit.py b/stackviz/parser/tempest_subunit.py index a825d62..868c314 100644 --- a/stackviz/parser/tempest_subunit.py +++ b/stackviz/parser/tempest_subunit.py @@ -29,16 +29,11 @@ from testtools import StreamToDict from testrepository.repository.file import RepositoryFactory from testrepository.repository.file import RepositoryNotFound -from stackviz import settings - NAME_SCENARIO_PATTERN = re.compile(r'^(.+) \((.+)\)$') NAME_TAGS_PATTERN = re.compile(r'^(.+)\[(.+)\]$') -_provider_cache = None - - class InvalidSubunitProvider(Exception): pass @@ -168,39 +163,44 @@ class StandardInputProvider(SubunitProvider): return self.buffer -def get_providers(): +def get_providers(repository_paths=None, stream_paths=None, stdin=False): """Loads all test providers from locations configured in settings. + :param repository_paths: a list of directory paths containing + '.testrepository' folders to read + :param stream_paths: a list of paths to direct subunit streams + :param stdin: if true, read a subunit stream from standard input :return: a dict of loaded provider names and their associated :class:`SubunitProvider` instances :rtype: dict[str, SubunitProvider] """ - global _provider_cache + if repository_paths is None: + repository_paths = [] - if _provider_cache is not None: - return _provider_cache + if stream_paths is None: + stream_paths = [] - _provider_cache = {} + ret = {} - for path in settings.TEST_REPOSITORIES: + for path in repository_paths: try: p = RepositoryProvider(path) - _provider_cache[p.name] = p + ret[p.name] = p except (ValueError, RepositoryNotFound): continue - for path in settings.TEST_STREAMS: + for path in stream_paths: try: p = FileProvider(path) - _provider_cache[p.name] = p + ret[p.name] = p except InvalidSubunitProvider: continue - if settings.TEST_STREAM_STDIN: + if stdin: p = StandardInputProvider() - _provider_cache[p.name] = p + ret[p.name] = p - return _provider_cache + return ret def _clean_name(name): diff --git a/stackviz/static/css/sb-admin-2.css b/stackviz/static/css/sb-admin-2.css deleted file mode 100644 index e8be396..0000000 --- a/stackviz/static/css/sb-admin-2.css +++ /dev/null @@ -1,354 +0,0 @@ -/*! - * Start Bootstrap - SB Admin 2 Bootstrap Admin Theme (http://startbootstrap.com) - * Code licensed under the Apache License v2.0. - * For details, see http://www.apache.org/licenses/LICENSE-2.0. - */ - -body { - background-color: #f8f8f8; -} - -#wrapper { - width: 100%; -} - -#page-wrapper { - padding: 0 15px; - min-height: 568px; - background-color: #fff; -} - -@media(min-width:768px) { - #page-wrapper { - position: inherit; - margin: 0 0 0 250px; - padding: 0 30px; - border-left: 1px solid #e7e7e7; - } -} - -.navbar-top-links { - margin-right: 0; -} - -.navbar-top-links li { - display: inline-block; -} - -.navbar-top-links li:last-child { - margin-right: 15px; -} - -.navbar-top-links li a { - padding: 15px; - min-height: 50px; -} - -.navbar-top-links .dropdown-menu li { - display: block; -} - -.navbar-top-links .dropdown-menu li:last-child { - margin-right: 0; -} - -.navbar-top-links .dropdown-menu li a { - padding: 3px 20px; - min-height: 0; -} - -.navbar-top-links .dropdown-menu li a div { - white-space: normal; -} - -.navbar-top-links .dropdown-messages, -.navbar-top-links .dropdown-tasks, -.navbar-top-links .dropdown-alerts { - width: 310px; - min-width: 0; -} - -.navbar-top-links .dropdown-messages { - margin-left: 5px; -} - -.navbar-top-links .dropdown-tasks { - margin-left: -59px; -} - -.navbar-top-links .dropdown-alerts { - margin-left: -123px; -} - -.navbar-top-links .dropdown-user { - right: 0; - left: auto; -} - -.sidebar .sidebar-nav.navbar-collapse { - padding-right: 0; - padding-left: 0; -} - -.sidebar .sidebar-search { - padding: 15px; -} - -.sidebar ul li { - border-bottom: 1px solid #e7e7e7; -} - -.sidebar ul li a.active { - background-color: #eee; -} - -.sidebar .arrow { - float: right; -} - -.sidebar .fa.arrow:before { - content: "\f104"; -} - -.sidebar .active>a>.fa.arrow:before { - content: "\f107"; -} - -.sidebar .nav-second-level li, -.sidebar .nav-third-level li { - border-bottom: 0!important; -} - -.sidebar .nav-second-level li a { - padding-left: 37px; -} - -.sidebar .nav-third-level li a { - padding-left: 52px; -} - -@media(min-width:768px) { - .sidebar { - z-index: 1; - position: absolute; - width: 250px; - margin-top: 51px; - } - - .navbar-top-links .dropdown-messages, - .navbar-top-links .dropdown-tasks, - .navbar-top-links .dropdown-alerts { - margin-left: auto; - } -} - -.btn-outline { - color: inherit; - background-color: transparent; - transition: all .5s; -} - -.btn-primary.btn-outline { - color: #428bca; -} - -.btn-success.btn-outline { - color: #5cb85c; -} - -.btn-info.btn-outline { - color: #5bc0de; -} - -.btn-warning.btn-outline { - color: #f0ad4e; -} - -.btn-danger.btn-outline { - color: #d9534f; -} - -.btn-primary.btn-outline:hover, -.btn-success.btn-outline:hover, -.btn-info.btn-outline:hover, -.btn-warning.btn-outline:hover, -.btn-danger.btn-outline:hover { - color: #fff; -} - -.chat { - margin: 0; - padding: 0; - list-style: none; -} - -.chat li { - margin-bottom: 10px; - padding-bottom: 5px; - border-bottom: 1px dotted #999; -} - -.chat li.left .chat-body { - margin-left: 60px; -} - -.chat li.right .chat-body { - margin-right: 60px; -} - -.chat li .chat-body p { - margin: 0; -} - -.panel .slidedown .glyphicon, -.chat .glyphicon { - margin-right: 5px; -} - -.chat-panel .panel-body { - height: 350px; - overflow-y: scroll; -} - -.login-panel { - margin-top: 25%; -} - -.flot-chart { - display: block; - height: 400px; -} - -.flot-chart-content { - width: 100%; - height: 100%; -} - -.dataTables_wrapper { - position: relative; - clear: both; -} - -table.dataTable thead .sorting, -table.dataTable thead .sorting_asc, -table.dataTable thead .sorting_desc, -table.dataTable thead .sorting_asc_disabled, -table.dataTable thead .sorting_desc_disabled { - background: 0 0; -} - -table.dataTable thead .sorting_asc:after { - content: "\f0de"; - float: right; - font-family: fontawesome; -} - -table.dataTable thead .sorting_desc:after { - content: "\f0dd"; - float: right; - font-family: fontawesome; -} - -table.dataTable thead .sorting:after { - content: "\f0dc"; - float: right; - font-family: fontawesome; - color: rgba(50,50,50,.5); -} - -.btn-circle { - width: 30px; - height: 30px; - padding: 6px 0; - border-radius: 15px; - text-align: center; - font-size: 12px; - line-height: 1.428571429; -} - -.btn-circle.btn-lg { - width: 50px; - height: 50px; - padding: 10px 16px; - border-radius: 25px; - font-size: 18px; - line-height: 1.33; -} - -.btn-circle.btn-xl { - width: 70px; - height: 70px; - padding: 10px 16px; - border-radius: 35px; - font-size: 24px; - line-height: 1.33; -} - -.show-grid [class^=col-] { - padding-top: 10px; - padding-bottom: 10px; - border: 1px solid #ddd; - background-color: #eee!important; -} - -.show-grid { - margin: 15px 0; -} - -.huge { - font-size: 40px; -} - -.panel-green { - border-color: #5cb85c; -} - -.panel-green .panel-heading { - border-color: #5cb85c; - color: #fff; - background-color: #5cb85c; -} - -.panel-green a { - color: #5cb85c; -} - -.panel-green a:hover { - color: #3d8b3d; -} - -.panel-red { - border-color: #d9534f; -} - -.panel-red .panel-heading { - border-color: #d9534f; - color: #fff; - background-color: #d9534f; -} - -.panel-red a { - color: #d9534f; -} - -.panel-red a:hover { - color: #b52b27; -} - -.panel-yellow { - border-color: #f0ad4e; -} - -.panel-yellow .panel-heading { - border-color: #f0ad4e; - color: #fff; - background-color: #f0ad4e; -} - -.panel-yellow a { - color: #f0ad4e; -} - -.panel-yellow a:hover { - color: #df8a13; -} \ No newline at end of file diff --git a/stackviz/static/css/timeline.css b/stackviz/static/css/timeline.css deleted file mode 100644 index 92161eb..0000000 --- a/stackviz/static/css/timeline.css +++ /dev/null @@ -1,180 +0,0 @@ -.timeline { - position: relative; - padding: 20px 0 20px; - list-style: none; -} - -.timeline:before { - content: " "; - position: absolute; - top: 0; - bottom: 0; - left: 50%; - width: 3px; - margin-left: -1.5px; - background-color: #eeeeee; -} - -.timeline > li { - position: relative; - margin-bottom: 20px; -} - -.timeline > li:before, -.timeline > li:after { - content: " "; - display: table; -} - -.timeline > li:after { - clear: both; -} - -.timeline > li:before, -.timeline > li:after { - content: " "; - display: table; -} - -.timeline > li:after { - clear: both; -} - -.timeline > li > .timeline-panel { - float: left; - position: relative; - width: 46%; - padding: 20px; - border: 1px solid #d4d4d4; - border-radius: 2px; - -webkit-box-shadow: 0 1px 6px rgba(0,0,0,0.175); - box-shadow: 0 1px 6px rgba(0,0,0,0.175); -} - -.timeline > li > .timeline-panel:before { - content: " "; - display: inline-block; - position: absolute; - top: 26px; - right: -15px; - border-top: 15px solid transparent; - border-right: 0 solid #ccc; - border-bottom: 15px solid transparent; - border-left: 15px solid #ccc; -} - -.timeline > li > .timeline-panel:after { - content: " "; - display: inline-block; - position: absolute; - top: 27px; - right: -14px; - border-top: 14px solid transparent; - border-right: 0 solid #fff; - border-bottom: 14px solid transparent; - border-left: 14px solid #fff; -} - -.timeline > li > .timeline-badge { - z-index: 100; - position: absolute; - top: 16px; - left: 50%; - width: 50px; - height: 50px; - margin-left: -25px; - border-radius: 50% 50% 50% 50%; - text-align: center; - font-size: 1.4em; - line-height: 50px; - color: #fff; - background-color: #999999; -} - -.timeline > li.timeline-inverted > .timeline-panel { - float: right; -} - -.timeline > li.timeline-inverted > .timeline-panel:before { - right: auto; - left: -15px; - border-right-width: 15px; - border-left-width: 0; -} - -.timeline > li.timeline-inverted > .timeline-panel:after { - right: auto; - left: -14px; - border-right-width: 14px; - border-left-width: 0; -} - -.timeline-badge.primary { - background-color: #2e6da4 !important; -} - -.timeline-badge.success { - background-color: #3f903f !important; -} - -.timeline-badge.warning { - background-color: #f0ad4e !important; -} - -.timeline-badge.danger { - background-color: #d9534f !important; -} - -.timeline-badge.info { - background-color: #5bc0de !important; -} - -.timeline-title { - margin-top: 0; - color: inherit; -} - -.timeline-body > p, -.timeline-body > ul { - margin-bottom: 0; -} - -.timeline-body > p + p { - margin-top: 5px; -} - -@media(max-width:767px) { - ul.timeline:before { - left: 40px; - } - - ul.timeline > li > .timeline-panel { - width: calc(100% - 90px); - width: -moz-calc(100% - 90px); - width: -webkit-calc(100% - 90px); - } - - ul.timeline > li > .timeline-badge { - top: 16px; - left: 15px; - margin-left: 0; - } - - ul.timeline > li > .timeline-panel { - float: right; - } - - ul.timeline > li > .timeline-panel:before { - right: auto; - left: -15px; - border-right-width: 15px; - border-left-width: 0; - } - - ul.timeline > li > .timeline-panel:after { - right: auto; - left: -14px; - border-right-width: 14px; - border-left-width: 0; - } -} \ No newline at end of file diff --git a/stackviz/static/css/upstream_run.css b/stackviz/static/css/upstream_run.css deleted file mode 100644 index 019a3a3..0000000 --- a/stackviz/static/css/upstream_run.css +++ /dev/null @@ -1,3 +0,0 @@ -.highlight { - font-weight: bold; -} \ No newline at end of file diff --git a/stackviz/static/js/log-dialog.js b/stackviz/static/js/log-dialog.js deleted file mode 100644 index b815ee8..0000000 --- a/stackviz/static/js/log-dialog.js +++ /dev/null @@ -1,87 +0,0 @@ - -"use strict"; - -var originalDetailsContent = null; - -var detailsCache = null; -var detailsInProgress = false; -var detailsWaiting = []; -var runId = null; - -var loadDetails = function(callback) { - if (detailsCache === null) { - detailsWaiting.push(callback); - - if (!detailsInProgress) { - var url = "tempest_api_details_" + runId + ".json"; - if ("{{use_gzip}}" === "True") { - url += ".gz"; - } - - detailsInProgress = true; - - d3.json(url, function(error, data) { - if (error) { - throw error; - } - - detailsCache = data; - detailsWaiting.forEach(function(cb) { - cb(detailsCache); - }); - }); - } - } else { - callback(detailsCache); - } -}; - -var showDetails = function(item) { - var parent = $("#details-dialog"); - - loadDetails(function(details) { - if (!details.hasOwnProperty(item.name_full)) { - console.log("Details not found for item:", item.name_full); - return; - } - - if (originalDetailsContent === null) { - originalDetailsContent = parent.html(); - } - - parent.empty(); - for (var prop in details[item.name_full]) { - $("

").text(prop).appendTo(parent); - $("
").text(details[item.name_full][prop]).appendTo(parent);
-        }
-    });
-};
-
-function addDialogButton(parentID, run_id) {
-    //parentID: A string contiaining the parent div id to which the button will be appended
-    runId=run_id;
-    var button = $('
-            
-
-
-
- - - - - - - - - -{% endblock %} diff --git a/stackviz/templates/tempest/timeline.html b/stackviz/templates/tempest/timeline.html deleted file mode 100644 index 69858ee..0000000 --- a/stackviz/templates/tempest/timeline.html +++ /dev/null @@ -1,269 +0,0 @@ -{% extends 'template.html' %} -{% load staticfiles %} - -{% block title %}Tempest: Execution Timeline (run #{{run_id}}){% endblock %} - -{% block head-extra %} - -{% endblock %} - -{% block body %} -
-

Details not found for test.

-
-
-
-

Execution Timeline ({{provider_name}}, run #{{run_id}})

-
-
- -
-
-
-
- Timeline -
-
- - -
-
-
- -
- -
-
-
-
-
-
- Info -
- -
- Mouse over an item to view info. -
-
-
-
- - - -{% endblock %} diff --git a/stackviz/templates/template.html b/stackviz/templates/template.html deleted file mode 100644 index c794b9f..0000000 --- a/stackviz/templates/template.html +++ /dev/null @@ -1,114 +0,0 @@ -{% load staticfiles %} - - - - - - - - - - - - {% block title %}{% endblock %} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {% block head-extra %}{% endblock %} - - - - -
- - - - -
- {% block body %}{% endblock %} -
- - -
- - - - - - - - - diff --git a/stackviz/templates/upstream/run.html b/stackviz/templates/upstream/run.html deleted file mode 100644 index 0ad6394..0000000 --- a/stackviz/templates/upstream/run.html +++ /dev/null @@ -1,49 +0,0 @@ -{% extends 'template.html' %} -{% load staticfiles %} - -{% block title %}Upstream Run Metadata{% endblock %} - -{% block head-extra %} - - -{% endblock %} - -{% block body %} - - - -
-
-

Upstream Run Data

-
- -
- -
-
-
-
Analyze Run
-
-
- Enter a Gerrit Change ID (six-digit): - -
-
-
-
-
-
-
-
Run Metadata
-
-
-
-
-
-
-
-
- - - -{% endblock %} \ No newline at end of file diff --git a/stackviz/templates/upstream/test.html b/stackviz/templates/upstream/test.html deleted file mode 100644 index 89bc6c1..0000000 --- a/stackviz/templates/upstream/test.html +++ /dev/null @@ -1,30 +0,0 @@ -{% extends 'template.html' %} - -{% block title %}Upstream Test Stats{% endblock %} - -{% block body %} - -
-
-

Upstream Test Stats

-
- -
- -
-
-
-
Compare Tests
-
-
-
Test
-
-
-
Avg
-
-
-
-
-
- -{% endblock %} \ No newline at end of file diff --git a/stackviz/urls.py b/stackviz/urls.py deleted file mode 100644 index fd6af7f..0000000 --- a/stackviz/urls.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from django.conf.urls import include -from django.conf.urls import patterns -from django.conf.urls import url - -from stackviz.views.index import IndexView - -urlpatterns = patterns( - '', - url(r'^$', IndexView.as_view()), - url(r'^index.html$', IndexView.as_view(), name="index"), - url(r'^tempest_', include('stackviz.views.tempest.urls')), - url(r'^devstack_', include('stackviz.views.devstack.urls')), - url(r'^upstream_', include('stackviz.views.upstream.urls')), - url(r'^dstat_', include('stackviz.views.dstat.urls')) -) diff --git a/stackviz/views/__init__.py b/stackviz/views/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/stackviz/views/devstack/__init__.py b/stackviz/views/devstack/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/stackviz/views/devstack/results.py b/stackviz/views/devstack/results.py deleted file mode 100644 index ca9471e..0000000 --- a/stackviz/views/devstack/results.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from django.views.generic import TemplateView - - -class ResultsView(TemplateView): - template_name = 'devstack/results.html' diff --git a/stackviz/views/devstack/urls.py b/stackviz/views/devstack/urls.py deleted file mode 100644 index 059445f..0000000 --- a/stackviz/views/devstack/urls.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from django.conf.urls import patterns -from django.conf.urls import url - -from stackviz.views.devstack.results import ResultsView - - -urlpatterns = patterns('', - url(r'^results$', ResultsView.as_view()), - ) diff --git a/stackviz/views/dstat/__init__.py b/stackviz/views/dstat/__init__.py deleted file mode 100644 index f87606f..0000000 --- a/stackviz/views/dstat/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__author__ = 'tim' diff --git a/stackviz/views/dstat/api.py b/stackviz/views/dstat/api.py deleted file mode 100644 index ab6754f..0000000 --- a/stackviz/views/dstat/api.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from django.http import Http404 -from django.http import HttpResponse - -from django.views.generic import View - -from stackviz import settings - -_cached_csv = None - - -def _load_csv(): - global _cached_csv - - if _cached_csv: - return _cached_csv - - try: - with open(settings.DSTAT_CSV, 'r') as f: - _cached_csv = f.readlines() - return _cached_csv - except IOError: - return None - - -class DStatCSVEndpoint(View): - def get(self, request): - csv = _load_csv() - - if not csv: - raise Http404("DStat log could not be loaded at path %s" - % settings.DSTAT_CSV) - - return HttpResponse(csv, content_type="text/csv") diff --git a/stackviz/views/dstat/urls.py b/stackviz/views/dstat/urls.py deleted file mode 100644 index 3c83633..0000000 --- a/stackviz/views/dstat/urls.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from django.conf.urls import patterns -from django.conf.urls import url - -from api import DStatCSVEndpoint - - -urlpatterns = patterns('', url(r'^log.csv$', DStatCSVEndpoint.as_view())) diff --git a/stackviz/views/index.py b/stackviz/views/index.py deleted file mode 100644 index 316ebe5..0000000 --- a/stackviz/views/index.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from django.views.generic import TemplateView - - -class IndexView(TemplateView): - template_name = 'index.html' diff --git a/stackviz/views/tempest/__init__.py b/stackviz/views/tempest/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/stackviz/views/tempest/aggregate.py b/stackviz/views/tempest/aggregate.py deleted file mode 100644 index b7d1d59..0000000 --- a/stackviz/views/tempest/aggregate.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from django.views.generic import TemplateView - - -class AggregateResultsView(TemplateView): - template_name = 'tempest/aggregate.html' diff --git a/stackviz/views/tempest/api.py b/stackviz/views/tempest/api.py deleted file mode 100644 index 2dbd422..0000000 --- a/stackviz/views/tempest/api.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from django.http import Http404 -from restless.views import Endpoint - -from stackviz.parser.tempest_subunit import convert_stream -from stackviz.parser.tempest_subunit import get_providers -from stackviz.parser.tempest_subunit import reorganize - -#: Cached results from loaded subunit logs indexed by their run number -_cached_run = {} - -#: Cached results converted into tree form -_cached_tree = {} - -#: Cached results for loaded subunit logs without details stripped out. Indexed -#: initially by log number, but contains nested dicts indexed by the test name. -_cached_details = {} - - -class NoRunDataException(Http404): - pass - - -class ProviderNotFoundException(Http404): - pass - - -class RunNotFoundException(Http404): - pass - - -class TestNotFoundException(Http404): - pass - - -def _load_run(provider_name, run_id): - if (provider_name, run_id) in _cached_run: - return _cached_run[provider_name, run_id] - - providers = get_providers() - if not providers: - raise NoRunDataException("No test providers could be loaded") - - if provider_name not in providers: - raise ProviderNotFoundException("Requested subunit provider could not " - "be found") - - p = providers[provider_name] - - try: - # assume first repo for now - stream = p.get_stream(run_id) - - # strip details for now - # TODO(provide method for getting details on demand) - # (preferably for individual tests to avoid bloat) - converted_run = convert_stream(stream, strip_details=True) - _cached_run[provider_name, run_id] = converted_run - - return converted_run - except KeyError: - raise RunNotFoundException("Requested test run could not be found") - - -def _load_tree(provider, run_id): - if (provider, run_id) in _cached_tree: - return _cached_tree[provider, run_id] - - run = _load_run(provider, run_id) - tree = reorganize(run) - - _cached_tree[provider, run_id] = tree - return tree - - -def _load_details(provider_name, run_id, test_name): - if (provider_name, run_id) not in _cached_details: - providers = get_providers() - if not providers: - raise NoRunDataException("No test providers could be loaded") - - if provider_name not in providers: - raise ProviderNotFoundException("Requested subunit provider could " - "not be found: " + provider_name) - - provider = providers[provider_name] - try: - stream = provider.get_stream(run_id) - converted_run = convert_stream(stream, strip_details=False) - - # remap dict to allow direct access to details via test name - dest = {} - for entry in converted_run: - dest[entry['name']] = entry['details'] - - _cached_details[provider_name, run_id] = dest - except (KeyError, IndexError): - raise RunNotFoundException("Requested test run could not be found") - - details_map = _cached_details[provider_name, run_id] - if test_name is None: - return details_map - else: - if test_name in details_map: - return details_map[test_name] - else: - raise TestNotFoundException( - "Requested test could not be found in run") - - -class TempestRunRawEndpoint(Endpoint): - def get(self, request, provider_name, run_id): - return _load_run(provider_name, int(run_id)) - - -class TempestRunTreeEndpoint(Endpoint): - def get(self, request, provider_name, run_id): - return _load_tree(provider_name, int(run_id)) - - -class TempestRunDetailsEndpoint(Endpoint): - def get(self, request, run_id, provider_name, test_name=None): - return _load_details(provider_name, int(run_id), test_name) diff --git a/stackviz/views/tempest/results.py b/stackviz/views/tempest/results.py deleted file mode 100644 index b8bbc0f..0000000 --- a/stackviz/views/tempest/results.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from django.views.generic import TemplateView - - -class ResultsView(TemplateView): - template_name = 'tempest/results.html' - - def get_context_data(self, **kwargs): - context = super(ResultsView, self).get_context_data(**kwargs) - context['provider_name'] = self.kwargs['provider_name'] - context['run_id'] = self.kwargs['run_id'] - - return context diff --git a/stackviz/views/tempest/timeline.py b/stackviz/views/tempest/timeline.py deleted file mode 100644 index ac3535b..0000000 --- a/stackviz/views/tempest/timeline.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from django.views.generic import TemplateView - - -class TimelineView(TemplateView): - template_name = 'tempest/timeline.html' - - def get_context_data(self, **kwargs): - context = super(TimelineView, self).get_context_data(**kwargs) - context['provider_name'] = self.kwargs['provider_name'] - context['run_id'] = self.kwargs['run_id'] - - return context diff --git a/stackviz/views/tempest/urls.py b/stackviz/views/tempest/urls.py deleted file mode 100644 index b13d739..0000000 --- a/stackviz/views/tempest/urls.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -from django.conf.urls import patterns -from django.conf.urls import url - -from aggregate import AggregateResultsView -from results import ResultsView -from timeline import TimelineView - -from api import TempestRunDetailsEndpoint -from api import TempestRunRawEndpoint -from api import TempestRunTreeEndpoint - - -urlpatterns = patterns( - '', - url(r'^results_(?P[\w_\.]+)_(?P\d+).html$', - ResultsView.as_view(), - name='tempest_results'), - url(r'^timeline_(?P[\w_\.]+)_(?P\d+).html$', - TimelineView.as_view(), - name='tempest_timeline'), - - url(r'^api_tree_(?P[\w_\.]+)_(?P\d+).json$', - TempestRunTreeEndpoint.as_view(), - name='tempest_api_tree'), - url(r'^api_raw_(?P[\w_\.]+)_(?P\d+).json$', - TempestRunRawEndpoint.as_view(), - name='tempest_api_raw'), - url(r'^api_details_(?P[\w_\.]+)_(?P\d+).json$', - TempestRunDetailsEndpoint.as_view()), - url(r'^api_details_(?P[\w_\.]+)_(?P\d+)_' - r'(?P[^/]+).json$', - TempestRunDetailsEndpoint.as_view()), - - url(r'^aggregate.html$', - AggregateResultsView.as_view(), - name='tempest_aggregate_results'), -) diff --git a/stackviz/views/upstream/__init__.py b/stackviz/views/upstream/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/stackviz/views/upstream/api.py b/stackviz/views/upstream/api.py deleted file mode 100644 index 66e2ce7..0000000 --- a/stackviz/views/upstream/api.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from restless.views import Endpoint - -from subunit2sql.db import api - -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker - - -def _get_runs(change_id): - """Returns the dict of run objects associated with a changeID - - When given the change_id of a Gerrit change, a connection will be made to - the upstream subunit2sql db and query all run meta having that change_id - :param change_id: the Gerrit change_id to query - :return: a json dict of run_meta objects - """ - - engine = create_engine('mysql://query:query@logstash.openstack.org' + - ':3306/subunit2sql') - Session = sessionmaker(bind=engine) - session = Session() - - list_of_runs = api.get_runs_by_key_value(key="build_change", - value=change_id, - session=session) - ret_list = [] - - for run in list_of_runs: - ret_list.append(run.to_dict()) - - return ret_list - - -def _get_metadata(run_id): - """Returns a dict of run_metadata objects associated with a run_id - - :param run_id: - :return: - """ - - engine = create_engine('mysql://query:query@logstash.openstack.org' + - ':3306/subunit2sql') - Session = sessionmaker(bind=engine) - session = Session() - - metadata = api.get_run_metadata(run_id, session=session) - ret_list = [] - - for meta in metadata: - ret_list.append(meta.to_dict()) - - return ret_list - - -class GerritURLEndpoint(Endpoint): - - def get(self, request, change_id): - return _get_runs(change_id) - - -class RunMetadataEndpoint(Endpoint): - - def get(self, request, run_id): - return _get_metadata(run_id) diff --git a/stackviz/views/upstream/run.py b/stackviz/views/upstream/run.py deleted file mode 100644 index 75d36ef..0000000 --- a/stackviz/views/upstream/run.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from django.views.generic import TemplateView - - -class RunView(TemplateView): - template_name = 'upstream/run.html' diff --git a/stackviz/views/upstream/test.py b/stackviz/views/upstream/test.py deleted file mode 100644 index 133f93a..0000000 --- a/stackviz/views/upstream/test.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from django.views.generic import TemplateView - -# TODO(Planned functionality) -# Compare one specific test against its moving average -# - - -class TestView(TemplateView): - template_name = 'upstream/test.html' diff --git a/stackviz/views/upstream/urls.py b/stackviz/views/upstream/urls.py deleted file mode 100644 index 9804de3..0000000 --- a/stackviz/views/upstream/urls.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from django.conf.urls import patterns -from django.conf.urls import url - -from run import RunView -from test import TestView - -from api import GerritURLEndpoint -from api import RunMetadataEndpoint - -urlpatterns = patterns('', - url(r'^run.html$', - RunView.as_view(), - name='run_metadata'), - - url(r'^test.html$', - TestView.as_view(), - name='test_data'), - - url(r'^api_changeid_(?P\d+).json$', - GerritURLEndpoint.as_view(), - name='gerrit_url'), - - url(r'^api_run_id_(?P[a-zA-Z0-9!$* ' - '\t\r\n\-]+).json$', - RunMetadataEndpoint.as_view(), - name='run_metadata_url') - ) diff --git a/stackviz/wsgi.py b/stackviz/wsgi.py deleted file mode 100644 index 37ec47a..0000000 --- a/stackviz/wsgi.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -WSGI config for stackviz project. - -It exposes the WSGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/ -""" - -import os -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "stackviz.settings") - -from django.core.wsgi import get_wsgi_application -application = get_wsgi_application() diff --git a/tox.ini b/tox.ini index 73ef134..8c677f3 100644 --- a/tox.ini +++ b/tox.ini @@ -32,4 +32,4 @@ commands = oslo_debug_helper {posargs} show-source = True ignore = E123,E125 builtins = _ -exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build +exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,app,test,build,node_modules